feat: add speckit-board canvas extension for Copilot CLI#3125
Closed
spboyer wants to merge 2 commits into
Closed
Conversation
Introduce a Copilot CLI canvas extension that turns Spec Kit projects into a live Spec-Driven Development dashboard in the side panel, plus a 'specify canvas' CLI subcommand for managing canvas installs. Two canvases registered by one extension: - speckit-board: portfolio view of every feature in specs/, each row showing pipeline state (Specify -> Clarify -> Plan -> Tasks -> Analyze -> Implement) with one-click slash-command actions and a collapsible constitution strip at the top. - speckit-feature: focused drill-in for a single feature with an artifact grid (spec, plan, tasks, checklists, research, data model, analyze report) and stage-specific buttons that send /speckit.* commands to the chat via session.send(). Both canvases are always openable and ship empty states that guide users through 'specify init' or kicking off their first spec. Native theming uses GitHub canvas tokens (--background-color-default, --text-color-*, --font-sans, --b-11-10, --g-11-10, etc.) so the UI follows the host theme — light, dark, and high-contrast. CLI: - 'specify canvas list' shows available canvases and install state. - 'specify canvas install [--dev] [--force] [<name>]' copies (or symlinks with --dev) into $COPILOT_HOME/extensions. - 'specify canvas uninstall <name>' removes the install. Implementation notes: - No package.json / node_modules — @github/copilot-sdk/extension is auto-resolved by the host. Native Node modules only. - One loopback HTTP server per instanceId; SESSION captured at module scope so request handlers can drive the session. - Logs go through session.log() — stdout is reserved for JSON-RPC. - Input validation: open_feature requires non-empty slug; run_command rejects anything that isn't a /speckit.* slash command. - Manual + on-focus refresh; no fs.watch in v1. All 4598 existing tests still pass. Assisted-by: GitHub Copilot (model: claude-opus-4.7, autonomous) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Contributor
There was a problem hiding this comment.
Pull request overview
Adds a first-party Copilot CLI canvas extension (extensions/speckit-board/) plus a new specify canvas CLI subcommand group to install/manage the extension, and documents the feature in the top-level README.
Changes:
- Introduces
specify canvas {list,install,uninstall}for managing Copilot CLI canvas extensions. - Adds the
speckit-boardextension implementation (session wiring, filesystem scanner, HTML renderer) registering bothspeckit-boardandspeckit-featurecanvases. - Updates repository documentation to describe installation and usage.
Show a summary per file
| File | Description |
|---|---|
| src/specify_cli/commands/canvas/init.py | New Typer command group implementing specify canvas list/install/uninstall. |
| src/specify_cli/init.py | Registers the new canvas command group in the main CLI. |
| README.md | Documents the new canvas extension and CLI commands. |
| extensions/speckit-board/README.md | Extension-specific documentation (usage, scope, actions). |
| extensions/speckit-board/extension.mjs | Copilot SDK joinSession + canvas registration + per-instance loopback servers and request handlers. |
| extensions/speckit-board/renderer.mjs | Generates themed HTML/CSS/JS UI for both canvases. |
| extensions/speckit-board/scanner.mjs | Scans a Spec Kit workspace filesystem into structured state for rendering. |
Copilot's findings
Tip
Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Files reviewed: 7/7 changed files
- Comments generated: 7
Comment on lines
+3
to
+5
| v1 supports a single first-party canvas (``speckit-board``) and is intentionally | ||
| lightweight: no registry, no manifest abstraction. Adding more canvases later | ||
| should extend ``_CANVASES`` and (if needed) split into a richer module. |
Comment on lines
+84
to
+88
| src = _source_dir(name) | ||
| if not src.is_dir(): | ||
| _fail( | ||
| f"Source not found at {src}. This command must be run from a checkout of the spec-kit repo." | ||
| ) |
Comment on lines
+105
to
+110
| if dev: | ||
| dest.symlink_to(src.resolve(), target_is_directory=True) | ||
| console.print(f"[green]✓[/green] Symlinked [cyan]{name}[/cyan] → {src}") | ||
| else: | ||
| shutil.copytree(src, dest) | ||
| console.print(f"[green]✓[/green] Installed [cyan]{name}[/cyan] → {dest}") |
Comment on lines
+63
to
+80
| @canvas_app.command("install") | ||
| def canvas_install( | ||
| name: str = typer.Argument( | ||
| "speckit-board", | ||
| help="Canvas to install (default: speckit-board).", | ||
| ), | ||
| dev: bool = typer.Option( | ||
| False, | ||
| "--dev", | ||
| help="Install via symlink from the repo source (for active development).", | ||
| ), | ||
| force: bool = typer.Option( | ||
| False, | ||
| "--force", | ||
| help="Overwrite an existing installation.", | ||
| ), | ||
| ) -> None: | ||
| """Install a canvas extension into ~/.copilot/extensions/.""" |
Comment on lines
+107
to
+110
| const slug = String(body.slug || "").trim(); | ||
| if (!slug) return jsonResponse(res, 400, { error: "slug required" }); | ||
| const prompt = `Open the "speckit-feature" canvas for the "${slug}" feature (use open_canvas with canvasId "speckit-feature" and input { "slug": "${slug}" }).`; | ||
| await SESSION.send({ prompt }); |
Comment on lines
+121
to
+124
| if (!file) return jsonResponse(res, 400, { error: "file required" }); | ||
| // Ask the agent to open the built-in editor canvas on the file. | ||
| const prompt = `Open the built-in "editor" canvas on the file at \`${file}\` (use open_canvas with canvasId "editor").`; | ||
| await SESSION.send({ prompt }); |
Comment on lines
+84
to
+87
| function hasClarifications(specText) { | ||
| if (!specText) return false; | ||
| return /##\s+Clarifications/i.test(specText) || /\[NEEDS CLARIFICATION/i.test(specText) === false && /Clarifications?:?\s*$/m.test(specText); | ||
| } |
Adds a Mock picker in both speckit-board and speckit-feature toolbars that swaps the live scanProject() output for synthetic state, letting reviewers preview every pipeline stage without seeding a real project. - mocks.mjs: 5 board scenarios (not-initialized → mature) and 8 feature scenarios (not-found, spec-only, with-clarify, planned, tasks-started, tasks-done, analyzed, implemented), all matching the real scanner shape so the renderer treats them identically to live state. - extension.mjs: handlers honor ?scenario=<name>; unknown names 404. - renderer.mjs: adds renderScenarioPicker(), a mock banner, picker styling using GitHub canvas theme tokens, and a change handler that updates the URL search param. - README: documents the available scenarios and how to use the picker. Smoke-tested every scenario via curl: board returns 0/0/1/5/7 feature cards as expected and feature scenarios escalate file-tile counts through the lifecycle. Mocks are read-only and never send prompts. Assisted-by: GitHub Copilot (model: claude-opus-4.7, autonomous) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds an opt-in Copilot CLI canvas extension that turns any Spec Kit project into a live Spec-Driven Development dashboard in the side panel, plus a
specify canvasCLI subcommand for managing canvas installs.What you get
Two canvases registered by one extension (
extensions/speckit-board/):speckit-board— portfolio view of every feature inspecs/, each row showing pipeline state (Specify → Clarify → Plan → Tasks → Analyze → Implement) with one-click slash-command actions and a collapsible constitution strip at the top.speckit-feature— focused drill-in for a single feature with an artifact grid (spec.md,plan.md,tasks.md, checklists, research, data model, analyze report) and stage-specific buttons that send/speckit.*commands to the chat viasession.send().Both canvases are always openable and ship empty states that guide users through
specify initor kicking off their first spec.Native theming
The iframes use the canvas-provided CSS custom properties (
--background-color-default,--text-color-default,--text-color-muted,--border-color-default,--color-focus-outline,--font-sans,--font-mono,--text-body-medium, plus the GitHub Primer ramps--b-11-10,--g-11-10,--n-1-10, etc.) — no hardcoded colors — so the UI follows the host theme (light, dark, high-contrast).CLI
--devsymlinks from the repo so iteration on the extension is instant.Implementation notes
package.json, nonode_modules—@github/copilot-sdk/extensionis auto-resolved by the Copilot CLI host. Native Node modules only.instanceId;SESSIONcaptured at module scope so request handlers can drive the session.session.log()only — stdout is reserved for JSON-RPC.open_featurerequires non-empty slug;run_commandrejects anything that isn't a/speckit.*slash command.fs.watchin v1.canvas.*) rejected by the host as expected.Validation
node --checkon all.mjsfilesspecify canvas list / install --dev --force / uninstalllist_canvas_capabilitiesfor both canvasesopen_canvas speckit-boardopen_canvas speckit-featurewith valid slugopen_canvas speckit-featurewith missing sluginvoke_canvas_action refresh / open_feature / run_commandopen_featurewith empty slugslug is required)run_commandwith non-/speckit.*commandpytest tests/Files
Scope (v1)
In: read-only dashboard, slash-command launching, "Open in editor" handoff to the built-in
editorcanvas, focus-based + manual refresh, inline text inputs for arg-taking commands (/speckit.specify,/speckit.plan).Out: editing markdown from the canvas, toggling task checkboxes, multi-workspace, filesystem watching.
Posted on behalf of @spboyer by GitHub Copilot (model: claude-opus-4.7).
Disclosure: every commit on this branch carries
Assisted-by: GitHub Copilot (model: claude-opus-4.7, autonomous)and aCo-authored-by: Copilottrailer perAGENTS.md.Mock UX scenarios (commit
7c3adb0)Both canvases now expose a Mock picker in the toolbar that swaps the live
scanProject()output for synthetic state, so reviewers can preview every pipeline stage without seeding a real project.speckit-boardscenarios:not-initialized,initialized-empty,early,mixed,mature(1→7 features).speckit-featurescenarios:not-found,spec-only,with-clarify,planned,tasks-started,tasks-done,analyzed,implemented.Selecting a scenario adds
?scenario=<name>to the iframe URL; picking Live (real project) returns to the actual scan. Mocks are read-only and never send prompts to the session.Files:
extensions/speckit-board/mocks.mjs(new),extension.mjsandrenderer.mjs(picker + banner + styling using GitHub canvas theme tokens),README.md(docs).