Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 36 additions & 16 deletions extensions/accessibility-kanban/extension.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,26 @@ const VALID_COLUMNS = new Set(COLUMNS);

let repoInfoCache = null;
let githubTokenCache;
let sessionRef = null;
let workspaceCwd = null;

// The canvas request context reports the active session's *working directory*
// (i.e. the repo checkout). Note this is different from `session.workspacePath`,
// which points at the session-state folder (~/.copilot/session-state/<id>) and is
// NOT a git repo — using it for git resolution is what caused the board to fail
// with "Unable to detect the current repository from this workspace."
function setWorkspaceCwd(dir) {
if (typeof dir !== "string" || !dir.trim() || dir === workspaceCwd) return;
workspaceCwd = dir;
repoInfoCache = null;
githubTokenCache = undefined;
}

function captureCwd(ctx) {
setWorkspaceCwd(ctx?.session?.workingDirectory);
}

function getWorkspaceCwd() {
return sessionRef?.workspacePath || process.cwd();
return workspaceCwd || process.cwd();
}

// ─── Repo resolution ───
Expand Down Expand Up @@ -61,12 +77,12 @@ function parseRepoFromRemoteUrl(remoteUrl) {
function candidateCwds(preferredCwd) {
const candidates = [
preferredCwd,
sessionRef?.workspacePath,
workspaceCwd,
process.cwd(),
__dirname,
path.dirname(__dirname),
path.dirname(path.dirname(__dirname)),
path.dirname(path.dirname(path.dirname(__dirname))),
process.cwd(),
].filter(Boolean);
return [...new Set(candidates)];
}
Expand Down Expand Up @@ -564,7 +580,8 @@ const canvas = createCanvas({
name: "get_state",
description: "Get the current board state including open repository issues and selected label filters.",
inputSchema: { type: "object", properties: {}, additionalProperties: false },
async handler() {
async handler(ctx) {
captureCwd(ctx);
return refreshIssuesSafe();
},
},
Expand All @@ -580,8 +597,9 @@ const canvas = createCanvas({
required: ["issue_number", "column"],
additionalProperties: false,
},
handler({ input }) {
const { issue } = moveIssue(input.issue_number, input.column);
handler(ctx) {
captureCwd(ctx);
const { issue } = moveIssue(ctx.input.issue_number, ctx.input.column);
return { issue, state: currentState() };
},
},
Expand Down Expand Up @@ -620,8 +638,9 @@ const canvas = createCanvas({
required: ["issues"],
additionalProperties: false,
},
handler({ input }) {
return replaceIssues(input.issues);
handler(ctx) {
captureCwd(ctx);
return replaceIssues(ctx.input.issues);
},
},
{
Expand All @@ -635,21 +654,24 @@ const canvas = createCanvas({
required: ["labels"],
additionalProperties: false,
},
handler({ input }) {
return setSelectedLabels(input.labels);
handler(ctx) {
captureCwd(ctx);
return setSelectedLabels(ctx.input.labels);
},
},
{
name: "reset_state",
description: "Reset all cards to backlog and clear label filters, then refresh from live repo issues.",
inputSchema: { type: "object", properties: {}, additionalProperties: false },
async handler() {
async handler(ctx) {
captureCwd(ctx);
resetBoard();
return refreshIssuesSafe();
},
},
],
async open() {
async open(ctx) {
captureCwd(ctx);
const state = await refreshIssuesSafe();
broadcast("state", state);
return {
Expand All @@ -662,7 +684,7 @@ const canvas = createCanvas({

// ─── Join session (tools + canvas) ───

const session = await joinSession({
await joinSession({
canvases: [canvas],
tools: [
{
Expand Down Expand Up @@ -702,5 +724,3 @@ const session = await joinSession({
},
],
});

sessionRef = session;
Binary file modified extensions/arcade-canvas/assets/preview.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified extensions/backlog-swipe-triage/assets/preview.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified extensions/chromium-control-canvas/assets/preview.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
"description": "Compose and refine launch-ready release notes with contributor callouts and export-friendly output.",
"version": "1.0.0",
"author": {
"name": "James Montemagno",
"url": "https://github.com/jamesmontemagno"
"name": "Kayla Cinnamon",
"url": "https://github.com/cinnamon-msft"
},
"keywords": [
"changelog",
Expand Down
Binary file modified extensions/release-notes-showcase/assets/preview.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified extensions/site-studio/assets/preview.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified extensions/token-pacman/assets/preview.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
38 changes: 30 additions & 8 deletions extensions/where-was-i/extension.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,28 @@ const contextCache = new Map(); // instanceId → contextData

const isWindows = process.platform === "win32";

// Derive repo root from extension location (.github/extensions/where-was-i/)
// Fallback repo root derived from extension location. Only used when the
// session's real working directory is unavailable (see captureCwd below).
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const REPO_ROOT = join(__dirname, "..", "..", "..");

// The canvas request context reports the active session's working directory —
// the actual repo checkout or worktree the user opened the canvas in. This is
// what git commands must run against; REPO_ROOT (the extension's install dir)
// and session.workspacePath (the session-state folder) are NOT the repo, which
// is why the board previously showed an empty branch as "detached HEAD".
let workspaceCwd = null;

function captureCwd(ctx) {
const dir = ctx?.session?.workingDirectory;
if (typeof dir === "string" && dir.trim()) workspaceCwd = dir;
}

function repoCwd() {
return workspaceCwd || REPO_ROOT;
}

// --- Shell helpers ---

function run(cmd, cwd) {
Expand All @@ -34,7 +51,7 @@ function run(cmd, cwd) {
}

async function gatherContext(cwd) {
cwd = cwd || REPO_ROOT;
cwd = cwd || repoCwd();
const authorCmd = isWindows
? 'git log --oneline -5 --format="%h %s" --author="$(git config user.name)"'
: 'git log --oneline -5 --format="%h %s" --author="$(git config user.name)"';
Expand Down Expand Up @@ -605,7 +622,7 @@ async function startServer(instanceId, sessionRef, cwd, workspacePath) {
}

if (url.pathname === "/refresh" && req.method === "POST") {
const data = await gatherContext(cwd);
const data = await gatherContext(repoCwd());
contextCache.set(instanceId, data);
await saveContext(workspacePath, data);
broadcast(instanceId, data);
Expand Down Expand Up @@ -672,7 +689,8 @@ const session = await joinSession({
name: "refresh",
description: "Re-gather all git/project context and push updates to the canvas",
handler: async (ctx) => {
const data = await gatherContext(REPO_ROOT);
captureCwd(ctx);
const data = await gatherContext(repoCwd());
contextCache.set(ctx.instanceId, data);
if (sessionRef) await saveContext(sessionRef.workspacePath, data);
broadcast(ctx.instanceId, data);
Expand Down Expand Up @@ -713,16 +731,20 @@ const session = await joinSession({
},
],
open: async (ctx) => {
captureCwd(ctx);
let entry = servers.get(ctx.instanceId);
if (!entry) {
entry = await startServer(ctx.instanceId, sessionRef, REPO_ROOT, sessionRef?.workspacePath);
entry = await startServer(ctx.instanceId, sessionRef, repoCwd(), sessionRef?.workspacePath);
servers.set(ctx.instanceId, entry);
}

// Load persisted context or gather fresh
// Load persisted context or gather fresh. Re-gather when the
// saved context is missing or has no branch (e.g. it was saved
// before the working directory was known), so the board never
// opens stuck on a stale "detached HEAD".
let data = await loadContext(sessionRef?.workspacePath);
if (!data) {
data = await gatherContext(REPO_ROOT);
if (!data || !data.branch) {
data = await gatherContext(repoCwd());
await saveContext(sessionRef?.workspacePath, data);
}
contextCache.set(ctx.instanceId, data);
Expand Down
Loading