diff --git a/README.md b/README.md index d9cf2bd..887de90 100644 --- a/README.md +++ b/README.md @@ -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 @@ -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 diff --git a/plugins/spec-kit-copilot-wizard/extensions/speckit-canvas-designer/README.md b/plugins/spec-kit-copilot-wizard/extensions/speckit-canvas-designer/README.md index 176cbbd..0c7647e 100644 --- a/plugins/spec-kit-copilot-wizard/extensions/speckit-canvas-designer/README.md +++ b/plugins/spec-kit-copilot-wizard/extensions/speckit-canvas-designer/README.md @@ -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": "" }` and asks the child -session to write the handoff JSON to -`speckit-canvas-designer/handoffs//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: diff --git a/plugins/spec-kit-copilot-wizard/extensions/speckit-wizard-canvas/README.md b/plugins/spec-kit-copilot-wizard/extensions/speckit-wizard-canvas/README.md index e39ee3e..c36cd50 100644 --- a/plugins/spec-kit-copilot-wizard/extensions/speckit-wizard-canvas/README.md +++ b/plugins/spec-kit-copilot-wizard/extensions/speckit-wizard-canvas/README.md @@ -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`, diff --git a/plugins/spec-kit-copilot-wizard/extensions/speckit-wizard-canvas/canvas-runtime/snapshot.mjs b/plugins/spec-kit-copilot-wizard/extensions/speckit-wizard-canvas/canvas-runtime/snapshot.mjs index 85ecabd..41e58a5 100644 --- a/plugins/spec-kit-copilot-wizard/extensions/speckit-wizard-canvas/canvas-runtime/snapshot.mjs +++ b/plugins/spec-kit-copilot-wizard/extensions/speckit-wizard-canvas/canvas-runtime/snapshot.mjs @@ -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, diff --git a/plugins/spec-kit-copilot-wizard/extensions/speckit-wizard-canvas/e2e/server.mjs b/plugins/spec-kit-copilot-wizard/extensions/speckit-wizard-canvas/e2e/server.mjs index ac9418e..aac4b96 100644 --- a/plugins/spec-kit-copilot-wizard/extensions/speckit-wizard-canvas/e2e/server.mjs +++ b/plugins/spec-kit-copilot-wizard/extensions/speckit-wizard-canvas/e2e/server.mjs @@ -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, diff --git a/plugins/spec-kit-copilot-wizard/extensions/speckit-wizard-canvas/extension.mjs b/plugins/spec-kit-copilot-wizard/extensions/speckit-wizard-canvas/extension.mjs index 3d26212..719e2f1 100644 --- a/plugins/spec-kit-copilot-wizard/extensions/speckit-wizard-canvas/extension.mjs +++ b/plugins/spec-kit-copilot-wizard/extensions/speckit-wizard-canvas/extension.mjs @@ -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()); @@ -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, diff --git a/plugins/spec-kit-copilot-wizard/extensions/speckit-wizard-canvas/server.mjs b/plugins/spec-kit-copilot-wizard/extensions/speckit-wizard-canvas/server.mjs index 19acdc6..71e1fbd 100644 --- a/plugins/spec-kit-copilot-wizard/extensions/speckit-wizard-canvas/server.mjs +++ b/plugins/spec-kit-copilot-wizard/extensions/speckit-wizard-canvas/server.mjs @@ -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"); @@ -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(); diff --git a/plugins/spec-kit-copilot-wizard/extensions/speckit-wizard-canvas/test/canvas-designer-dialog.test.mjs b/plugins/spec-kit-copilot-wizard/extensions/speckit-wizard-canvas/test/canvas-designer-dialog.test.mjs index 9e42465..e90d3d1 100644 --- a/plugins/spec-kit-copilot-wizard/extensions/speckit-wizard-canvas/test/canvas-designer-dialog.test.mjs +++ b/plugins/spec-kit-copilot-wizard/extensions/speckit-wizard-canvas/test/canvas-designer-dialog.test.mjs @@ -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>/); diff --git a/plugins/spec-kit-copilot-wizard/extensions/speckit-wizard-canvas/ui/phase-runtime.js b/plugins/spec-kit-copilot-wizard/extensions/speckit-wizard-canvas/ui/phase-runtime.js index 691ebdc..6d5ad5e 100644 --- a/plugins/spec-kit-copilot-wizard/extensions/speckit-wizard-canvas/ui/phase-runtime.js +++ b/plugins/spec-kit-copilot-wizard/extensions/speckit-wizard-canvas/ui/phase-runtime.js @@ -379,7 +379,9 @@ export function renderPipelineBanner() {
- + ${state.snapshot?.featureFlags?.generateCanvas === true + ? '' + : ""}
`;