@design-ai/cli 4.57.0 → 4.59.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "design-ai",
3
- "version": "4.57.0",
3
+ "version": "4.59.0",
4
4
  "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
5
  "author": {
6
6
  "name": "Sungjin Park",
package/CHANGELOG.md CHANGED
@@ -2,6 +2,65 @@
2
2
 
3
3
  User-facing release notes for design-ai. Versions follow semver.
4
4
 
5
+ ## v4.59.0 — Agent SDK Phase A (2026-07)
6
+
7
+ Adds a curated, semver-stable programmatic surface — `@design-ai/cli/sdk` — so an external Node.js program can call design-ai's deterministic design verbs as importable functions, without shelling out to the CLI or spawning the MCP server. The SDK is a thin adapter over the same `cli/lib` functions the CLI and MCP already call, so no design behavior changes. Phase A is read-only: no file writes, no network, no runtime dependencies. All additions are additive and backward-compatible.
8
+
9
+ ### Added
10
+ - `@design-ai/cli/sdk` — eight read-only adapter verbs with their own stable signatures: `route(brief, opts)`, `prompt(brief, opts)`, `pack(brief, opts)`, `search(query, opts)`, `recall(query, opts)`, `check(artifact, opts)`, `routes()`, and `version()`. Each validates its inputs, resolves the package root the same way the CLI does, and returns the same JSON-serializable shape the CLI's `--json` mode emits. `check` operates on artifact content (not a path), so it reads and writes nothing.
11
+ - `package.json` `exports` map exposing `./sdk` (and `./package.json`) as the only public import paths; `cli/lib/*` stays internal and unstable, refactorable behind the adapter seam.
12
+ - `docs/SDK.md` — the public SDK reference (surface, return shapes, semver stability contract) — plus a "Node.js / Agent SDK" row in the README install tables and an Agent SDK mkdocs nav section.
13
+
14
+ ### Changed
15
+ - `npm test` now also runs `cli/sdk/*.test.mjs` (61 new SDK tests, 540 total), including a contract test (`cli/sdk/index.test.mjs`) that pins the eight exported names and their return-shape keys — the semver anchor — and asserts Phase A writes no `learningUsage` sidecar.
16
+ - `tools/audit/package-smoke.py` now imports `@design-ai/cli/sdk` from the installed tarball and exercises the eight verbs, asserting determinism and the read-only guarantee.
17
+
18
+ ### Verified
19
+ - All 8 audits passed.
20
+ - `npm run release:check`.
21
+ - `npm run release:metadata`.
22
+ - `git diff --check`.
23
+ - Main-branch GitHub Actions (`Design-AI audit`, `Deploy doc site`) passed for the constituent commits.
24
+
25
+ ### Versions
26
+ - `package.json` + `.claude-plugin/plugin.json`: 4.58.0 → 4.59.0.
27
+ - `vscode-extension/package.json`: remains 0.4.1.
28
+
29
+ ### What this enables
30
+ - Agent runtimes, build scripts, and custom tools can consume design-ai's routing, prompt/pack generation, ranked search, recall, and artifact checks as plain functions — deterministic, in-process, zero-dependency — with a semver-stable contract independent of the internal `cli/lib` shapes.
31
+
32
+ ## v4.58.0 — Retrieval Across Surfaces and Corpus Depth (2026-07)
33
+
34
+ Completes the retrieval integration started in v4.57.0 by bringing it to the last untouched surface (`route --explain`), refines recall quality so injected knowledge is real design guidance rather than index files, and closes the remaining dogfood-named corpus gaps. All additions are additive and backward-compatible; default output is unchanged unless a flag is passed.
35
+
36
+ ### Added
37
+ - `route --explain` now includes an advisory "Related knowledge" section — brief-relevant corpus knowledge recalled by the shared lexical scorer, excluding what the route already curates. This is advisory only: route ids, scores, and curated knowledge are unchanged, and default `route` output is byte-unchanged. `design_ai_route`'s existing `explain` parameter carries it to MCP automatically. Retrieval now spans every surface: `search --ranked`, `prompt`/`pack --with-recall`, `learn --recall`, and `route --explain`.
38
+ - `knowledge/patterns/async-control.md` — the async action in-flight lifecycle: pending states, double-submit prevention, duration-matched loading affordances, optimistic updates with rollback, debounce/throttle, cancellation and out-of-order handling, concurrency policy, timeouts, and the accessibility floor.
39
+ - `knowledge/i18n/korean-density-conventions.md` — Korean B2B/enterprise density: the density ladder, table-first layouts, label-left multi-column forms, dense navigation, typography at density, and where density must yield to accessibility.
40
+
41
+ ### Changed
42
+ - Recall injection surfaces (`prompt`/`pack --with-recall`, `learn --recall`, `route --explain`) now exclude generated index/meta files (`COVERAGE.md`, `INDEX.md`, `docs/reference/*`) so injected context is real design knowledge; raw `search --ranked` still returns those files when explicitly searched.
43
+ - Knowledge corpus is now 94 files (from 92).
44
+
45
+ ### Fixed
46
+ - `tools/audit/registry-smoke.py`'s learn-relevance assertion expected a stale token order (`keyboard, accessibility`) after the v4.57 shared-scorer change sorted matched tokens; aligned it with `package-smoke.py` and the real CLI output so the live post-publish registry smoke matches the published package.
47
+
48
+ ### Verified
49
+ - All 8 audits passed.
50
+ - `npm run release:check`.
51
+ - `npm run release:metadata`.
52
+ - `git diff --check`.
53
+ - Main-branch GitHub Actions (`Design-AI audit`, `Deploy doc site`) passed for the constituent commits.
54
+
55
+ ### Versions
56
+ - `package.json` + `.claude-plugin/plugin.json`: 4.57.0 → 4.58.0.
57
+ - `vscode-extension/package.json`: remains 0.4.1.
58
+
59
+ ### What this enables
60
+ - Agents routing a brief now see additional relevant knowledge beyond the route's curated set, and every retrieval surface injects real design guidance rather than index tables.
61
+ - The Korean-market corpus gains async-control and B2B density coverage that dogfood runs specifically named as missing.
62
+ - Publishing v4.58.0 realigns `main` with the published package so the live registry smoke verifies cleanly again.
63
+
5
64
  ## v4.57.0 — Local Retrieval Memory: Ranked Search and Optional Embeddings (2026-07)
6
65
 
7
66
  Ships the Phase 754 AI-learning retrieval work: a deterministic, zero-dependency local retrieval layer over the knowledge corpus and the local learning profile, plus an opt-in local embedding rerank backend. Defaults are unchanged — `search`, `route`, `prompt`, and `pack` behave exactly as before unless a new flag is passed — so this release is additive and backward-compatible. It also moves `refs/` source links behind generated reference pages and hardens the docs deploy against intermittent GitHub Pages failures.
package/README.ko.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [![Audit](https://img.shields.io/badge/audit-passing-brightgreen)](https://github.com/sungjin9288/design-ai/actions/workflows/audit.yml)
4
4
  [![Docs](https://img.shields.io/badge/docs-live-indigo)](https://sungjin9288.github.io/design-ai/ko/)
5
- [![Knowledge files](https://img.shields.io/badge/knowledge-92-blue)](knowledge/PRINCIPLES.md)
5
+ [![Knowledge files](https://img.shields.io/badge/knowledge-94-blue)](knowledge/PRINCIPLES.md)
6
6
  [![Examples](https://img.shields.io/badge/examples-221-blue)](examples/README.md)
7
7
  [![Skills](https://img.shields.io/badge/skills-20-blue)](skills/README.md)
8
8
 
@@ -12,7 +12,7 @@
12
12
 
13
13
  > **모델이 아니에요. 파인튜닝도 아니에요.** 디자인 전문 지식을 구조화한 코퍼스 + 에이전트가 바로 실행할 수 있는 지시문이에요. 범용 LLM을 이번 세션에서만큼은 시니어 디자이너로 바꿔주는 셈이에요.
14
14
 
15
- > **배포 상태, 2026-07-02 확인:** 로컬 `npm run release:check`는 통과했고, GitHub Pages 문서는 live 상태이며, GitHub Release `v4.56.0`과 npm `@design-ai/cli@4.56.0` publish registry smoke 확인됐어요. Homebrew formula는 `v4.56.0`에 pinning되어 있고, VS Code Marketplace에는 `sungjin.design-ai-vscode@0.4.1`이 공개되어 있어요. 자세한 내용은 [`docs/external-status.md`](docs/external-status.md)를 확인하세요.
15
+ > **배포 상태, 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)를 확인하세요.
16
16
 
17
17
  ## 한눈에 보는 커버리지
18
18
 
@@ -88,6 +88,7 @@ CLI 명령어: `design-ai install [--json]`, `update [--dry-run] [--json]`, `uni
88
88
  | **Cursor** | 이 폴더를 열고 `AGENTS.md`를 `.cursorrules`로 심볼릭 링크하거나 복사해요. [워크스루](docs/integrations/cursor-walkthrough.md). |
89
89
  | **Aider** | `AGENTS.md`를 시스템 프롬프트로 전달해요. [워크스루](docs/integrations/aider-walkthrough.md). |
90
90
  | **Anthropic / OpenAI SDK** | 관련 스킬 `PLAYBOOK.md` 파일을 프롬프트에 포함시켜요. [워크스루](docs/integrations/sdk-walkthrough.md). |
91
+ | **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). |
91
92
  | **일반 프롬프트** | 어떤 `skills/*/PLAYBOOK.md` 본문이든 붙여넣으세요 — 각각 자기 완결적이에요. |
92
93
 
93
94
  에이전트별 설치 방법은 [`docs/USING.md`](docs/USING.md)를, 구체적인 사용 예시는 위 워크스루 링크를 참고하세요.
@@ -107,7 +108,7 @@ design-ai/
107
108
 
108
109
  ├── refs/ # Sparse-clone된 업스트림 소스 (gitignored)
109
110
 
110
- ├── knowledge/ # 92개 손으로 쓴 + 추출된 지식 파일
111
+ ├── knowledge/ # 94개 손으로 쓴 + 추출된 지식 파일
111
112
  │ ├── design-tokens/ # W3C DTCG, OKLCH, HCT
112
113
  │ ├── components/ # Ant + MUI + shadcn 합성
113
114
  │ ├── patterns/ # 인증, 가격, 랜딩 히어로, 브랜드, 이메일 등
@@ -171,7 +172,7 @@ design-ai는 한국 시장을 1순위로 만들어졌고, 글로벌 시장 패
171
172
 
172
173
  ## 상태
173
174
 
174
- 전체 단계 로그는 [`docs/ROADMAP.md`](docs/ROADMAP.md), 현재 완료 범위는 [`docs/PRODUCT-READINESS.md`](docs/PRODUCT-READINESS.md)에서 확인하세요. 현재 **v4.56.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가 완료됐어요.
175
+ 전체 단계 로그는 [`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가 완료됐어요.
175
176
 
176
177
  핵심 디자인 컨설팅 워크플로우는 로컬 릴리스 기준으로 준비되어 있어요. 웹사이트 개선 컨트롤 타워는 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은 여전히 현재 배포 범위 밖이에요.
177
178
 
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [![Audit](https://img.shields.io/badge/audit-passing-brightgreen)](https://github.com/sungjin9288/design-ai/actions/workflows/audit.yml)
4
4
  [![Docs](https://img.shields.io/badge/docs-live-indigo)](https://sungjin9288.github.io/design-ai/)
5
- [![Knowledge files](https://img.shields.io/badge/knowledge-92-blue)](knowledge/PRINCIPLES.md)
5
+ [![Knowledge files](https://img.shields.io/badge/knowledge-94-blue)](knowledge/PRINCIPLES.md)
6
6
  [![Examples](https://img.shields.io/badge/examples-221-blue)](examples/README.md)
7
7
  [![Skills](https://img.shields.io/badge/skills-20-blue)](skills/README.md)
8
8
 
@@ -12,7 +12,7 @@ A model-agnostic design knowledge base + skill system. Drop it in front of any A
12
12
 
13
13
  > **Not a model. Not a fine-tune.** A structured corpus of design expertise + agent-ready instructions that turn a general-purpose LLM into an expert.
14
14
 
15
- > **Distribution status, checked 2026-07-02:** local `npm run release:check` passes, GitHub Pages docs are live, GitHub Release `v4.56.0` is published, `@design-ai/cli@4.56.0` is public on npm with registry smoke coverage, the Homebrew formula is pinned to `v4.56.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).
15
+ > **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).
16
16
 
17
17
  ## Coverage at a glance
18
18
 
@@ -89,6 +89,7 @@ See [`docs/DISTRIBUTION.md`](docs/DISTRIBUTION.md) for the full distribution gui
89
89
  | **Aider** | Pass `AGENTS.md` as system prompt. [Walkthrough](docs/integrations/aider-walkthrough.md). |
90
90
  | **Anthropic / OpenAI SDK** | Embed relevant skill `PLAYBOOK.md` files in your prompt. [Walkthrough](docs/integrations/sdk-walkthrough.md). |
91
91
  | **VS Code** | Install the public Marketplace extension for sidebar trees + quick-pick commands. [Walkthrough](docs/integrations/vscode-walkthrough.md). |
92
+ | **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). |
92
93
  | **Plain prompt** | Paste any `skills/*/PLAYBOOK.md` body — each is self-contained. |
93
94
 
94
95
  See [`docs/USING.md`](docs/USING.md) for per-agent setup details, or the linked walkthroughs for concrete example sessions.
@@ -107,7 +108,7 @@ design-ai/
107
108
 
108
109
  ├── refs/ # Sparse-cloned upstream sources (gitignored)
109
110
 
110
- ├── knowledge/ # 92 hand-written + extracted knowledge files
111
+ ├── knowledge/ # 94 hand-written + extracted knowledge files
111
112
  │ ├── design-tokens/ # Token systems (W3C DTCG, OKLCH, HCT)
112
113
  │ ├── components/ # Component synthesis (Ant + MUI + shadcn)
113
114
  │ ├── patterns/ # Auth, pricing, landing hero, brand, email, ...
@@ -213,7 +214,7 @@ Refresh refs/ on demand: `./tools/extractors/run-all.sh`.
213
214
 
214
215
  ## Status
215
216
 
216
- 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.56.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.
217
+ 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.
217
218
 
218
219
  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.
219
220
 
@@ -28,7 +28,7 @@ function printHelp() {
28
28
  console.log(" --stdin Read the task brief, or route eval JSON with --eval, from standard input");
29
29
  console.log(" --list List route ids without scoring a brief");
30
30
  console.log(" --limit N Maximum route recommendations to return, 1-10. Default: 3");
31
- console.log(" --explain Include route scoring and reference coverage details");
31
+ console.log(" --explain Include route scoring, reference coverage, and related knowledge");
32
32
  console.log(" --eval-template Generate a runnable route eval checkpoint JSON template");
33
33
  console.log(" --eval Run deterministic route-selection checkpoint cases");
34
34
  console.log(" --strict With --eval, exit non-zero on warning or failure");
@@ -78,6 +78,17 @@ function printRoute(route, index, explain = false) {
78
78
  for (const knowledge of route.knowledge) {
79
79
  console.log(` read: ${formatPath(knowledge)}`);
80
80
  }
81
+
82
+ if (explain) {
83
+ const related = route.relatedKnowledge || [];
84
+ if (related.length === 0) {
85
+ console.log(" related: (none)");
86
+ } else {
87
+ for (const item of related) {
88
+ console.log(` related: ${item.id} ${dim(`(score ${item.score.toFixed(2)})`)}`);
89
+ }
90
+ }
91
+ }
81
92
  }
82
93
 
83
94
  function printCatalogRoute(route, index) {
@@ -211,6 +222,7 @@ export async function runRoute(args) {
211
222
  brief,
212
223
  sourceRoot: DESIGN_AI_HOME,
213
224
  limit: parsed.limit,
225
+ explain: parsed.explain,
214
226
  });
215
227
 
216
228
  const payload = {
@@ -109,7 +109,7 @@ export function buildRecallContext({
109
109
  const candidateCount = collectCorpusDocuments({ designAiPath, dirs }).length;
110
110
 
111
111
  const hits = query
112
- ? rankedSearchCorpus({ query, designAiPath, dirs, limit: recallLimit }).hits
112
+ ? rankedSearchCorpus({ query, designAiPath, dirs, limit: recallLimit, excludeGeneratedIndex: true }).hits
113
113
  : [];
114
114
 
115
115
  const selected = hits.map((hit) => ({
@@ -150,7 +150,7 @@ export function buildLearnRecall({
150
150
 
151
151
  const corpusCandidateCount = collectCorpusDocuments({ designAiPath, dirs }).length;
152
152
  const corpusHits = normalizedQuery
153
- ? rankedSearchCorpus({ query: normalizedQuery, designAiPath, dirs, limit }).hits
153
+ ? rankedSearchCorpus({ query: normalizedQuery, designAiPath, dirs, limit, excludeGeneratedIndex: true }).hits
154
154
  : [];
155
155
  const corpusSelected = corpusHits.map((hit) => ({
156
156
  id: hit.relPath,
package/cli/lib/route.mjs CHANGED
@@ -7,6 +7,7 @@ import {
7
7
  import path from "node:path";
8
8
 
9
9
  import { parseBriefSourceFlag } from "./brief.mjs";
10
+ import { rankedSearchCorpus } from "./search-ranked.mjs";
10
11
  import { suggestNearest, unknownOptionMessage } from "./suggest.mjs";
11
12
 
12
13
  function exists(p) {
@@ -35,6 +36,17 @@ const ROUTE_OPTIONS = [
35
36
  ];
36
37
  const ROUTE_EVAL_VERSION = 1;
37
38
  const ROUTE_EVAL_DEFAULT_LIMIT = 3;
39
+ // Advisory "Related knowledge" recall (docs/AI-LEARNING-PHASE2.md, "Phase A
40
+ // implementation review" Q3): under `--explain` only, surface the top corpus
41
+ // knowledge/ files recalled by the shipped deterministic lexical scorer that the
42
+ // route's curated `knowledge` list does NOT already point to. Purely additive —
43
+ // it never changes route selection, ids, scores, or the curated list. Restricted
44
+ // to knowledge/ so it surfaces design knowledge, not docs/QUICKSTART. The recall
45
+ // pulls RELATED_KNOWLEDGE_RECALL_LIMIT candidates then keeps the top
46
+ // RELATED_KNOWLEDGE_KEEP after excluding the curated set.
47
+ const RELATED_KNOWLEDGE_DIRS = ["knowledge"];
48
+ const RELATED_KNOWLEDGE_RECALL_LIMIT = 10;
49
+ const RELATED_KNOWLEDGE_KEEP = 3;
38
50
  const CONFIDENCE_ORDER = {
39
51
  low: 1,
40
52
  medium: 2,
@@ -456,6 +468,35 @@ function routeExplanation({ hits, command, skills, agents, knowledge, forced = f
456
468
  };
457
469
  }
458
470
 
471
+ // Advisory related-knowledge recall for a single route. REUSES rankedSearchCorpus
472
+ // (the shipped deterministic lexical scorer — score desc, id asc) scoped to
473
+ // knowledge/, excludes the route's curated knowledge relPaths, and keeps the top
474
+ // RELATED_KNOWLEDGE_KEEP remaining. Returns [] on empty brief or no hits.
475
+ function relatedKnowledgeFor({ brief, sourceRoot, curatedRelPaths }) {
476
+ const query = String(brief || "").trim();
477
+ if (!query) return [];
478
+
479
+ const curated = new Set(curatedRelPaths);
480
+ const { hits } = rankedSearchCorpus({
481
+ query,
482
+ dirs: RELATED_KNOWLEDGE_DIRS,
483
+ limit: RELATED_KNOWLEDGE_RECALL_LIMIT,
484
+ designAiPath: sourceRoot,
485
+ // Recall/injection surface: keep generated index/meta docs (COVERAGE.md,
486
+ // INDEX.md, docs/reference/*) out of the advisory related-knowledge list.
487
+ excludeGeneratedIndex: true,
488
+ });
489
+
490
+ return hits
491
+ .filter((hit) => !curated.has(hit.relPath))
492
+ .slice(0, RELATED_KNOWLEDGE_KEEP)
493
+ .map((hit) => ({
494
+ id: hit.relPath,
495
+ score: hit.score,
496
+ matchedTokens: hit.matchedTokens,
497
+ }));
498
+ }
499
+
459
500
  function routeToResult(route, sourceRoot, hits, options = {}) {
460
501
  const command = route.command
461
502
  ? { path: route.command, exists: exists(path.join(sourceRoot, route.command)) }
@@ -467,6 +508,17 @@ function routeToResult(route, sourceRoot, hits, options = {}) {
467
508
  const fallback = Boolean(options.fallback);
468
509
  const catalog = Boolean(options.catalog);
469
510
 
511
+ // Advisory related-knowledge is attached ONLY when explain is requested, so the
512
+ // default `route` JSON stays byte-unchanged. `knowledge` already merges
513
+ // COMMON_KNOWLEDGE + route.knowledge, so its relPaths are the dedupe set.
514
+ const relatedKnowledge = options.explain
515
+ ? relatedKnowledgeFor({
516
+ brief: options.brief || "",
517
+ sourceRoot,
518
+ curatedRelPaths: knowledge.map((entry) => entry.path),
519
+ })
520
+ : null;
521
+
470
522
  return {
471
523
  id: route.id,
472
524
  label: route.label,
@@ -479,6 +531,7 @@ function routeToResult(route, sourceRoot, hits, options = {}) {
479
531
  knowledge,
480
532
  keywords: route.keywords,
481
533
  explanation: routeExplanation({ hits, command, skills, agents, knowledge, forced, fallback, catalog }),
534
+ ...(relatedKnowledge ? { relatedKnowledge } : {}),
482
535
  ...(forced ? { forced: true } : {}),
483
536
  ...(fallback ? { fallback: true } : {}),
484
537
  };
@@ -501,18 +554,22 @@ export function routeById({ routeId, sourceRoot }) {
501
554
  };
502
555
  }
503
556
 
504
- function fallbackResult(sourceRoot) {
557
+ function fallbackResult(sourceRoot, options = {}) {
505
558
  const route = ROUTES.find((item) => item.id === "design-from-brief");
506
559
  return {
507
- ...routeToResult(route, sourceRoot, [], { fallback: true }),
560
+ ...routeToResult(route, sourceRoot, [], { fallback: true, ...options }),
508
561
  confidence: "low",
509
562
  };
510
563
  }
511
564
 
512
- export function routeBrief({ brief, sourceRoot, limit = 3 }) {
565
+ export function routeBrief({ brief, sourceRoot, limit = 3, explain = false }) {
513
566
  const normalized = brief.trim();
514
567
  if (!normalized) return [];
515
568
 
569
+ // Only compute advisory related-knowledge under --explain; keep the default
570
+ // routing (keyword scoring, ordering, selection) completely unchanged.
571
+ const resultOptions = explain ? { explain: true, brief: normalized } : {};
572
+
516
573
  const scored = ROUTES
517
574
  .map((route) => ({ route, hits: keywordHits(normalized, route.keywords) }))
518
575
  .filter((item) => item.hits.length > 0)
@@ -521,10 +578,10 @@ export function routeBrief({ brief, sourceRoot, limit = 3 }) {
521
578
  return a.route.label.localeCompare(b.route.label);
522
579
  })
523
580
  .slice(0, limit)
524
- .map((item) => routeToResult(item.route, sourceRoot, item.hits));
581
+ .map((item) => routeToResult(item.route, sourceRoot, item.hits, resultOptions));
525
582
 
526
583
  if (scored.length > 0) return scored;
527
- return [fallbackResult(sourceRoot)];
584
+ return [fallbackResult(sourceRoot, resultOptions)];
528
585
  }
529
586
 
530
587
  function isoTimestamp(now = new Date()) {
@@ -24,6 +24,22 @@ import { DEFAULT_SEARCH_DIRS } from "./search.mjs";
24
24
 
25
25
  const RANKED_PREVIEW_LEN = 120;
26
26
 
27
+ // Predicate for GENERATED index/meta docs that must be kept out of the RECALL /
28
+ // context-injection layer (pack/prompt --with-recall, learn --recall corpus side,
29
+ // route --explain related knowledge). These files rank on keyword density but are
30
+ // meta/index tables, not design knowledge worth injecting into an agent's context.
31
+ // Rule: true when the basename is `COVERAGE.md` or `INDEX.md` (generated coverage
32
+ // table / component index), OR the forward-slash-normalized path starts with
33
+ // `docs/reference/` (generated upstream-reference pages). Raw `search --ranked`
34
+ // does NOT apply this — a user explicitly searching may legitimately want an index.
35
+ export function isGeneratedIndexDoc(relPath) {
36
+ const normalized = String(relPath || "").replace(/\\/g, "/");
37
+ const basename = normalized.slice(normalized.lastIndexOf("/") + 1);
38
+ return basename === "COVERAGE.md"
39
+ || basename === "INDEX.md"
40
+ || normalized.startsWith("docs/reference/");
41
+ }
42
+
27
43
  // N = max(limit*5, 25): the number of top lexical candidates handed to the embedding
28
44
  // reranker. Documented constant (docs/AI-LEARNING-PHASE2.md, Phase B CLI wiring).
29
45
  export function embeddingCandidateCount(limit) {
@@ -66,12 +82,28 @@ export function rankedSearchCorpus({
66
82
  dirs = DEFAULT_SEARCH_DIRS,
67
83
  limit = 20,
68
84
  indexDir = defaultIndexDir(),
85
+ // Opt-in RECALL filter: when true, drop generated index/meta docs
86
+ // (isGeneratedIndexDoc) BEFORE applying `limit`, so the limit fills with real
87
+ // knowledge instead of index files. Default false keeps raw `search --ranked`
88
+ // byte-unchanged. Filtering before the limit preserves determinism (score desc,
89
+ // id asc) — the rank pass already orders fully, we only remove excluded ids.
90
+ excludeGeneratedIndex = false,
69
91
  } = {}) {
70
92
  const documents = collectCorpusDocuments({ designAiPath, dirs });
71
93
  const stats = buildLexicalStats(documents.map(({ id, text }) => ({ id, text })));
72
94
  const textById = new Map(documents.map((doc) => [doc.id, doc.text]));
73
95
 
74
- const hits = rankLexical(query, stats, { limit }).map((hit) => ({
96
+ // When excluding, rank the FULL candidate pool first (limit = documents.length)
97
+ // so that filtering out index docs cannot let a real-knowledge hit fall off the
98
+ // pre-limit cutoff; then re-apply `limit`. Ordering is unchanged (score desc, id asc).
99
+ const ranked = rankLexical(query, stats, {
100
+ limit: excludeGeneratedIndex ? documents.length : limit,
101
+ });
102
+ const filtered = excludeGeneratedIndex
103
+ ? ranked.filter((hit) => !isGeneratedIndexDoc(hit.id)).slice(0, limit)
104
+ : ranked;
105
+
106
+ const hits = filtered.map((hit) => ({
75
107
  relPath: hit.id,
76
108
  file: path.join(designAiPath, hit.id),
77
109
  score: hit.score,
@@ -0,0 +1,36 @@
1
+ // SDK adapter: check(artifact, opts). See docs/AGENT-SDK.md.
2
+ //
3
+ // Read-only: capture is never enabled from the SDK in Phase A (the CLI's
4
+ // `--learn` writes a local learning-profile capture; there is no equivalent
5
+ // opt-in here, consistent with "no file writes" for Phase A).
6
+
7
+ import { checkArtifactContent } from "../lib/check.mjs";
8
+ import { assertKnownRouteId } from "../lib/route.mjs";
9
+ import { optionalBoolean, optionalString, requireNonEmptyString, requireOptions } from "./validate.mjs";
10
+
11
+ /**
12
+ * Check a generated design Markdown artifact for grounding, accessibility,
13
+ * responsive, unresolved-marker, and route-specific requirements. Pure,
14
+ * read-only adapter over `checkArtifactContent` from cli/lib/check.mjs.
15
+ *
16
+ * @param {string} artifact - Markdown artifact content to check.
17
+ * @param {{routeId?: string, strict?: boolean}} [opts]
18
+ * @returns {object} CheckReport — the same shape as `design-ai check --json`.
19
+ */
20
+ export function check(artifact, opts = {}) {
21
+ requireNonEmptyString(artifact, "artifact");
22
+ const options = requireOptions(opts, "check");
23
+
24
+ const routeId = optionalString(options.routeId, "routeId");
25
+ if (routeId) assertKnownRouteId(routeId, { allowEmpty: false });
26
+ // `strict` only affects CLI exit-code behavior; validated for type safety,
27
+ // but the SDK returns the report as-is and lets the caller decide what to
28
+ // do with report.status — there is no process exit code to influence here.
29
+ optionalBoolean(options.strict, "strict", false);
30
+
31
+ return checkArtifactContent({
32
+ content: artifact,
33
+ filePath: "sdk",
34
+ routeId,
35
+ });
36
+ }
@@ -0,0 +1,22 @@
1
+ // design-ai Agent SDK — Phase A (read-only). See docs/AGENT-SDK.md and docs/SDK.md.
2
+ //
3
+ // A curated, semver-stable adapter over the same cli/lib functions the CLI and
4
+ // MCP server call. Each verb validates its inputs, resolves the package root
5
+ // the same way the CLI does, and returns a plain JSON-serializable object —
6
+ // the same shape the CLI's --json mode emits. No network calls, no runtime
7
+ // dependencies. Phase A performs no file writes: no learning-usage sidecar
8
+ // writes, even from prompt/pack's withLearning option.
9
+ //
10
+ // Import path: `@design-ai/cli/sdk` (see the "exports" map in package.json).
11
+ // `cli/lib/*` stays internal and unstable; this barrel is the only supported
12
+ // public surface. Do not import `cli/lib/*.mjs` directly from outside this
13
+ // package — only the 8 named exports below are covered by the semver
14
+ // stability contract described in docs/SDK.md.
15
+
16
+ export { check } from "./check-adapter.mjs";
17
+ export { pack } from "./pack-adapter.mjs";
18
+ export { prompt } from "./prompt-adapter.mjs";
19
+ export { recall } from "./recall-adapter.mjs";
20
+ export { route, routes } from "./route-adapter.mjs";
21
+ export { search } from "./search-adapter.mjs";
22
+ export { version } from "./version-adapter.mjs";
@@ -0,0 +1,55 @@
1
+ // SDK adapter: pack(brief, opts). See docs/AGENT-SDK.md.
2
+ //
3
+ // Phase A is read-only: like the prompt adapter, this never records the
4
+ // learning-usage sidecar, even when withLearning is requested.
5
+
6
+ import { DESIGN_AI_HOME, SYMLINK_PREFIX } from "../lib/paths.mjs";
7
+ import { buildPromptPack } from "../lib/pack.mjs";
8
+ import {
9
+ optionalBoolean,
10
+ optionalInteger,
11
+ optionalString,
12
+ requireNonEmptyString,
13
+ requireOptions,
14
+ } from "./validate.mjs";
15
+
16
+ const DEFAULT_MAX_BYTES = 120_000;
17
+
18
+ /**
19
+ * Build a ready-to-use prompt plus bounded context-file bundle from a task
20
+ * brief. Pure, read-only adapter over `buildPromptPack` from cli/lib/pack.mjs.
21
+ * Never records learning-usage, even with withLearning: true.
22
+ *
23
+ * @param {string} brief - Task brief text.
24
+ * @param {{routeId?: string, maxBytes?: number, withLearning?: boolean, learningCategory?: string, learningLimit?: number, withRecall?: boolean, recallLimit?: number}} [opts]
25
+ * @returns {object} Pack — the same shape as `design-ai pack --json` (minus learningUsage).
26
+ */
27
+ export function pack(brief, opts = {}) {
28
+ requireNonEmptyString(brief, "brief");
29
+ const options = requireOptions(opts, "pack");
30
+
31
+ const routeId = optionalString(options.routeId, "routeId");
32
+ const maxBytes = optionalInteger(options.maxBytes, "maxBytes", {
33
+ fallback: DEFAULT_MAX_BYTES,
34
+ min: 1000,
35
+ max: 1_000_000,
36
+ });
37
+ const withLearning = optionalBoolean(options.withLearning, "withLearning", false);
38
+ const learningCategory = optionalString(options.learningCategory, "learningCategory");
39
+ const learningLimit = optionalInteger(options.learningLimit, "learningLimit", { fallback: 0, min: 1, max: 100, zeroMeansUnset: true });
40
+ const withRecall = optionalBoolean(options.withRecall, "withRecall", false);
41
+ const recallLimit = optionalInteger(options.recallLimit, "recallLimit", { fallback: 0, min: 1, max: 20, zeroMeansUnset: true });
42
+
43
+ return buildPromptPack({
44
+ brief,
45
+ sourceRoot: DESIGN_AI_HOME,
46
+ prefix: SYMLINK_PREFIX,
47
+ maxBytes,
48
+ routeId,
49
+ withLearning,
50
+ learningCategory,
51
+ learningLimit,
52
+ withRecall,
53
+ recallLimit,
54
+ });
55
+ }
@@ -0,0 +1,49 @@
1
+ // SDK adapter: prompt(brief, opts). See docs/AGENT-SDK.md.
2
+ //
3
+ // Phase A is read-only: unlike the CLI's `--with-learning` (which records a
4
+ // local usage sidecar entry via recordLearningUsage), the SDK adapter never
5
+ // writes the learning-usage sidecar, even when withLearning is requested. It
6
+ // only reads the local learning profile to build the learning context.
7
+
8
+ import { DESIGN_AI_HOME, SYMLINK_PREFIX } from "../lib/paths.mjs";
9
+ import { buildPromptPlan } from "../lib/prompt.mjs";
10
+ import {
11
+ optionalBoolean,
12
+ optionalInteger,
13
+ optionalString,
14
+ requireNonEmptyString,
15
+ requireOptions,
16
+ } from "./validate.mjs";
17
+
18
+ /**
19
+ * Build a ready-to-use agent prompt plan from a task brief. Pure, read-only
20
+ * adapter over `buildPromptPlan` from cli/lib/prompt.mjs. Never records
21
+ * learning-usage, even with withLearning: true (Phase A is read-only).
22
+ *
23
+ * @param {string} brief - Task brief text.
24
+ * @param {{routeId?: string, withLearning?: boolean, learningCategory?: string, learningLimit?: number, withRecall?: boolean, recallLimit?: number}} [opts]
25
+ * @returns {object|null} PromptPlan — the same shape as `design-ai prompt --json` (minus learningUsage).
26
+ */
27
+ export function prompt(brief, opts = {}) {
28
+ requireNonEmptyString(brief, "brief");
29
+ const options = requireOptions(opts, "prompt");
30
+
31
+ const routeId = optionalString(options.routeId, "routeId");
32
+ const withLearning = optionalBoolean(options.withLearning, "withLearning", false);
33
+ const learningCategory = optionalString(options.learningCategory, "learningCategory");
34
+ const learningLimit = optionalInteger(options.learningLimit, "learningLimit", { fallback: 0, min: 1, max: 100, zeroMeansUnset: true });
35
+ const withRecall = optionalBoolean(options.withRecall, "withRecall", false);
36
+ const recallLimit = optionalInteger(options.recallLimit, "recallLimit", { fallback: 0, min: 1, max: 20, zeroMeansUnset: true });
37
+
38
+ return buildPromptPlan({
39
+ brief,
40
+ sourceRoot: DESIGN_AI_HOME,
41
+ prefix: SYMLINK_PREFIX,
42
+ routeId,
43
+ withLearning,
44
+ learningCategory,
45
+ learningLimit,
46
+ withRecall,
47
+ recallLimit,
48
+ });
49
+ }
@@ -0,0 +1,39 @@
1
+ // SDK adapter: recall(query, opts). See docs/AGENT-SDK.md.
2
+ //
3
+ // Combined recall view: brief-relevant shipped corpus knowledge (`corpus`) plus
4
+ // brief-relevant local learning-profile entries (`learning`), both ranked by the
5
+ // same shipped deterministic lexical scorer used by `search --ranked`. Read-only:
6
+ // reads the local corpus and `DESIGN_AI_LEARNING_FILE` learning profile, exactly
7
+ // as `design-ai learn --recall` does, and never writes either.
8
+
9
+ import { DESIGN_AI_HOME } from "../lib/paths.mjs";
10
+ import { buildLearnRecall, DEFAULT_RECALL_LIMIT } from "../lib/recall.mjs";
11
+ import { optionalInteger, optionalString, requireNonEmptyString, requireOptions } from "./validate.mjs";
12
+
13
+ /**
14
+ * Recall brief-relevant corpus knowledge and local learning-profile entries for
15
+ * a query. Pure, read-only adapter over `buildLearnRecall` from cli/lib/recall.mjs.
16
+ *
17
+ * @param {string} query - Recall query text.
18
+ * @param {{limit?: number, category?: string}} [opts]
19
+ * @returns {{corpus: object, learning: object}} combined recall view.
20
+ */
21
+ export function recall(query, opts = {}) {
22
+ requireNonEmptyString(query, "query");
23
+ const options = requireOptions(opts, "recall");
24
+
25
+ const limit = optionalInteger(options.limit, "limit", { fallback: DEFAULT_RECALL_LIMIT, min: 1, max: 20 });
26
+ const category = optionalString(options.category, "category");
27
+
28
+ const result = buildLearnRecall({
29
+ query,
30
+ limit,
31
+ category,
32
+ designAiPath: DESIGN_AI_HOME,
33
+ });
34
+
35
+ return {
36
+ corpus: result.corpus,
37
+ learning: result.learning,
38
+ };
39
+ }