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
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ Contributions are welcome — see [CONTRIBUTING.md](CONTRIBUTING.md) to get star
| `spec-kit-copilot-assess` | 0.1.0 | Copilot App canvas | Optional visual dashboard for the Spec Kit `assess` extension |
| `spec-kit-copilot-bugfix` | 0.1.0 | Copilot App canvas | Optional visual dashboard for the Spec Kit `bug` extension |
| `spec-kit-copilot-sdd` | 0.1.0 | Copilot App canvas | Optional visual dashboard for the core spec-driven development workflow |
| `spec-kit-copilot-wizard` | 0.4.0 | Copilot App canvases | Guided wizard and an under-development Designer shell for future workflow-specific canvases from Wizard handoffs (not ready for use) |
| `spec-kit-copilot-wizard` | 0.4.0 | Copilot App canvases | Guided wizard and an under-development Designer shell (not ready for use) |

The plugins are independently installable and versioned. Install the core skills,
the assessment canvas, the bug fix canvas, the spec-driven development canvas, the
Expand Down Expand Up @@ -102,7 +102,7 @@ own README for full details.
| [`bugfix-canvas`](plugins/spec-kit-copilot-bugfix/extensions/bugfix-canvas/README.md) | `spec-kit-copilot-bugfix` | Dashboard for the optional `bug` extension — the assess → fix → test triage pipeline. |
| [`sdd-canvas`](plugins/spec-kit-copilot-sdd/extensions/sdd-canvas/README.md) | `spec-kit-copilot-sdd` | Dashboard for the core spec-driven workflow — constitution → specify → clarify → plan → tasks → analyze → checklist → implement. |
| [`speckit-wizard-canvas`](plugins/spec-kit-copilot-wizard/extensions/speckit-wizard-canvas/README.md) | `spec-kit-copilot-wizard` | Guided wizard for the full Spec Kit lifecycle — setup → constitution → specify → clarify → plan → tasks → analyze → checklist → implement, with preset / extension / composition inspectors. |
| [`speckit-canvas-designer`](plugins/spec-kit-copilot-wizard/extensions/speckit-canvas-designer/README.md) | `spec-kit-copilot-wizard` | Under development; not ready for use. Primarily launched from the Wizard with its configured pipeline and selected presets, extensions, and bundles as a handoff for future workflow-specific canvas generation. Direct opening shows only an empty shell. |
| [`speckit-canvas-designer`](plugins/spec-kit-copilot-wizard/extensions/speckit-canvas-designer/README.md) | `spec-kit-copilot-wizard` | Under development; not ready for use. Direct opening shows only an empty shell. |

### Previews

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,13 @@ This extension is **under development and not ready for use**. It ships inside
the `spec-kit-copilot-wizard` plugin. Installing that plugin registers both the
Wizard and Designer canvases.

The intended primary flow launches Designer **from the Wizard**. The Wizard
provides a handoff containing its configured pipeline and selected presets,
extensions, and bundles; these will guide the generation of a workflow-specific
canvas in a later update. For now, Designer displays only a shell (with a
handoff summary when provided): it does not generate a canvas or install
selected customizations.
Designer displays only a shell (with a handoff summary when provided).
Opening `speckit-canvas-designer` without input (or with `{}`) shows an empty
shell.

The Wizard launch flow supplies `{ "handoffId": "<id>" }` and asks the child
session to write the handoff JSON to
`speckit-canvas-designer/handoffs/<id>/handoff.json` **under that child's
`session.workspacePath`** before opening the canvas. The provider never writes
that file. Opening `speckit-canvas-designer` without input (or with `{}`)
shows an empty shell, not a generated canvas.

A supplied ID must match the bounded handoff ID pattern; the provider checks the
handoff structure, fingerprint, size, and session-artifact boundary. A supplied ID
with a missing or invalid file is an error, not an empty shell. The HTTP shell
A supplied `handoffId` must match the bounded handoff ID pattern; the provider
checks the handoff structure, fingerprint, size, and session-artifact boundary.
A missing or invalid handoff is an error, not an empty shell. The HTTP shell
binds to loopback and requires an unguessable URL token.

Run the provider tests with:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,52 +50,6 @@ artifacts, provide input, and run the matching `speckit-*` skill.

![Phases page](../../../../docs/images/wizard-phases.png)

### Canvas designer setup (first increment)

The **Generate canvas** button in the Phases pipeline header opens the
**Canvas designer setup** dialog. It offers separate Presets, Extensions,
and Bundles tabs with source badges and checkboxes for entries tagged
`canvas-design`. Presets and extensions come from the Copilot and Community
catalogs; bundles come from the available Default and Community catalogs.
No eligible items are expected in this first increment, so the tabs show
empty states until those catalogs publish design-time customizations.
Selections reset when the dialog closes and do not install anything in the
current project.
Community selections use the same prominent confirmation warning as
adding an item from the Catalogs page.
Selecting a bundle inspects its components without installing it and checks
same-source presets and extensions already listed in the standalone catalogs.
Those checkboxes remain editable and show which bundle includes them;
components absent from the standalone catalogs are not listed. Removing a
bundle releases its selections unless another selected bundle or a direct
selection still requires them; an inspection failure is shown in the dialog.

