Skip to content

feat: add speckit-board canvas extension for Copilot CLI#3125

Closed
spboyer wants to merge 2 commits into
github:mainfrom
spboyer:spboyer-symmetrical-guide
Closed

feat: add speckit-board canvas extension for Copilot CLI#3125
spboyer wants to merge 2 commits into
github:mainfrom
spboyer:spboyer-symmetrical-guide

Conversation

@spboyer

@spboyer spboyer commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

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 canvas CLI subcommand for managing canvas installs.

What you get

Two canvases registered by one extension (extensions/speckit-board/):

  • 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.md, plan.md, tasks.md, 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

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

specify canvas list                                  # show available + install state
specify canvas install [--dev] [--force] [<name>]    # copy or symlink (--dev) into $COPILOT_HOME/extensions
specify canvas uninstall <name>                      # remove

--dev symlinks from the repo so iteration on the extension is instant.

Implementation notes

  • No package.json, no node_modules@github/copilot-sdk/extension is auto-resolved by the Copilot CLI 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() only — 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.
  • Reserved action names (canvas.*) rejected by the host as expected.

Validation

Check Result
node --check on all .mjs files
specify canvas list / install --dev --force / uninstall
list_canvas_capabilities for both canvases ✅ schema matches
open_canvas speckit-board ✅ ready, HTML renders, constitution parsed
open_canvas speckit-feature with valid slug ✅ pipeline + 50% progress bar render
open_canvas speckit-feature with missing slug ✅ empty state shown
invoke_canvas_action refresh / open_feature / run_command
open_feature with empty slug ✅ rejected (slug is required)
run_command with non-/speckit.* command ✅ rejected
pytest tests/ ✅ 4598 passed, 4 skipped

Files

extensions/speckit-board/
├── README.md            # extension docs
├── extension.mjs        # joinSession + per-canvas wiring + loopback servers
├── renderer.mjs         # HTML/CSS for both canvases (native theming)
└── scanner.mjs          # pure filesystem scan → state JSON

src/specify_cli/commands/canvas/__init__.py   # `specify canvas` subcommand
src/specify_cli/__init__.py                   # +1 register call
README.md                                     # new "Canvas: Spec Kit Board" section

Scope (v1)

In: read-only dashboard, slash-command launching, "Open in editor" handoff to the built-in editor canvas, 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 a Co-authored-by: Copilot trailer per AGENTS.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-board scenarios: not-initialized, initialized-empty, early, mixed, mature (1→7 features).
  • speckit-feature scenarios: 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.mjs and renderer.mjs (picker + banner + styling using GitHub canvas theme tokens), README.md (docs).

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>
Copilot AI review requested due to automatic review settings June 23, 2026 16:39

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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-board extension implementation (session wiring, filesystem scanner, HTML renderer) registering both speckit-board and speckit-feature canvases.
  • 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>
@spboyer spboyer closed this Jun 23, 2026
@spboyer spboyer deleted the spboyer-symmetrical-guide branch June 23, 2026 17:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants