/** * Authoring a canvas: the request a person types, the files it writes, and the * brief the model builds from. * * Design: `docs/canvas-extensions-design.md` §9 Phase 3, §13. * * This lives in `core/` rather than beside the other `/new-*` scaffolds because * `/new-canvas` stopped being a file-writing command. Copilot's `/create-canvas` * takes a sentence, has the agent write the extension, and opens it in a panel to * iterate on; matching that means the command has to reach the canvas session to * open, and the agent loop to build. Every decision that does not need either — * what the name is, where the file goes, what goes in it, what the model is told — * is here, testable without a terminal, a fork or a model. */ import type { MarketplacePlatform } from "../extensions/plugins/formats/types.js"; /** * Where a canvas extension lives, per platform. * * This does not go through `WorkspaceLayout` like the other scaffolds, and the * reason is that a canvas has no Claude convention to emit into: the surface * exists in Copilot and in hoocode's own `.agents/` tree and nowhere else. A * layout method returning nothing for one adapter would be a worse lie than * naming the two real homes here — these are exactly the roots * `core/canvas/discovery.ts` searches, which is what makes a scaffold live on the * next `/canvas`. */ export declare const CANVAS_HOMES: Partial>; /** Validates a canvas name: lowercase a-z, 0-9, hyphens, no leading/trailing/double hyphens. */ export declare function validateCanvasName(name: string): string | null; /** * Turn a sentence into a directory name. * * Returns undefined when nothing usable survives — an all-punctuation request, or * a sentence of nothing but filler — because inventing `canvas-1` would hide from * the person that we did not understand them. */ export declare function canvasNameFromDescription(description: string): string | undefined; /** What a person asked `/new-canvas` for. */ export interface CanvasRequest { /** Directory and default canvas id. */ name: string; /** * What they want it to do, in their words, or undefined when they only named * one. Present means the model is expected to build it (Copilot's * `/create-canvas` shape); absent means they want the template to edit by hand. */ description: string | undefined; } /** * Parse `/new-canvas`'s argument. * * Three shapes, in the order they are tested: * * - `my-board` — a bare name. Unchanged from before descriptions existed, and * tested first so it can never be re-read as a one-word description. * - `my-board: a kanban board for the release checklist` — both, when someone * cares what the directory is called. A colon rather than a flag because the * rest of the line is prose and a flag parser would have to guess where it ends. * - `a kanban board for the release checklist` — a description, the shape * `/create-canvas` uses. The name is derived and reported. * * Returns a string when the input cannot become a canvas; the caller shows it. */ export declare function parseCanvasRequest(input: string): CanvasRequest | string; /** * A working single-canvas extension. * * Deliberately complete rather than a stub: a canvas has no passive half — its * name, its actions and its UI all come from running its code — so a scaffold * that does not run teaches nothing and cannot be checked with `/canvas open`. * This one opens, serves a page, answers an action, and closes cleanly, which is * the whole contract; everything past that is the author's. * * Shaped like the catalog extensions hoocode already hosts: the only import is * `@github/copilot-sdk/extension` (host-resolved — never installed, see * `docs/canvas-extensions-design.md` §4.1) plus `node:` builtins, and the UI is * served from an ephemeral loopback port behind a per-instance token so nothing * else on the machine can read it. */ export declare const CANVAS_ENTRY_TEMPLATE: (name: string) => string; /** What {@link scaffoldCanvas} wrote. */ export interface CanvasScaffoldResult { /** Workspace-relative entry files created, one per platform target. */ created: string[]; /** Workspace-relative entry files that already existed and were left alone. */ skipped: string[]; } /** * Write the template into every platform target that has a canvas home. * * Existing files are never clobbered — they are reported and skipped, so running * `/new-canvas` twice on a canvas you have been editing cannot lose it. */ export declare function scaffoldCanvas(cwd: string, name: string, platforms: MarketplacePlatform[]): CanvasScaffoldResult; /** Where a build brief's canvas is, if opening it worked. */ export interface CanvasBriefTarget { instanceId: string; url: string | undefined; } /** * The message the model builds from — hoocode's half of `/create-canvas`. * * A scaffold plus a sentence is not a canvas, and the gap between them is the * agent's work. This is what turns "a kanban board for the release checklist" * into a build task with the contract attached, so the model does not have to * infer the rules of a surface it cannot see from a template it has not read. * * Four of those rules are stated because getting them wrong fails in ways whose * symptom does not name the cause: an installed dependency (the resolver already * provides the SDK, and a `node_modules` here is a §4.1 violation), a * `console.log` (corrupts the JSON-RPC channel and surfaces as "non-protocol * stdout"), a write without a reload (changes nothing at all, because the running * child was forked from the old bytes), and a renamed canvas id. * * That last one is the newest and was found the hard way, by building a canvas * with this command: the scaffold names the canvas after the directory, a model * that thinks of a better name renames the `id`, and the next reload drops the * instance the person is looking at — correctly, since the canvas it was opened * against no longer exists. `displayName` is the half they actually see, so it * is the half to change. */ export declare function canvasBuildBrief(name: string, description: string, entryPath: string, target: CanvasBriefTarget | undefined, guidePath?: string): string; //# sourceMappingURL=scaffold.d.ts.map