mirror of
https://github.com/github/awesome-copilot.git
synced 2026-10-13 07:14:24 +00:00
chore: publish from main
This commit is contained in:
@@ -24,8 +24,14 @@ Open it by asking Copilot to "open the plugin review board" (canvas id `external
|
||||
|
||||
## State
|
||||
|
||||
Board state lives in `state/board.json` next to this file and is gitignored. It stores the synced issue
|
||||
Board state lives in
|
||||
`<session.workspacePath>/extensions/external-plugin-review-board/state/board.json`.
|
||||
This is Copilot's session-state workspace, not the repository checkout. It stores the synced issue
|
||||
metadata, AI reviews, manual bucket overrides, and a history of decisions made from the board.
|
||||
State survives extension reloads and resuming the same session; a new session has its own board.
|
||||
Infinite sessions must be enabled to provide the workspace. Keeping writes outside the extension
|
||||
folder avoids invalidating its safety fingerprint. State previously stored in the extension folder is
|
||||
not imported; boards start fresh.
|
||||
|
||||
## Requirements
|
||||
|
||||
@@ -35,3 +41,8 @@ metadata, AI reviews, manual bucket overrides, and a history of decisions made f
|
||||
## Agent actions
|
||||
|
||||
`get_board`, `refresh`, `start_review`, `start_rereview`, `record_review`, `move_item`, `get_issue`.
|
||||
|
||||
## Regression tests
|
||||
|
||||
Run `node --experimental-vm-modules --test eng/board-state-storage.test.mjs` from the repository root
|
||||
to check both canvases' session-scoped persistence without accessing GitHub.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Extension: external-plugin-review-board
|
||||
// Kanban board for triaging external plugin submissions with AI review,
|
||||
// quick approve/reject, and an issue detail view. Board state is persisted to
|
||||
// ./state/board.json (gitignored) so it survives reloads and new sessions.
|
||||
// quick approve/reject, and an issue detail view. Board state is persisted under
|
||||
// session.workspacePath so writes do not change the extension fingerprint.
|
||||
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
@@ -18,7 +18,7 @@ const PUBLIC_DIR = path.join(EXT_DIR, "public");
|
||||
const GUIDANCE_PATH = path.join(EXT_DIR, "review-guidance.md");
|
||||
const DETAIL_TTL_MS = 2 * 60 * 1000;
|
||||
|
||||
const store = await new BoardStore(path.join(EXT_DIR, "state", "board.json"), REPO).load();
|
||||
let store;
|
||||
const servers = new Map();
|
||||
const detailCache = new Map();
|
||||
let refreshInFlight = null;
|
||||
@@ -213,6 +213,7 @@ function routesFor(instanceId) {
|
||||
function wrap(fn) {
|
||||
return async (ctx) => {
|
||||
try {
|
||||
if (!store) throw new CanvasError("not_ready", "The extension is still starting. Retry the action.");
|
||||
return await fn(ctx);
|
||||
} catch (error) {
|
||||
if (error instanceof CanvasError) throw error;
|
||||
@@ -316,6 +317,7 @@ session = await joinSession({
|
||||
},
|
||||
],
|
||||
open: async (ctx) => {
|
||||
if (!store) throw new CanvasError("not_ready", "The extension is still starting. Retry opening it.");
|
||||
let entry = servers.get(ctx.instanceId);
|
||||
if (!entry) {
|
||||
entry = await startBoardServer({
|
||||
@@ -330,6 +332,7 @@ session = await joinSession({
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
servers.set(ctx.instanceId, entry);
|
||||
}
|
||||
if (!store.state.lastRefreshedAt) {
|
||||
@@ -350,3 +353,11 @@ session = await joinSession({
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
if (!session.workspacePath) {
|
||||
throw new Error("External Plugin Review Board requires a session workspace for persistent state. Enable infinite sessions.");
|
||||
}
|
||||
store = await new BoardStore(
|
||||
path.join(session.workspacePath, "extensions", CANVAS_ID, "state", "board.json"),
|
||||
REPO,
|
||||
).load();
|
||||
|
||||
@@ -46,9 +46,15 @@ branch-protection rules still apply.
|
||||
|
||||
## State
|
||||
|
||||
Board state lives in `.github/extensions/open-pr-board/state/<owner>__<repo>.json`, one
|
||||
file per repository, and is git-ignored. Use **Reset board** (or the `reset_board`
|
||||
action) to clear reviews and history without touching GitHub.
|
||||
Board state lives in
|
||||
`<session.workspacePath>/extensions/open-pr-board/state/<owner>__<repo>.json`, one
|
||||
file per repository. This is Copilot's session-state workspace, not the repository
|
||||
checkout. State survives extension reloads and resuming the same session; a new
|
||||
session has its own board. Infinite sessions must be enabled to provide the workspace.
|
||||
Keeping writes outside the extension folder avoids invalidating its safety fingerprint.
|
||||
State previously stored in the extension folder is not imported; boards start fresh.
|
||||
Use **Reset board** (or the `reset_board` action) to clear reviews and history without
|
||||
touching GitHub.
|
||||
|
||||
## Agent actions
|
||||
|
||||
@@ -78,3 +84,5 @@ Results arriving after a manual move, reset, or replacement review are rejected.
|
||||
|
||||
Run `node --test eng/open-pr-board.test.mjs` from the repository root. GitHub writes
|
||||
are tested with injected in-process fakes, never the authenticated CLI.
|
||||
Run `node --experimental-vm-modules --test eng/board-state-storage.test.mjs` to check
|
||||
both canvases' session-scoped persistence without accessing GitHub.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
// Review board for open pull requests: AI review with persistent state tracking,
|
||||
// approve / request-changes / needs-insight / close recommendations, visible labels,
|
||||
// and GitHub actions. Decisions are applied through the authenticated gh CLI and
|
||||
// recorded on the board. Board state is persisted to
|
||||
// ./state/<owner>__<repo>.json (gitignored) so it survives reloads and new sessions.
|
||||
// recorded on the board. Board state is persisted under session.workspacePath
|
||||
// so writes do not change the extension fingerprint.
|
||||
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
@@ -41,7 +41,8 @@ async function getStore(repo) {
|
||||
let store = stores.get(repo);
|
||||
if (!store) {
|
||||
// The repo is validated against REPO_PATTERN above, so it cannot escape the state directory.
|
||||
const file = path.join(EXT_DIR, "state", `${repo.replace("/", "__")}.json`);
|
||||
const filename = `${repo.replace("/", "__")}.json`;
|
||||
const file = path.join(session.workspacePath, "extensions", CANVAS_ID, "state", filename);
|
||||
store = await new BoardStore({ file, repo }).load();
|
||||
stores.set(repo, store);
|
||||
}
|
||||
@@ -399,6 +400,7 @@ session = await joinSession({
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
entry = { repo, ...server };
|
||||
servers.set(ctx.instanceId, entry);
|
||||
}
|
||||
@@ -421,3 +423,7 @@ session = await joinSession({
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
if (!session.workspacePath) {
|
||||
throw new Error("Open PR Board requires a session workspace for persistent state. Enable infinite sessions.");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user