**Launch designer** sends a versioned handoff with the active phase IDs and
explicit preset, extension, and bundle selections (including empty lists)
to the Wizard's agent. Only on launch, the Wizard checks that the official
`plugin:spec-kit-copilot-wizard:speckit-canvas-designer` extension is running
in this session and its canvas is registered. If disabled, launching enables
it only for this session and verifies readiness before queueing. Missing or failed extensions
and activation errors leave the dialog and selections intact with an error.
No plugin is installed, global preference changed, or preview canvas substituted.
Environment checks and opening the dialog never perform this check.
The launch creates a nested, app-native **Canvas designer**
session in the same project, starting from the project's default branch.
The child stores the handoff in its own session artifacts and opens the
Designer canvas provider bundled with the installed Wizard plugin. No provider
files are copied into the child worktree. The
Wizard returns an accepted launch request; child setup and errors appear
in the child session (dispatch failures appear in the Wizard session log).
The child writes the handoff and opens the official plugin canvas directly;
opening fails visibly in the child if its own provider is unavailable. Enabling
the Wizard session's provider does not enable it in the child session.
The dialog closes once the launch request is accepted; if a check or request
fails, it stays open with selections intact and shows the error. Selected
design customizations are handed off, **not
installed yet**; Designer pages and installation support will follow in
later increments. The current Wizard project remains unchanged. The
Catalogs page continues to install items as before.

### Browser tests

From this extension directory, run `npm ci`, `npx playwright install chromium`,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,9 @@ export async function snapshot(inst) {
// /api/skills/reload) so the UI can gate setup completion on the
// live SDK result rather than a persisted flag or a folder probe.
snap.skillsReload = inst.skillsReload ?? null;
snap.featureFlags = {
generateCanvas: inst.generateCanvas === true,
};
inst.state = applyPatch(inst.state ?? {}, {
currentPhase: scan.currentPhase,
preset: scan.preset,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { createHandler } from "../server.mjs";
const repoPath = fileURLToPath(new URL("../../../../../", import.meta.url));
const snapshot = {
workspacePath: process.cwd(),
featureFlags: { generateCanvas: true },
currentPhase: "constitution",
setup: {
pluginInstalled: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ const instances = allInstances();
async function onOpen(ctx) {
const inst = getInstance(ctx.instanceId);
inst._session = getSession();
inst.generateCanvas = ctx.input?.generateCanvas === true;
// If the session repo path wasn't captured at startup (race), try once more.
if (!sessionState.repoPath && getSession()) {
sessionState.repoPath = await fetchSessionRepoPath(getSession());
Expand Down Expand Up @@ -359,6 +360,7 @@ setSession(await joinSession({
type: "object",
properties: {
cwd: { type: "string", description: "Workspace directory. Defaults to the session's cwd." },
generateCanvas: { type: "boolean", description: "Show the experimental Generate canvas button. Defaults to false." },
},
},
actions: ACTIONS,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ import {
import { handleNpmDiagnose, handleNpmRetry } from "./server/handlers-deps.mjs";
import { ensureEnvProbe } from "./env/probe-cache.mjs";
import { inspectBundleMembers } from "./catalog/bundles.mjs";
import { handleDesignerLaunch } from "./server/handlers-designer.mjs";

const __dirname = dirname(fileURLToPath(import.meta.url));
const DEFAULT_UI_DIR = join(__dirname, "ui");
Expand Down Expand Up @@ -346,10 +345,13 @@ export function createHandler(deps) {
"/api/env/probe": () => handleProbeEnv(res, { getState, broadcast, getInstance, ensureEnvProbe }),
"/api/deps/diagnose": () => handleNpmDiagnose(res, body, { broadcast, getInstance }),
"/api/deps/retry": () => handleNpmRetry(res, body, { broadcast, getInstance }),
"/api/designer/launch": () => handleDesignerLaunch(res, body, {
getState, getInstance, session, log,
enableProviderForSession: deps.enableDesignerProvider,
}),
"/api/designer/launch": async () => {
const { handleDesignerLaunch } = await import("./server/handlers-designer.mjs");
return handleDesignerLaunch(res, body, {
getState, getInstance, session, log,
enableProviderForSession: deps.enableDesignerProvider,
});
},
};
const route = postRoutes[url.pathname];
if (route) return route();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -642,7 +642,7 @@ test("Phases header exposes Generate canvas even without steps", () => {
globalThis.document = { getElementById: (id) => id === "pipeline-banner" ? banner : null };
state.activeTab = "phases";
try {
state.snapshot = { pipeline: [] };
state.snapshot = { pipeline: [], featureFlags: { generateCanvas: true } };
renderPipelineBanner();
assert.equal(banner.hidden, false);
assert.match(banner.innerHTML, /pipeline-generate" aria-label="Generate canvas"[^>]*>Generate canvas<\/button>/);
Comment thread
nicolehaugen marked this conversation as resolved.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,9 @@ export function renderPipelineBanner() {
<div class="header-actions pipeline-actions">
<button type="button" class="btn btn-ghost pipeline-clear" data-action="clear"${items.length ? "" : " disabled"}>Clear</button>
<button type="button" class="btn btn-ghost pipeline-reset" data-action="reset"${edited ? "" : " disabled"}>Reset to default</button>
<button type="button" class="btn btn-ghost pipeline-generate" aria-label="Generate canvas" title="Choose design customizations and launch a new Canvas designer session from this pipeline.">Generate canvas</button>
${state.snapshot?.featureFlags?.generateCanvas === true
? '<button type="button" class="btn btn-ghost pipeline-generate" aria-label="Generate canvas" title="Choose design customizations and launch a new Canvas designer session from this pipeline.">Generate canvas</button>'
: ""}
</div>
</header>
`;
Expand Down
Loading