Fix installed canvas repo scope follow-up (#2230)

* Address canvas review feedback

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

* Optimize arcade sprite sheet

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

* Shrink arcade extension package files

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

* Address backlog canvas API review

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

* Compress arcade defender sounds

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
James Montemagno
2026-07-06 20:45:13 -07:00
committed by GitHub
parent 1f92908fa8
commit cb2a6022d7
37 changed files with 70 additions and 37 deletions
@@ -684,7 +684,7 @@ const canvas = createCanvas({
// ─── Join session (tools + canvas) ───
await joinSession({
const session = await joinSession({
canvases: [canvas],
tools: [
{
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 538 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 MiB

After

Width:  |  Height:  |  Size: 361 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 MiB

After

Width:  |  Height:  |  Size: 937 KiB

+4 -1
View File
@@ -929,6 +929,9 @@
<div id="game"></div>
<script src="./hud.js"></script>
<script src="./phaser.min.js"></script>
<script type="module" src="./game.js"></script>
<script type="module">
await window.__agentArcadePhaserReady;
await import("./game.js");
</script>
</body>
</html>
File diff suppressed because one or more lines are too long
Binary file not shown.
@@ -266,19 +266,19 @@ export class PlanetGuardianScene extends BaseScene {
this.load.image('def-swarmer', '../assets/defender/swarmer.png');
this.load.image('def-baiter', '../assets/defender/baiter.png');
// Sounds from OpenDefender
this.load.audio('snd_laser', '../assets/defender/sounds/sound_laser.wav');
this.load.audio('snd_enemydead', '../assets/defender/sounds/sound_enemydead.wav');
this.load.audio('snd_explode', '../assets/defender/sounds/sound_explode.wav');
this.load.audio('snd_playerdead', '../assets/defender/sounds/sound_playerdead.wav');
this.load.audio('snd_bonus', '../assets/defender/sounds/sound_bonus.wav');
this.load.audio('snd_humanoiddead', '../assets/defender/sounds/sound_humanoiddead.wav');
this.load.audio('snd_start', '../assets/defender/sounds/sound_start.wav');
this.load.audio('snd_thrust', '../assets/defender/sounds/sound_thurst.wav');
this.load.audio('snd_warning', '../assets/defender/sounds/sound_warning.wav');
this.load.audio('snd_baiterwarning', '../assets/defender/sounds/sound_baiterwarning.wav');
this.load.audio('snd_player1up', '../assets/defender/sounds/sound_player1up.wav');
this.load.audio('snd_enemyshoot', '../assets/defender/sounds/sound_enemyshoot.wav');
this.load.audio('snd_enemyshoot2', '../assets/defender/sounds/sound_enemyshoot2.wav');
this.load.audio('snd_laser', '../assets/defender/sounds/sound_laser.ogg');
this.load.audio('snd_enemydead', '../assets/defender/sounds/sound_enemydead.ogg');
this.load.audio('snd_explode', '../assets/defender/sounds/sound_explode.ogg');
this.load.audio('snd_playerdead', '../assets/defender/sounds/sound_playerdead.ogg');
this.load.audio('snd_bonus', '../assets/defender/sounds/sound_bonus.ogg');
this.load.audio('snd_humanoiddead', '../assets/defender/sounds/sound_humanoiddead.ogg');
this.load.audio('snd_start', '../assets/defender/sounds/sound_start.ogg');
this.load.audio('snd_thrust', '../assets/defender/sounds/sound_thurst.ogg');
this.load.audio('snd_warning', '../assets/defender/sounds/sound_warning.ogg');
this.load.audio('snd_baiterwarning', '../assets/defender/sounds/sound_baiterwarning.ogg');
this.load.audio('snd_player1up', '../assets/defender/sounds/sound_player1up.ogg');
this.load.audio('snd_enemyshoot', '../assets/defender/sounds/sound_enemyshoot.ogg');
this.load.audio('snd_enemyshoot2', '../assets/defender/sounds/sound_enemyshoot2.ogg');
}
create() {
this.initBase();
+34 -20
View File
@@ -72,6 +72,15 @@ function normalizeText(value, fallback = "") {
return typeof value === "string" ? value.trim() : fallback;
}
function captureCwd(ctx) {
const dir = ctx?.session?.workingDirectory;
return typeof dir === "string" && dir.trim() ? dir : null;
}
function cwdForContext(ctx) {
return captureCwd(ctx) || servers.get(ctx?.instanceId)?.cwd || null;
}
function escapeHtml(value) {
return normalizeText(value).replace(/[&<>"']/g, (char) => {
if (char === "&") return "&amp;";
@@ -385,7 +394,7 @@ async function runGh(args, cwd) {
return result.stdout;
}
async function closeGithubIssue(board, item, note) {
async function closeGithubIssue(board, item, note, cwd = process.cwd()) {
const issueNumber = normalizeText(item?.number);
const repo = normalizeText(board?.repo || item?.repo);
if (!issueNumber || !repo) {
@@ -397,7 +406,7 @@ async function closeGithubIssue(board, item, note) {
args.push("--comment", comment);
}
try {
await runGh(args, activeSession?.workspacePath || process.cwd());
await runGh(args, cwd);
} catch (error) {
const stderr = normalizeText(error?.stderr || "");
if (stderr.toLowerCase().includes("already closed")) {
@@ -407,7 +416,7 @@ async function closeGithubIssue(board, item, note) {
}
}
async function commentGithubIssue(board, item, note) {
async function commentGithubIssue(board, item, note, cwd = process.cwd()) {
const repo = normalizeText(board?.repo || item?.repo);
const issueNumber = extractIssueNumber(item);
const comment = normalizeText(note);
@@ -418,7 +427,7 @@ async function commentGithubIssue(board, item, note) {
return;
}
try {
await runGh(["issue", "comment", issueNumber, "--repo", repo, "--body", comment], activeSession?.workspacePath || process.cwd());
await runGh(["issue", "comment", issueNumber, "--repo", repo, "--body", comment], cwd);
} catch (error) {
const stderr = normalizeText(error?.stderr || "");
throw new Error(stderr || `Failed to comment on issue #${issueNumber} in ${repo}.`);
@@ -510,11 +519,11 @@ function pruneDecisionsForCurrentItems(board) {
}
}
async function syncBoardFromRepo(board, filtersInput) {
const workspacePath = activeSession?.workspacePath;
async function syncBoardFromRepo(board, filtersInput, cwd = null) {
const commandCwd = cwd || process.cwd();
let repo = normalizeText(board.repo);
if (!repo && workspacePath) {
const repoData = await runGhJson(["repo", "view", "--json", "nameWithOwner"], workspacePath);
if (!repo && cwd) {
const repoData = await runGhJson(["repo", "view", "--json", "nameWithOwner"], cwd);
repo = normalizeText(repoData?.nameWithOwner);
}
if (!repo) {
@@ -535,7 +544,7 @@ async function syncBoardFromRepo(board, filtersInput) {
"--json",
"number,title,url,labels,assignees,createdAt,updatedAt,author,body",
],
workspacePath || process.cwd(),
commandCwd,
);
const filteredIssues = Array.isArray(issues) ? sortIssues(issues.filter((issue) => issueMatchesFilters(issue, filters)), filters.sortBy) : [];
@@ -1843,6 +1852,7 @@ async function handleServerRequest(instanceId, req, res) {
await ensureStorageLoaded();
const board = getOrCreateBoard(entry.boardId);
board.title = entry.title;
const cwd = entry.cwd || null;
if (req.method === "GET" && req.url === "/") {
res.setHeader("Content-Type", "text/html; charset=utf-8");
@@ -1866,7 +1876,7 @@ async function handleServerRequest(instanceId, req, res) {
if (payload?.resetDecisions === true) {
resetBoardDecisions(board);
}
await syncBoardFromRepo(board, payload?.filters);
await syncBoardFromRepo(board, payload?.filters, cwd);
await persistStorage();
res.setHeader("Content-Type", "application/json; charset=utf-8");
res.end(JSON.stringify(buildBoardState(board)));
@@ -1905,10 +1915,10 @@ async function handleServerRequest(instanceId, req, res) {
return;
}
if (decision === "close") {
await closeGithubIssue(board, item, payload?.note);
await closeGithubIssue(board, item, payload?.note, cwd || process.cwd());
}
if (payload?.quickResponse === true && decision !== "close" && normalizeText(payload?.note)) {
await commentGithubIssue(board, item, payload?.note);
await commentGithubIssue(board, item, payload?.note, cwd || process.cwd());
}
if (decision === "assign_agent") {
const sessionStatus = await startImplementationSession(board, item, payload?.agent, payload?.note);
@@ -1932,7 +1942,7 @@ async function handleServerRequest(instanceId, req, res) {
res.end("Not found");
}
async function startServer(instanceId) {
async function startServer(instanceId, cwd) {
const server = createServer((req, res) => {
handleServerRequest(instanceId, req, res).catch((error) => {
if (res.headersSent) {
@@ -1946,7 +1956,7 @@ async function startServer(instanceId) {
await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve));
const address = server.address();
const port = typeof address === "object" && address ? address.port : 0;
return { server, url: `http://127.0.0.1:${port}/` };
return { server, url: `http://127.0.0.1:${port}/`, cwd };
}
const session = await joinSession({
@@ -1998,6 +2008,7 @@ const session = await joinSession({
additionalProperties: false,
},
handler: async (ctx) => {
const cwd = cwdForContext(ctx);
await ensureStorageLoaded();
const board = getOrCreateBoard(normalizeText(ctx.input?.boardId, "default"));
const title = normalizeText(ctx.input?.title);
@@ -2008,7 +2019,7 @@ const session = await joinSession({
if (repo) {
board.repo = repo;
}
await syncBoardFromRepo(board, ctx.input?.filters);
await syncBoardFromRepo(board, ctx.input?.filters, cwd);
await persistStorage();
return buildBoardState(board);
},
@@ -2072,6 +2083,7 @@ const session = await joinSession({
additionalProperties: false,
},
handler: async (ctx) => {
const cwd = cwdForContext(ctx);
await ensureStorageLoaded();
const board = getOrCreateBoard(normalizeText(ctx.input?.boardId, "default"));
const itemId = normalizeText(ctx.input?.itemId);
@@ -2081,10 +2093,10 @@ const session = await joinSession({
throw new Error(`Item "${itemId}" not found`);
}
if (decision === "close") {
await closeGithubIssue(board, item, ctx.input?.note);
await closeGithubIssue(board, item, ctx.input?.note, cwd || process.cwd());
}
if (ctx.input?.commentOnIssue === true && decision !== "close" && normalizeText(ctx.input?.note)) {
await commentGithubIssue(board, item, ctx.input?.note);
await commentGithubIssue(board, item, ctx.input?.note, cwd || process.cwd());
}
if (decision === "assign_agent") {
const sessionStatus = await startImplementationSession(board, item, ctx.input?.agent, ctx.input?.note);
@@ -2120,6 +2132,8 @@ const session = await joinSession({
},
],
open: async (ctx) => {
let entry = servers.get(ctx.instanceId);
const cwd = cwdForContext(ctx);
await ensureStorageLoaded();
const boardId = normalizeText(ctx.input?.boardId, "default");
const board = getOrCreateBoard(boardId);
@@ -2139,17 +2153,17 @@ const session = await joinSession({
setBoardItems(board, ctx.input.items, true);
await persistStorage();
} else if (syncFromRepo) {
await syncBoardFromRepo(board, board.filters);
await syncBoardFromRepo(board, board.filters, cwd);
await persistStorage();
}
let entry = servers.get(ctx.instanceId);
if (!entry) {
entry = await startServer(ctx.instanceId);
entry = await startServer(ctx.instanceId, cwd);
servers.set(ctx.instanceId, entry);
}
entry.boardId = boardId;
entry.title = title;
entry.cwd = cwd;
return {
title,
status: "Swipe to triage backlog",
+1 -1
View File
@@ -622,7 +622,7 @@ async function startServer(instanceId, sessionRef, cwd, workspacePath) {
}
if (url.pathname === "/refresh" && req.method === "POST") {
const data = await gatherContext(repoCwd());
const data = await gatherContext(cwd);
contextCache.set(instanceId, data);
await saveContext(workspacePath, data);
broadcast(instanceId, data);