@design-ai/cli
Advanced tools
| // SDK adapter: check(artifact, opts). See docs/AGENT-SDK.md. | ||
| // | ||
| // Read-only: capture is never enabled from the SDK in Phase A (the CLI's | ||
| // `--learn` writes a local learning-profile capture; there is no equivalent | ||
| // opt-in here, consistent with "no file writes" for Phase A). | ||
| import { checkArtifactContent } from "../lib/check.mjs"; | ||
| import { assertKnownRouteId } from "../lib/route.mjs"; | ||
| import { optionalBoolean, optionalString, requireNonEmptyString, requireOptions } from "./validate.mjs"; | ||
| /** | ||
| * Check a generated design Markdown artifact for grounding, accessibility, | ||
| * responsive, unresolved-marker, and route-specific requirements. Pure, | ||
| * read-only adapter over `checkArtifactContent` from cli/lib/check.mjs. | ||
| * | ||
| * @param {string} artifact - Markdown artifact content to check. | ||
| * @param {{routeId?: string, strict?: boolean}} [opts] | ||
| * @returns {object} CheckReport — the same shape as `design-ai check --json`. | ||
| */ | ||
| export function check(artifact, opts = {}) { | ||
| requireNonEmptyString(artifact, "artifact"); | ||
| const options = requireOptions(opts, "check"); | ||
| const routeId = optionalString(options.routeId, "routeId"); | ||
| if (routeId) assertKnownRouteId(routeId, { allowEmpty: false }); | ||
| // `strict` only affects CLI exit-code behavior; validated for type safety, | ||
| // but the SDK returns the report as-is and lets the caller decide what to | ||
| // do with report.status — there is no process exit code to influence here. | ||
| optionalBoolean(options.strict, "strict", false); | ||
| return checkArtifactContent({ | ||
| content: artifact, | ||
| filePath: "sdk", | ||
| routeId, | ||
| }); | ||
| } |
| // design-ai Agent SDK — Phase A (read-only). See docs/AGENT-SDK.md and docs/SDK.md. | ||
| // | ||
| // A curated, semver-stable adapter over the same cli/lib functions the CLI and | ||
| // MCP server call. Each verb validates its inputs, resolves the package root | ||
| // the same way the CLI does, and returns a plain JSON-serializable object — | ||
| // the same shape the CLI's --json mode emits. No network calls, no runtime | ||
| // dependencies. Phase A performs no file writes: no learning-usage sidecar | ||
| // writes, even from prompt/pack's withLearning option. | ||
| // | ||
| // Import path: `@design-ai/cli/sdk` (see the "exports" map in package.json). | ||
| // `cli/lib/*` stays internal and unstable; this barrel is the only supported | ||
| // public surface. Do not import `cli/lib/*.mjs` directly from outside this | ||
| // package — only the 8 named exports below are covered by the semver | ||
| // stability contract described in docs/SDK.md. | ||
| export { check } from "./check-adapter.mjs"; | ||
| export { pack } from "./pack-adapter.mjs"; | ||
| export { prompt } from "./prompt-adapter.mjs"; | ||
| export { recall } from "./recall-adapter.mjs"; | ||
| export { route, routes } from "./route-adapter.mjs"; | ||
| export { search } from "./search-adapter.mjs"; | ||
| export { version } from "./version-adapter.mjs"; |
| // SDK adapter: pack(brief, opts). See docs/AGENT-SDK.md. | ||
| // | ||
| // Phase A is read-only: like the prompt adapter, this never records the | ||
| // learning-usage sidecar, even when withLearning is requested. | ||
| import { DESIGN_AI_HOME, SYMLINK_PREFIX } from "../lib/paths.mjs"; | ||
| import { buildPromptPack } from "../lib/pack.mjs"; | ||
| import { | ||
| optionalBoolean, | ||
| optionalInteger, | ||
| optionalString, | ||
| requireNonEmptyString, | ||
| requireOptions, | ||
| } from "./validate.mjs"; | ||
| const DEFAULT_MAX_BYTES = 120_000; | ||
| /** | ||
| * Build a ready-to-use prompt plus bounded context-file bundle from a task | ||
| * brief. Pure, read-only adapter over `buildPromptPack` from cli/lib/pack.mjs. | ||
| * Never records learning-usage, even with withLearning: true. | ||
| * | ||
| * @param {string} brief - Task brief text. | ||
| * @param {{routeId?: string, maxBytes?: number, withLearning?: boolean, learningCategory?: string, learningLimit?: number, withRecall?: boolean, recallLimit?: number}} [opts] | ||
| * @returns {object} Pack — the same shape as `design-ai pack --json` (minus learningUsage). | ||
| */ | ||
| export function pack(brief, opts = {}) { | ||
| requireNonEmptyString(brief, "brief"); | ||
| const options = requireOptions(opts, "pack"); | ||
| const routeId = optionalString(options.routeId, "routeId"); | ||
| const maxBytes = optionalInteger(options.maxBytes, "maxBytes", { | ||
| fallback: DEFAULT_MAX_BYTES, | ||
| min: 1000, | ||
| max: 1_000_000, | ||
| }); | ||
| const withLearning = optionalBoolean(options.withLearning, "withLearning", false); | ||
| const learningCategory = optionalString(options.learningCategory, "learningCategory"); | ||
| const learningLimit = optionalInteger(options.learningLimit, "learningLimit", { fallback: 0, min: 1, max: 100, zeroMeansUnset: true }); | ||
| const withRecall = optionalBoolean(options.withRecall, "withRecall", false); | ||
| const recallLimit = optionalInteger(options.recallLimit, "recallLimit", { fallback: 0, min: 1, max: 20, zeroMeansUnset: true }); | ||
| return buildPromptPack({ | ||
| brief, | ||
| sourceRoot: DESIGN_AI_HOME, | ||
| prefix: SYMLINK_PREFIX, | ||
| maxBytes, | ||
| routeId, | ||
| withLearning, | ||
| learningCategory, | ||
| learningLimit, | ||
| withRecall, | ||
| recallLimit, | ||
| }); | ||
| } |
| // SDK adapter: prompt(brief, opts). See docs/AGENT-SDK.md. | ||
| // | ||
| // Phase A is read-only: unlike the CLI's `--with-learning` (which records a | ||
| // local usage sidecar entry via recordLearningUsage), the SDK adapter never | ||
| // writes the learning-usage sidecar, even when withLearning is requested. It | ||
| // only reads the local learning profile to build the learning context. | ||
| import { DESIGN_AI_HOME, SYMLINK_PREFIX } from "../lib/paths.mjs"; | ||
| import { buildPromptPlan } from "../lib/prompt.mjs"; | ||
| import { | ||
| optionalBoolean, | ||
| optionalInteger, | ||
| optionalString, | ||
| requireNonEmptyString, | ||
| requireOptions, | ||
| } from "./validate.mjs"; | ||
| /** | ||
| * Build a ready-to-use agent prompt plan from a task brief. Pure, read-only | ||
| * adapter over `buildPromptPlan` from cli/lib/prompt.mjs. Never records | ||
| * learning-usage, even with withLearning: true (Phase A is read-only). | ||
| * | ||
| * @param {string} brief - Task brief text. | ||
| * @param {{routeId?: string, withLearning?: boolean, learningCategory?: string, learningLimit?: number, withRecall?: boolean, recallLimit?: number}} [opts] | ||
| * @returns {object|null} PromptPlan — the same shape as `design-ai prompt --json` (minus learningUsage). | ||
| */ | ||
| export function prompt(brief, opts = {}) { | ||
| requireNonEmptyString(brief, "brief"); | ||
| const options = requireOptions(opts, "prompt"); | ||
| const routeId = optionalString(options.routeId, "routeId"); | ||
| const withLearning = optionalBoolean(options.withLearning, "withLearning", false); | ||
| const learningCategory = optionalString(options.learningCategory, "learningCategory"); | ||
| const learningLimit = optionalInteger(options.learningLimit, "learningLimit", { fallback: 0, min: 1, max: 100, zeroMeansUnset: true }); | ||
| const withRecall = optionalBoolean(options.withRecall, "withRecall", false); | ||
| const recallLimit = optionalInteger(options.recallLimit, "recallLimit", { fallback: 0, min: 1, max: 20, zeroMeansUnset: true }); | ||
| return buildPromptPlan({ | ||
| brief, | ||
| sourceRoot: DESIGN_AI_HOME, | ||
| prefix: SYMLINK_PREFIX, | ||
| routeId, | ||
| withLearning, | ||
| learningCategory, | ||
| learningLimit, | ||
| withRecall, | ||
| recallLimit, | ||
| }); | ||
| } |
| // SDK adapter: recall(query, opts). See docs/AGENT-SDK.md. | ||
| // | ||
| // Combined recall view: brief-relevant shipped corpus knowledge (`corpus`) plus | ||
| // brief-relevant local learning-profile entries (`learning`), both ranked by the | ||
| // same shipped deterministic lexical scorer used by `search --ranked`. Read-only: | ||
| // reads the local corpus and `DESIGN_AI_LEARNING_FILE` learning profile, exactly | ||
| // as `design-ai learn --recall` does, and never writes either. | ||
| import { DESIGN_AI_HOME } from "../lib/paths.mjs"; | ||
| import { buildLearnRecall, DEFAULT_RECALL_LIMIT } from "../lib/recall.mjs"; | ||
| import { optionalInteger, optionalString, requireNonEmptyString, requireOptions } from "./validate.mjs"; | ||
| /** | ||
| * Recall brief-relevant corpus knowledge and local learning-profile entries for | ||
| * a query. Pure, read-only adapter over `buildLearnRecall` from cli/lib/recall.mjs. | ||
| * | ||
| * @param {string} query - Recall query text. | ||
| * @param {{limit?: number, category?: string}} [opts] | ||
| * @returns {{corpus: object, learning: object}} combined recall view. | ||
| */ | ||
| export function recall(query, opts = {}) { | ||
| requireNonEmptyString(query, "query"); | ||
| const options = requireOptions(opts, "recall"); | ||
| const limit = optionalInteger(options.limit, "limit", { fallback: DEFAULT_RECALL_LIMIT, min: 1, max: 20 }); | ||
| const category = optionalString(options.category, "category"); | ||
| const result = buildLearnRecall({ | ||
| query, | ||
| limit, | ||
| category, | ||
| designAiPath: DESIGN_AI_HOME, | ||
| }); | ||
| return { | ||
| corpus: result.corpus, | ||
| learning: result.learning, | ||
| }; | ||
| } |
| // SDK adapter: route(brief, opts) and routes(). See docs/AGENT-SDK.md. | ||
| import { DESIGN_AI_HOME } from "../lib/paths.mjs"; | ||
| import { readRouteManifestVersion, routeBrief, routeCatalog } from "../lib/route.mjs"; | ||
| import { | ||
| optionalBoolean, | ||
| optionalInteger, | ||
| requireNonEmptyString, | ||
| requireOptions, | ||
| } from "./validate.mjs"; | ||
| /** | ||
| * Recommend the best design-ai route(s), commands, skills, and knowledge files | ||
| * for a task brief. Pure, read-only adapter over `routeBrief` from cli/lib/route.mjs. | ||
| * | ||
| * @param {string} brief - Task brief text. | ||
| * @param {{limit?: number, explain?: boolean}} [opts] | ||
| * @returns {Array<object>} RouteResult[] — the same shape as `design-ai route --json`'s `routes` array. | ||
| */ | ||
| export function route(brief, opts = {}) { | ||
| requireNonEmptyString(brief, "brief"); | ||
| const options = requireOptions(opts, "route"); | ||
| const limit = optionalInteger(options.limit, "limit", { fallback: 3, min: 1, max: 10 }); | ||
| const explain = optionalBoolean(options.explain, "explain", false); | ||
| return routeBrief({ | ||
| brief, | ||
| sourceRoot: DESIGN_AI_HOME, | ||
| limit, | ||
| explain, | ||
| }); | ||
| } | ||
| /** | ||
| * List the full route catalog (all route ids with their static metadata), | ||
| * independent of any brief. Pure, read-only adapter over `routeCatalog`. | ||
| * | ||
| * @returns {{version: string, routes: Array<object>}} RouteCatalog | ||
| */ | ||
| export function routes() { | ||
| return { | ||
| version: readRouteManifestVersion(DESIGN_AI_HOME), | ||
| routes: routeCatalog({ sourceRoot: DESIGN_AI_HOME }), | ||
| }; | ||
| } |
| // SDK adapter: search(query, opts). See docs/AGENT-SDK.md. | ||
| import { DESIGN_AI_HOME } from "../lib/paths.mjs"; | ||
| import { rankedSearchCorpus } from "../lib/search-ranked.mjs"; | ||
| import { DEFAULT_SEARCH_DIRS, searchCorpus } from "../lib/search.mjs"; | ||
| import { | ||
| optionalBoolean, | ||
| optionalInteger, | ||
| optionalString, | ||
| requireNonEmptyString, | ||
| requireOptions, | ||
| } from "./validate.mjs"; | ||
| function resolveDirs(dirName) { | ||
| if (!dirName) return DEFAULT_SEARCH_DIRS; | ||
| if (!DEFAULT_SEARCH_DIRS.includes(dirName)) { | ||
| throw new Error(`dir must be one of: ${DEFAULT_SEARCH_DIRS.join(", ")}`); | ||
| } | ||
| return [dirName]; | ||
| } | ||
| /** | ||
| * Search the local design-ai markdown corpus. Pure, read-only adapter over | ||
| * `searchCorpus` / `rankedSearchCorpus` from cli/lib/search.mjs and | ||
| * cli/lib/search-ranked.mjs. | ||
| * | ||
| * @param {string} query - Search query text. | ||
| * @param {{dir?: string, limit?: number, ranked?: boolean}} [opts] | ||
| * @returns {Array<object>} SearchHit[] — line hits by default, ranked (score + matchedTokens) hits when `ranked: true`. | ||
| */ | ||
| export function search(query, opts = {}) { | ||
| requireNonEmptyString(query, "query"); | ||
| const options = requireOptions(opts, "search"); | ||
| const dirName = optionalString(options.dir, "dir"); | ||
| const dirs = resolveDirs(dirName); | ||
| const limit = optionalInteger(options.limit, "limit", { fallback: 20, min: 1, max: 500 }); | ||
| const ranked = optionalBoolean(options.ranked, "ranked", false); | ||
| if (ranked) { | ||
| return rankedSearchCorpus({ | ||
| query, | ||
| designAiPath: DESIGN_AI_HOME, | ||
| dirs, | ||
| limit, | ||
| }).hits; | ||
| } | ||
| return searchCorpus({ | ||
| query, | ||
| designAiPath: DESIGN_AI_HOME, | ||
| dirs, | ||
| limit, | ||
| }); | ||
| } |
| // Input validation helpers shared by the SDK adapters (cli/sdk/*.mjs). | ||
| // Fail fast with a clear Error on bad input, per docs/AGENT-SDK.md Phase A. | ||
| export function requireNonEmptyString(value, label) { | ||
| if (typeof value !== "string" || value.trim() === "") { | ||
| throw new Error(`${label} must be a non-empty string`); | ||
| } | ||
| return value; | ||
| } | ||
| export function optionalString(value, label, fallback = "") { | ||
| if (value === undefined || value === null) return fallback; | ||
| if (typeof value !== "string") { | ||
| throw new Error(`${label} must be a string`); | ||
| } | ||
| return value; | ||
| } | ||
| export function optionalBoolean(value, label, fallback = false) { | ||
| if (value === undefined || value === null) return fallback; | ||
| if (typeof value !== "boolean") { | ||
| throw new Error(`${label} must be a boolean`); | ||
| } | ||
| return value; | ||
| } | ||
| // `zeroMeansUnset: true` is for options where the underlying cli/lib function | ||
| // treats 0 itself as "not set, use the library default" (e.g. learningLimit, | ||
| // recallLimit in prompt.mjs/pack.mjs use `learningLimit || 12`). In that case | ||
| // an explicit 0 is accepted and passed through unchecked against min/max, | ||
| // exactly like omitting the option. | ||
| export function optionalInteger(value, label, { fallback = 0, min = -Infinity, max = Infinity, zeroMeansUnset = false } = {}) { | ||
| if (value === undefined || value === null) return fallback; | ||
| if (zeroMeansUnset && value === 0) return 0; | ||
| const num = Number(value); | ||
| if (!Number.isInteger(num) || num < min || num > max) { | ||
| throw new Error(`${label} must be an integer from ${min} to ${max}`); | ||
| } | ||
| return num; | ||
| } | ||
| export function requireOptions(opts, label) { | ||
| if (opts === undefined || opts === null) return {}; | ||
| if (typeof opts !== "object" || Array.isArray(opts)) { | ||
| throw new Error(`${label} options must be a plain object`); | ||
| } | ||
| return opts; | ||
| } |
| // SDK adapter: version(). See docs/AGENT-SDK.md. | ||
| import { collectVersionReport } from "../commands/version.mjs"; | ||
| /** | ||
| * Report the CLI package version and the plugin/corpus version. Pure, | ||
| * read-only adapter over `collectVersionReport` from cli/commands/version.mjs. | ||
| * | ||
| * @returns {{cli: string, corpus: string}} version summary. | ||
| */ | ||
| export function version() { | ||
| const report = collectVersionReport(); | ||
| return { | ||
| cli: report.versions.cli, | ||
| corpus: report.versions.plugin, | ||
| }; | ||
| } |
| # Agent SDK design | ||
| > Status: draft / planning — 2026-07-04 | ||
| This document opens the Agent SDK phase — the fast-follow chosen in [NEXT-SURFACE-DECISION.md](NEXT-SURFACE-DECISION.md), now unblocked because the CLI recall interface has been stable across two releases (`--with-recall` / `learn --recall` shipped in v4.57.0 and stayed stable through v4.58.0's `route --explain` enrichment). | ||
| Nothing here is shipped until a phase below is implemented and release-gated. Until then, the only supported programmatic entry to design-ai is the CLI and the MCP server. | ||
| ## Goal | ||
| Let an external Node.js program — an agent runtime, a build script, a custom tool — use design-ai's deterministic design capabilities **as importable functions**, without shelling out to the CLI or spawning the MCP server. The SDK is a thin, documented, semver-stable adapter over the same `cli/lib` functions the CLI and MCP already call, so a capability that ships in the CLI is instantly available to an SDK consumer. | ||
| The decision record scored this surface highest on leverage and learning synergy precisely because it reuses the shared library verbatim: `route`, `prompt`, `pack`, `search --ranked`, `recall`, and `check` are already pure functions in `cli/lib`. | ||
| ## Non-goals | ||
| - No new capabilities. The SDK exposes existing CLI/MCP behavior; it does not add design logic. | ||
| - No runtime dependencies, no network calls, no telemetry — same posture as the CLI. | ||
| - No exposure of the entire `cli/lib` internal surface. The SDK is a curated, stable subset; `cli/lib/*` stays internal and free to refactor. | ||
| - No new process model — the SDK runs in the caller's process, deterministic and synchronous where the underlying functions are. | ||
| - No model inference or fine-tuning (unchanged product stance). | ||
| ## The stable surface | ||
| A single curated entry (`cli/sdk/index.mjs`) re-exports a small set of adapter functions with **their own stable signatures**, independent of the internal `cli/lib` shapes. Internal functions may be renamed or refactored; the SDK adapter absorbs that so the public API stays put. | ||
| Proposed surface (Phase A — read-only verbs): | ||
| ```js | ||
| import { route, prompt, pack, search, recall, check, routes, version } from "@design-ai/cli/sdk"; | ||
| route(brief, { limit = 3, explain = false }) // → RouteResult[] | ||
| prompt(brief, { routeId, withLearning, learningCategory, learningLimit, withRecall, recallLimit }) // → PromptPlan | ||
| pack(brief, { routeId, maxBytes, withLearning, learningCategory, learningLimit, withRecall, recallLimit }) // → Pack | ||
| search(query, { dir, limit = 20, ranked = false }) // → SearchHit[] (ranked: BM25 hits with scores) | ||
| recall(query, { limit = 5, category = "" }) // → { corpus, learning } (combined recall view) | ||
| check(artifact, { routeId = "", strict = false }) // → CheckReport | ||
| routes() // → RouteCatalog | ||
| version() // → { cli, corpus } | ||
| ``` | ||
| Every function is a pure adapter: it validates its inputs, calls the corresponding `cli/lib` function with the resolved package root, and returns a plain JSON-serializable object — the same shape the CLI's `--json` mode emits, which is already a contract covered by smoke tests. No function writes files or reads the network in Phase A. `recall` and the `withRecall`/`withLearning` options read the local corpus and the user's local `learning.json` (via `DESIGN_AI_LEARNING_FILE`) exactly as the CLI does; nothing else is read. | ||
| ## Packaging | ||
| Add an `exports` map to `package.json`: | ||
| ```json | ||
| "exports": { | ||
| "./sdk": "./cli/sdk/index.mjs", | ||
| "./package.json": "./package.json" | ||
| } | ||
| ``` | ||
| - `@design-ai/cli/sdk` is the one public import path. The bare package (`.`) intentionally stays unexported so `import "@design-ai/cli"` does not accidentally couple callers to internals; the CLI is invoked as a bin, the SDK via the explicit subpath. | ||
| - `cli/` is already in `files`, so `cli/sdk/` ships with the package with no `files` change. | ||
| - The `package-contents` audit and `package:smoke` must cover the new entry: a packed-tarball smoke that `import`s `@design-ai/cli/sdk` and calls `route`/`search --ranked`/`recall` and asserts deterministic output. | ||
| ## Stability contract | ||
| - The `@design-ai/cli/sdk` surface is **semver-stable**: additive changes are minor, signature/return changes are major. This is stated in the SDK reference doc and enforced by SDK contract tests that pin the exported names and return-shape keys. | ||
| - `cli/lib/*` remains **internal and unstable** — importing `@design-ai/cli/cli/lib/...` is unsupported. The adapter layer is the seam that lets internals refactor (as they did repeatedly during the retrieval work) without breaking SDK consumers. | ||
| - Determinism: same inputs → same outputs, matching the CLI. The SDK adds no randomness or time-dependence. | ||
| ## Phased plan | ||
| ### Phase A — read-only SDK core | ||
| The verbs above (`route`, `prompt`, `pack`, `search`, `recall`, `check`, `routes`, `version`) as pure adapters over existing `cli/lib` functions. Read-only: no file writes, no network, no learning-usage sidecar writes (the SDK's `prompt`/`pack` default to not recording usage, unlike the CLI's opt-in `--with-learning` sidecar write — an SDK caller opts in explicitly if that is added later). | ||
| Verification gates: | ||
| - `node --test` unit coverage for each adapter: signature, option defaults, return-shape keys, determinism, and parity with the CLI `--json` output for a fixed brief. | ||
| - SDK contract test pinning the exported names and the return-shape key sets (the semver anchor). | ||
| - `npm run audit` 8/8 (the SDK reference doc's links resolve; frontmatter valid). | ||
| - `npm run release:check` additions: packed-tarball smoke that imports `@design-ai/cli/sdk` from the installed package and exercises `route`/`search`(ranked)/`recall`, plus a registry-smoke parity check after publish. | ||
| - `npm run release:metadata` unchanged (README stance preserved). | ||
| ### Phase B — optional, explicit local writes | ||
| Only if demand appears: opt-in adapters that mirror the CLI's local-write commands (`learn.remember`, `learn.feedback`, `check` with capture), each requiring an explicit option and writing only the local learning profile, never the network. Deferred until an adopter needs it; Phase A is read-only. | ||
| ## Integration points | ||
| - **MCP server** already wraps the same `cli/lib` functions; the SDK and MCP stay in lockstep because both are thin layers over one core. No MCP change is required for Phase A. | ||
| - **Retrieval** (`search` ranked, `recall`, `withRecall`) flows into the SDK for free — the shared lexical scorer and the generated-index exclusion apply identically. | ||
| - **Docs**: a `docs/SDK.md` (or this file, promoted from draft) becomes the public SDK reference; the README install table gains an "SDK" row pointing to it. | ||
| ## Risks and open questions | ||
| Risks: | ||
| - **Semver commitment.** Once published, the SDK surface is a promise. Mitigation: keep Phase A small (8 verbs), pin names/shapes in a contract test, and treat the adapter as the only stable seam. | ||
| - **Return-shape coupling.** SDK returns mirror the CLI `--json` shapes; if those change, the SDK breaks. Mitigation: the same smoke/parity tests that already guard the CLI JSON guard the SDK, and shape changes are already major-version events. | ||
| Open questions (answer during Phase A implementation review): | ||
| 1. Export the bare package root (`.`) as an alias for `./sdk`, or keep `./sdk` the only path? (Leaning: `./sdk` only, to avoid implying the whole package is the SDK.) | ||
| 2. Ship TypeScript types (`.d.ts`) hand-written for the 8 verbs, or JSDoc-only for the zero-toolchain stance? (Leaning: JSDoc + a hand-written `sdk/index.d.ts` with no build step.) | ||
| 3. Does `prompt`/`pack` via the SDK ever write the learning-usage sidecar, or is that strictly a Phase B explicit opt-in? (Leaning: never in Phase A; read-only.) | ||
| 4. Is `check` in Phase A (read-only quality check) or deferred with the other write-adjacent verbs? (Leaning: Phase A — `check` is read-only when capture is off.) |
+162
| # Agent SDK reference | ||
| > Status: shipped (Phase A) — read-only verbs, semver-stable surface | ||
| `@design-ai/cli/sdk` lets an external Node.js program — an agent runtime, a build script, a custom tool — use design-ai's deterministic design capabilities as importable functions, without shelling out to the CLI or spawning the MCP server. It is a thin, curated adapter over the same `cli/lib` functions the CLI and MCP server already call, so a capability that ships in the CLI is instantly available to an SDK consumer. | ||
| See [`AGENT-SDK.md`](AGENT-SDK.md) for the full design rationale, phased plan, and open questions. This page is the public reference for the shipped Phase A surface. | ||
| ## Install and import | ||
| The SDK ships inside the `@design-ai/cli` package (no separate install): | ||
| ```bash | ||
| npm install @design-ai/cli | ||
| ``` | ||
| ```js | ||
| import { route, prompt, pack, search, recall, check, routes, version } from "@design-ai/cli/sdk"; | ||
| ``` | ||
| Only the `./sdk` subpath is exported — `import "@design-ai/cli"` (the bare package root) is intentionally not exported, so importing the SDK is always an explicit `@design-ai/cli/sdk` import. `cli/lib/*` is internal and unstable; do not import it directly. | ||
| ## Phase A: read-only | ||
| Every verb below is a pure, read-only adapter: | ||
| - It validates its inputs and throws a plain `Error` with a clear message on bad input (e.g. a non-string brief). | ||
| - It resolves the package root the same way the CLI does. | ||
| - It calls the corresponding `cli/lib` function and returns a plain JSON-serializable object — the same shape the CLI's `--json` mode emits. | ||
| - It performs no file writes, no network calls, and no learning-usage sidecar writes. This is a deliberate difference from the CLI: `prompt`/`pack`'s `withLearning` option in the SDK only **reads** the local learning profile to build context — it never records a usage event, unlike the CLI's `--with-learning` flag. There is no SDK equivalent of `check --learn` (capture) in Phase A. | ||
| - Given the same inputs, it returns the same outputs (determinism), matching the CLI. | ||
| ## Verbs | ||
| ### `route(brief, opts)` | ||
| Recommend the best design-ai route(s), commands, skills, and knowledge files for a task brief. | ||
| ```js | ||
| route(brief: string, opts?: { limit?: number, explain?: boolean }): RouteResult[] | ||
| ``` | ||
| - `limit` — maximum route recommendations, 1-10. Default: `3`. | ||
| - `explain` — include route scoring, reference coverage, and related-knowledge detail. Default: `false`. | ||
| Returns an array of `RouteResult` objects (`id`, `label`, `score`, `confidence`, `matchedKeywords`, `command`, `skills`, `agents`, `knowledge`, `keywords`, `explanation`, plus `relatedKnowledge` when `explain: true`) — the same shape as the `routes` array in `design-ai route --json`. | ||
| ### `prompt(brief, opts)` | ||
| Build a ready-to-use agent prompt plan from a task brief. | ||
| ```js | ||
| prompt(brief: string, opts?: { | ||
| routeId?: string, | ||
| withLearning?: boolean, | ||
| learningCategory?: string, | ||
| learningLimit?: number, | ||
| withRecall?: boolean, | ||
| recallLimit?: number, | ||
| }): PromptPlan | null | ||
| ``` | ||
| - `routeId` — force a route id instead of scoring the brief. | ||
| - `withLearning` — include brief-relevant local learning preferences (reads `DESIGN_AI_LEARNING_FILE`; **never** records usage, unlike the CLI's `--with-learning`). | ||
| - `learningCategory`, `learningLimit` (1-100) — scope/limit the learning context; require `withLearning`. | ||
| - `withRecall` — include brief-relevant shipped corpus knowledge. | ||
| - `recallLimit` (1-20) — limit recalled corpus files; requires `withRecall`. | ||
| Returns a `PromptPlan` object (`brief`, `version`, `route`, `slashCommand`, `referenceExamples`, `filesToRead`, `checklist`, `qualityCommand`, `prompt`, plus `learningContext`/`recall` when requested) — the same shape as `design-ai prompt --json`, minus `learningUsage` (Phase A never writes it). | ||
| ### `pack(brief, opts)` | ||
| Build a ready-to-use prompt plus a bounded context-file bundle from a task brief. | ||
| ```js | ||
| pack(brief: string, opts?: { | ||
| routeId?: string, | ||
| maxBytes?: number, | ||
| withLearning?: boolean, | ||
| learningCategory?: string, | ||
| learningLimit?: number, | ||
| withRecall?: boolean, | ||
| recallLimit?: number, | ||
| }): Pack | ||
| ``` | ||
| - `maxBytes` — maximum context bytes to include, 1000-1,000,000. Default: `120000`. | ||
| - The remaining options mirror `prompt`'s learning/recall options. | ||
| Returns a `Pack` object (`brief`, `version`, `maxBytes`, `usedBytes`, `summary`, `warnings`, `plan`, `files`, `markdown`) — the same shape as `design-ai pack --json`, minus `learningUsage`. | ||
| ### `search(query, opts)` | ||
| Search the local design-ai markdown corpus. | ||
| ```js | ||
| search(query: string, opts?: { dir?: string, limit?: number, ranked?: boolean }): SearchHit[] | ||
| ``` | ||
| - `dir` — restrict to one corpus directory: `knowledge`, `examples`, `skills`, `docs`, `agents`, or `commands`. | ||
| - `limit` — maximum hits, 1-500. Default: `20`. | ||
| - `ranked` — rank results with the deterministic lexical (BM25-style) scorer instead of returning raw line hits. Default: `false`. | ||
| Returns `SearchHit[]`. Unranked hits: `{ file, relPath, lineNumber, preview }`. Ranked hits: `{ file, relPath, score, matchedTokens, preview }` — the same shapes as `design-ai search --json` and `design-ai search --ranked --json`. | ||
| ### `recall(query, opts)` | ||
| Recall brief-relevant shipped corpus knowledge and local learning-profile entries for a query — a combined read-only view. | ||
| ```js | ||
| recall(query: string, opts?: { limit?: number, category?: string }): { corpus: object, learning: object } | ||
| ``` | ||
| - `limit` — applies to both the corpus and learning lists, 1-20. Default: `5`. | ||
| - `category` — scopes only the learning list. | ||
| Returns `{ corpus: { candidateCount, selectedCount, selected }, learning: { mode, candidateCount, selectedCount, selected } }` — the same shape as `design-ai learn --recall --json`. | ||
| ### `check(artifact, opts)` | ||
| Check a generated design Markdown artifact for grounding, accessibility, responsive, unresolved-marker, and route-specific requirements. | ||
| ```js | ||
| check(artifact: string, opts?: { routeId?: string, strict?: boolean }): CheckReport | ||
| ``` | ||
| - `routeId` — add route-specific checks (e.g. `component-spec`, `palette-from-brand`). Must be a known route id. | ||
| - `strict` — accepted for CLI-flag parity but has no effect on the returned report; the SDK never sets a process exit code. Inspect `report.status` yourself. | ||
| Returns a `CheckReport` object (`filePath`, `status`, `passes`, `warnings`, `failures`, `total`, `score`, `results`, plus `routeId` when passed) — the same shape as `design-ai check --json` (minus any `--learn` capture, which Phase A does not support). | ||
| ### `routes()` | ||
| List the full route catalog (every route id with its static metadata), independent of any brief. | ||
| ```js | ||
| routes(): { version: string, routes: RouteResult[] } | ||
| ``` | ||
| Returns the same shape as `design-ai route --list --json`. | ||
| ### `version()` | ||
| Report the CLI package version and the plugin/corpus version. | ||
| ```js | ||
| version(): { cli: string, corpus: string } | ||
| ``` | ||
| ## Stability contract | ||
| `@design-ai/cli/sdk` is **semver-stable**: | ||
| - Additive changes (new optional fields, new opt-in options) are minor version bumps. | ||
| - Signature or return-shape changes are major version bumps. | ||
| - The 8 exported names (`route`, `prompt`, `pack`, `search`, `recall`, `check`, `routes`, `version`) and their return-shape key sets are pinned by an SDK contract test (`cli/sdk/index.test.mjs`) — this is the semver anchor. If a name or a top-level key drifts unintentionally, that test fails. | ||
| - `cli/lib/*` remains internal and unstable. Only `@design-ai/cli/sdk` is a supported import path. | ||
| - Determinism: the same inputs always produce the same outputs, with no randomness or time-dependence added by the adapter layer. | ||
| ## Phase B (not yet shipped) | ||
| Phase A is read-only by design. A future Phase B may add opt-in, explicit local-write adapters mirroring the CLI's local-write commands (`learn.remember`, `learn.feedback`, `check` with capture) — deferred until an adopter needs it. See [`AGENT-SDK.md`](AGENT-SDK.md#phase-b-optional-explicit-local-writes) for the current plan. |
| { | ||
| "name": "design-ai", | ||
| "version": "4.58.0", | ||
| "version": "4.59.0", | ||
| "description": "Senior product designer for any AI coding agent. 20 skills, 17 commands, 4 review agents covering UI/UX, website improvement, design systems, motion, illustration, print, video, game UI, conversational, and spatial design. Korean market depth.", | ||
@@ -5,0 +5,0 @@ "author": { |
| # External Publication Status | ||
| > Checked: 2026-07-03 | ||
| > Checked: 2026-07-04 | ||
| > Scope: npm registry, GitHub Pages, Homebrew tap, VS Code Marketplace, Claude/Codex MCP | ||
@@ -8,3 +8,3 @@ | ||
| npm is publicly published at `@design-ai/cli@4.57.0` (npm dist-tag `latest` = `4.57.0`); publish run `28663430171` from tag `v4.57.0` succeeded with provenance and all pre-publish gates green (8 audits, CLI unit tests, release self-tests, package contents, and the packed-tarball installed-package smoke on the 4.57.0 tarball). The automated post-publish live registry smoke step failed on a stale learn-relevance token-order assertion in `tools/audit/registry-smoke.py` — a verification-tool bug fixed on `main` in `710656a`, not a package defect; the published package is validated by the pre-publish packed-tarball smoke. GitHub Release `v4.57.0` is published, and the Homebrew tap formula points at the `v4.57.0` release source tarball with a verified SHA-256. The VS Code extension `sungjin.design-ai-vscode` remains published at `0.4.1`. GitHub Pages docs are publicly reachable. | ||
| npm is publicly published at `@design-ai/cli@4.58.0` (npm dist-tag `latest` = `4.58.0`); publish run from tag `v4.58.0` succeeded with provenance and all pre-publish gates green. After fixing the remaining stale learn-relevance score-type assertions in `tools/audit/registry-smoke.py` (`16e97aa`), the live `npm run registry:smoke` passes cleanly against published `@design-ai/cli@4.58.0` ("Registry smoke passed"), covering the retrieval surfaces (index/ranked/embeddings/recall) and route enrichment. GitHub Release `v4.58.0` is published, and the Homebrew tap formula points at the `v4.58.0` release source tarball with a verified SHA-256. The VS Code extension `sungjin.design-ai-vscode` remains published at `0.4.1`. GitHub Pages docs are publicly reachable. | ||
@@ -15,8 +15,8 @@ ## Results | ||
| |---|---|---|---| | ||
| | npm registry | `@design-ai/cli` | Published latest is `4.57.0`; publish run `28663430171` (tag `v4.57.0`) succeeded with provenance. Pre-publish packed-tarball smoke passed on the 4.57.0 tarball; the post-publish live registry smoke failed on a stale assertion (fixed on `main` `710656a`). | publish run `28663430171`; `npm view @design-ai/cli version` → `4.57.0` | | ||
| | npm registry | `@design-ai/cli` | Published latest is `4.58.0` (tag `v4.58.0`, provenance). Pre-publish packed-tarball smoke and post-fix live `npm run registry:smoke` both pass for `@design-ai/cli@4.58.0`. | `npm view @design-ai/cli version` → `4.58.0`; live registry smoke "Registry smoke passed" | | ||
| | GitHub Pages | `https://sungjin9288.github.io/design-ai/` | Published and reachable: HTTP `200`, design-ai MkDocs page rendered | `evidence/cli-logs/github-pages-status.log` | | ||
| | Homebrew tap | `Formula/design-ai.rb` | Formula pinned to `v4.57.0` release source tarball with SHA-256 `c1fed279ed7bc2daf42473bd9a68cf3fa6df6823fe3390b2f2ccee8625407559` (recomputed from the published tag tarball) | `Formula/design-ai.rb` | | ||
| | Homebrew tap | `Formula/design-ai.rb` | Formula pinned to `v4.58.0` release source tarball with SHA-256 `45ec7fa4e82e7af38cdf948e1ad056a3758be848e5541013144e8d93d4c9da37` (recomputed from the published tag tarball) | `Formula/design-ai.rb` | | ||
| | VS Code Marketplace | `sungjin.design-ai-vscode` | Published: run `28431571256` published `v0.4.1`, and the Marketplace Gallery API returned visible version `0.4.1` on 2026-07-02. | `evidence/cli-logs/vscode-marketplace-status.log`, `evidence/cli-logs/vscode-marketplace-secret-status.log`, `evidence/cli-logs/vscode-extension-vsce-package.log`, `evidence/cli-logs/vscode-publish-workflow-status.log` | | ||
| | GitHub Release | `v4.57.0` | Published (not draft/prerelease) for tag `v4.57.0` at commit `4be46d7` | `gh release view v4.57.0` | | ||
| | MCP server | `@design-ai/cli@4.57.0` / local clone | Public npm `design-ai-mcp` responds to initialize and tools/list with 10 tools; local Codex and Claude Code both report `design-ai` MCP as configured and connected. | `evidence/cli-logs/npm-registry-smoke.log`, `evidence/cli-logs/design-ai-mcp-client-status.log` | | ||
| | GitHub Release | `v4.58.0` | Published for tag `v4.58.0` at commit `22dc8a9` | `gh release view v4.58.0` | | ||
| | MCP server | `@design-ai/cli@4.58.0` / local clone | Public npm `design-ai-mcp` responds to initialize and tools/list with 10 tools; local Codex and Claude Code both report `design-ai` MCP as configured and connected. | `evidence/cli-logs/npm-registry-smoke.log`, `evidence/cli-logs/design-ai-mcp-client-status.log` | | ||
@@ -23,0 +23,0 @@ ## Interpretation |
+6
-2
| { | ||
| "name": "@design-ai/cli", | ||
| "version": "4.58.0", | ||
| "version": "4.59.0", | ||
| "description": "Senior product designer for any AI coding agent. Installs design-ai (20 skills, 17 commands, 4 agents) into Claude Code globally. Korean market depth plus website improvement control tower.", | ||
@@ -10,2 +10,6 @@ "bin": { | ||
| "type": "module", | ||
| "exports": { | ||
| "./sdk": "./cli/sdk/index.mjs", | ||
| "./package.json": "./package.json" | ||
| }, | ||
| "engines": { | ||
@@ -15,3 +19,3 @@ "node": ">=18" | ||
| "scripts": { | ||
| "test": "node --test cli/lib/*.test.mjs", | ||
| "test": "node --test cli/lib/*.test.mjs cli/sdk/*.test.mjs", | ||
| "audit": "python3 -B tools/audit/run-all.py", | ||
@@ -18,0 +22,0 @@ "audit:strict": "python3 -B tools/audit/run-all.py --strict", |
+3
-2
@@ -15,3 +15,3 @@ # Design AI | ||
| > **배포 상태, 2026-07-03 확인:** 로컬 `npm run release:check`는 통과했고, GitHub Pages 문서는 live 상태이며, GitHub Release `v4.57.0`과 npm `@design-ai/cli@4.57.0`(`latest`) publish가 provenance와 함께 확인됐어요. Homebrew formula는 `v4.57.0`에 pinning되어 있고, VS Code Marketplace에는 `sungjin.design-ai-vscode@0.4.1`이 공개되어 있어요. v4.57.0 post-publish live registry smoke는 stale 어서션에 걸렸고(`main`에서 수정됨), 패키지는 pre-publish packed-tarball smoke로 검증됐어요. 자세한 내용은 [`docs/external-status.md`](docs/external-status.md)를 확인하세요. | ||
| > **배포 상태, 2026-07-04 확인:** 로컬 `npm run release:check`는 통과했고, GitHub Pages 문서는 live 상태이며, GitHub Release `v4.58.0`과 npm `@design-ai/cli@4.58.0`(`latest`) publish가 provenance와 함께 확인됐어요. 수정 후 live `npm run registry:smoke`가 published `@design-ai/cli@4.58.0`에 대해 깨끗이 통과했어요(retrieval 표면과 route enrichment 포함). Homebrew formula는 `v4.58.0`에 pinning되어 있고, VS Code Marketplace에는 `sungjin.design-ai-vscode@0.4.1`이 공개되어 있어요. 자세한 내용은 [`docs/external-status.md`](docs/external-status.md)를 확인하세요. | ||
@@ -92,2 +92,3 @@ ## 한눈에 보는 커버리지 | ||
| | **Anthropic / OpenAI SDK** | 관련 스킬 `PLAYBOOK.md` 파일을 프롬프트에 포함시켜요. [워크스루](docs/integrations/sdk-walkthrough.md). | | ||
| | **Node.js / Agent SDK** | `import { route, prompt, pack, search, recall, check, routes, version } from "@design-ai/cli/sdk"` — CLI 셸 실행이나 MCP 서버 없이 design-ai의 결정적 기능을 함수로 바로 호출해요. 읽기 전용(Phase A). [SDK 레퍼런스](docs/SDK.md). | | ||
| | **일반 프롬프트** | 어떤 `skills/*/PLAYBOOK.md` 본문이든 붙여넣으세요 — 각각 자기 완결적이에요. | | ||
@@ -176,3 +177,3 @@ | ||
| 전체 단계 로그는 [`docs/ROADMAP.md`](docs/ROADMAP.md), 현재 완료 범위는 [`docs/PRODUCT-READINESS.md`](docs/PRODUCT-READINESS.md)에서 확인하세요. 현재 **v4.57.0**: public npm publish, provenance-backed GitHub Actions release, public registry smoke, Website Console MCP readiness, workspace learning restore/eval coverage, handoff bundle verification, 90%+ component coverage가 완료됐어요. | ||
| 전체 단계 로그는 [`docs/ROADMAP.md`](docs/ROADMAP.md), 현재 완료 범위는 [`docs/PRODUCT-READINESS.md`](docs/PRODUCT-READINESS.md)에서 확인하세요. 현재 **v4.58.0**: public npm publish, provenance-backed GitHub Actions release, public registry smoke, Website Console MCP readiness, workspace learning restore/eval coverage, handoff bundle verification, 90%+ component coverage가 완료됐어요. | ||
@@ -179,0 +180,0 @@ 핵심 디자인 컨설팅 워크플로우는 로컬 릴리스 기준으로 준비되어 있어요. 웹사이트 개선 컨트롤 타워는 zero-dependency static Web App과 `website-improvement` route/skill/command로 제공되고, Site Profile, audit checklist, MCP readiness, refactor prompt, handoff evidence tracking, bundle export/verify/repair를 한 번에 다뤄요. 로컬 학습 선호도는 `design-ai learn`으로 관리해요 — profile bootstrap, feedback 캡처, 읽기 전용 signal registry, 반복 QA 신호에서 만드는 skill 제안, 그리고 backup/restore/curate/audit까지 전부 로컬에서만 동작하는 opt-in 기능이에요. AI 모델 학습이나 fine-tuning은 여전히 현재 배포 범위 밖이에요. |
+3
-2
@@ -15,3 +15,3 @@ # Design AI | ||
| > **Distribution status, checked 2026-07-03:** local `npm run release:check` passes, GitHub Pages docs are live, GitHub Release `v4.57.0` is published, `@design-ai/cli@4.57.0` is public on npm (`latest`) with provenance and registry smoke coverage, the Homebrew formula is pinned to `v4.57.0`, and `sungjin.design-ai-vscode@0.4.1` is public on the VS Code Marketplace. The v4.57.0 post-publish live registry smoke hit a stale assertion (fixed on `main`); the package is validated by the pre-publish packed-tarball smoke. See [`docs/external-status.md`](docs/external-status.md). | ||
| > **Distribution status, checked 2026-07-04:** local `npm run release:check` passes, GitHub Pages docs are live, GitHub Release `v4.58.0` is published, `@design-ai/cli@4.58.0` is public on npm (`latest`) with provenance, and the live `npm run registry:smoke` passes cleanly against `@design-ai/cli@4.58.0`. The Homebrew formula is pinned to `v4.58.0`, and `sungjin.design-ai-vscode@0.4.1` is public on the VS Code Marketplace. See [`docs/external-status.md`](docs/external-status.md). | ||
@@ -93,2 +93,3 @@ ## Coverage at a glance | ||
| | **VS Code** | Install the public Marketplace extension for sidebar trees + quick-pick commands. [Walkthrough](docs/integrations/vscode-walkthrough.md). | | ||
| | **Node.js / Agent SDK** | `import { route, prompt, pack, search, recall, check, routes, version } from "@design-ai/cli/sdk"` — call design-ai's deterministic verbs as functions, no CLI shell-out or MCP server needed. Read-only (Phase A). [SDK reference](docs/SDK.md). | | ||
| | **Plain prompt** | Paste any `skills/*/PLAYBOOK.md` body — each is self-contained. | | ||
@@ -218,3 +219,3 @@ | ||
| See [`docs/ROADMAP.md`](docs/ROADMAP.md) for the full phase log and [`docs/PRODUCT-READINESS.md`](docs/PRODUCT-READINESS.md) for the current completion boundary. Currently at **v4.57.0**: public npm publish, provenance-backed GitHub Actions release, public registry smoke, Website Console MCP readiness, workspace learning restore/eval coverage, handoff bundle verification, and 90%+ component coverage are complete. | ||
| See [`docs/ROADMAP.md`](docs/ROADMAP.md) for the full phase log and [`docs/PRODUCT-READINESS.md`](docs/PRODUCT-READINESS.md) for the current completion boundary. Currently at **v4.58.0**: public npm publish, provenance-backed GitHub Actions release, public registry smoke, Website Console MCP readiness, workspace learning restore/eval coverage, handoff bundle verification, and 90%+ component coverage are complete. | ||
@@ -221,0 +222,0 @@ Core design consulting workflows are locally release-ready. The website improvement control tower ships as a zero-dependency static Web App plus a `website-improvement` route/skill/command, covering Site Profiles, audit checklists, MCP readiness, refactor prompts, handoff evidence tracking, and bundle export/verify/repair. Local learning preferences are available through `design-ai learn` — profile bootstrap, feedback capture, a read-only signals registry, skill-proposal generation from repeated QA signals, and full backup/restore/curate/audit tooling, all local-only and opt-in. AI model training or fine-tuning remains outside the shipped scope. |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
9928681
0.41%632
1.77%90807
0.42%266
0.38%