@design-ai/cli 4.59.0 → 4.60.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.59.0",
3
+ "version": "4.60.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,32 @@
2
2
 
3
3
  User-facing release notes for design-ai. Versions follow semver.
4
4
 
5
+ ## v4.60.0 — Agent SDK types and local-write namespace (2026-07)
6
+
7
+ Extends the Agent SDK (`@design-ai/cli/sdk`) two ways, both additive and backward-compatible. First, hand-written TypeScript declarations now ship with the package, so TypeScript and editors resolve `@design-ai/cli/sdk` with full types — no `@types` install and no build step. Second, an opt-in `learn.*` namespace adds the SDK's first local-write verbs, keeping the eight Phase A verbs read-only and unchanged.
8
+
9
+ ### Added
10
+ - `cli/sdk/index.d.ts` — hand-written type declarations for all eight read-only verbs (precise signatures and return interfaces, including `search` ranked/plain overloads, the `RouteConfidence` union, and optional `learningContext`/`recall`/`relatedKnowledge` fields) plus the `learn` namespace. Wired through an `exports` `types` condition, so `moduleResolution: node16`/`nodenext`/`bundler` consumers get types automatically. A `node --test` guard (`cli/sdk/types.test.mjs`) keeps the declarations in exact sync with the runtime exports; the declarations are verified to compile under `tsc --strict`.
11
+ - `learn` — a frozen namespace grouping the SDK's only writing verbs (Phase B): `learn.remember(text, { category })`, `learn.feedback(text, { outcome, category })`, and `learn.captureFromCheck(artifact, { routeId })`. Each writes only the local learning profile (`DESIGN_AI_LEARNING_FILE`), never the network, mirroring the CLI's `learn remember` / `learn feedback` / `check --learn` write paths. There is no `filePath` or timestamp option — consumers target a profile via the env var, exactly like the CLI.
12
+
13
+ ### Changed
14
+ - The eight Phase A verbs (`route`, `prompt`, `pack`, `search`, `recall`, `check`, `routes`, `version`) remain read-only and unchanged. `check` in particular stays read-only: capture lives in `learn.captureFromCheck`, never as a `check` option. The SDK contract test (`cli/sdk/index.test.mjs`) now pins both the eight read-only function verbs and the frozen `learn` namespace with its three verbs and their return-shape keys.
15
+ - `tools/audit/package-smoke.py` now also exercises `learn.remember` from the installed tarball against a temp `DESIGN_AI_LEARNING_FILE`, asserting the write.
16
+
17
+ ### Verified
18
+ - All 8 audits passed.
19
+ - `npm run release:check`.
20
+ - `npm run release:metadata`.
21
+ - `git diff --check`.
22
+ - Main-branch GitHub Actions (`Design-AI audit`, `Deploy doc site`) passed for the constituent commits.
23
+
24
+ ### Versions
25
+ - `package.json` + `.claude-plugin/plugin.json`: 4.59.0 → 4.60.0.
26
+ - `vscode-extension/package.json`: remains 0.4.1.
27
+
28
+ ### What this enables
29
+ - TypeScript consumers get first-class types for the whole SDK with zero toolchain cost on either side, and agents can now record design preferences, feedback, and check-derived learnings through a single explicit, opt-in write namespace — while every read-only verb keeps its no-write guarantee.
30
+
5
31
  ## v4.59.0 — Agent SDK Phase A (2026-07)
6
32
 
7
33
  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.
package/README.ko.md CHANGED
@@ -12,7 +12,7 @@
12
12
 
13
13
  > **모델이 아니에요. 파인튜닝도 아니에요.** 디자인 전문 지식을 구조화한 코퍼스 + 에이전트가 바로 실행할 수 있는 지시문이에요. 범용 LLM을 이번 세션에서만큼은 시니어 디자이너로 바꿔주는 셈이에요.
14
14
 
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)를 확인하세요.
15
+ > **배포 상태, 2026-07-04 확인:** 로컬 `npm run release:check`는 통과했고, GitHub Pages 문서는 live 상태이며, GitHub Release `v4.59.0`과 npm `@design-ai/cli@4.59.0`(`latest`) publish가 provenance와 함께 확인됐어요. 이번 릴리스는 읽기 전용 Agent SDK 표면(`@design-ai/cli/sdk`, 8개 어댑터 verb)을 추가했고 additive라 CLI 출력은 그대로예요. live `npm run registry:smoke`가 published `@design-ai/cli@4.59.0`에 대해 깨끗이 통과했어요(retrieval 표면과 route enrichment 포함). Homebrew formula는 `v4.59.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
 
@@ -172,7 +172,7 @@ design-ai는 한국 시장을 1순위로 만들어졌고, 글로벌 시장 패
172
172
 
173
173
  ## 상태
174
174
 
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
+ 전체 단계 로그는 [`docs/ROADMAP.md`](docs/ROADMAP.md), 현재 완료 범위는 [`docs/PRODUCT-READINESS.md`](docs/PRODUCT-READINESS.md)에서 확인하세요. 현재 **v4.59.0**: 읽기 전용 Agent SDK(`@design-ai/cli/sdk`), 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가 완료됐어요.
176
176
 
177
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은 여전히 현재 배포 범위 밖이에요.
178
178
 
package/README.md CHANGED
@@ -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-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).
15
+ > **Distribution status, checked 2026-07-04:** local `npm run release:check` passes, GitHub Pages docs are live, GitHub Release `v4.59.0` is published, `@design-ai/cli@4.59.0` is public on npm (`latest`) with provenance, and the live `npm run registry:smoke` passes cleanly against `@design-ai/cli@4.59.0`. The Homebrew formula is pinned to `v4.59.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
 
@@ -214,7 +214,7 @@ Refresh refs/ on demand: `./tools/extractors/run-all.sh`.
214
214
 
215
215
  ## Status
216
216
 
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
+ 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.59.0**: the read-only Agent SDK (`@design-ai/cli/sdk`), 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.
218
218
 
219
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.
220
220
 
@@ -0,0 +1,435 @@
1
+ // Type declarations for the design-ai Agent SDK — Phase A (read-only).
2
+ // Hand-written, zero-build: no TypeScript toolchain is required to produce or
3
+ // ship these. They mirror the runtime shapes exercised by cli/sdk/index.test.mjs
4
+ // (the semver anchor). The eight exported names and their return-shape top-level
5
+ // keys are the stable, semver-covered surface; deeper nested shapes follow the
6
+ // same JSON the CLI's `--json` mode emits.
7
+ //
8
+ // Import: `import { route, search } from "@design-ai/cli/sdk";`
9
+
10
+ // ── Shared building blocks ────────────────────────────────────────────────
11
+
12
+ /** A curated reference (skill/agent/knowledge/command) and whether it exists on disk. */
13
+ export interface RouteReference {
14
+ path: string;
15
+ exists: boolean;
16
+ }
17
+
18
+ /** One reference-coverage tally (available vs total) within a route explanation. */
19
+ export interface CoverageCount {
20
+ available: number;
21
+ total: number;
22
+ }
23
+
24
+ export interface ReferenceCoverage {
25
+ command: CoverageCount;
26
+ skills: CoverageCount;
27
+ agents: CoverageCount;
28
+ knowledge: CoverageCount;
29
+ total: CoverageCount;
30
+ }
31
+
32
+ export interface ScoreBreakdownEntry {
33
+ label: string;
34
+ value: number;
35
+ }
36
+
37
+ export interface RouteExplanation {
38
+ summary: string;
39
+ scoreBreakdown: ScoreBreakdownEntry[];
40
+ referenceCoverage: ReferenceCoverage;
41
+ missingReferences: string[];
42
+ }
43
+
44
+ /** Advisory brief-relevant knowledge recalled by the shared lexical scorer. */
45
+ export interface RelatedKnowledge {
46
+ id: string;
47
+ score: number;
48
+ matchedTokens: string[];
49
+ }
50
+
51
+ export type RouteConfidence = "high" | "medium" | "low" | "catalog" | "forced";
52
+
53
+ export interface RouteResult {
54
+ id: string;
55
+ label: string;
56
+ score: number;
57
+ confidence: RouteConfidence;
58
+ matchedKeywords: string[];
59
+ command: RouteReference | null;
60
+ skills: RouteReference[];
61
+ agents: RouteReference[];
62
+ knowledge: RouteReference[];
63
+ keywords: string[];
64
+ explanation: RouteExplanation;
65
+ /** Present only when `route(brief, { explain: true })` is requested. */
66
+ relatedKnowledge?: RelatedKnowledge[];
67
+ /** Present only when a route was forced via `routeId`. */
68
+ forced?: boolean;
69
+ /** Present only when the fallback route was used. */
70
+ fallback?: boolean;
71
+ }
72
+
73
+ export interface RouteCatalog {
74
+ version: string;
75
+ routes: RouteResult[];
76
+ }
77
+
78
+ // ── prompt / pack ─────────────────────────────────────────────────────────
79
+
80
+ export interface ReferenceExample {
81
+ relPath: string;
82
+ title: string;
83
+ category: string;
84
+ score: number;
85
+ preview: string;
86
+ }
87
+
88
+ /** A single recalled learning-profile entry. */
89
+ export interface LearningRecallEntry {
90
+ id: string;
91
+ category: string;
92
+ score: number;
93
+ matchedTokens: string[];
94
+ text: string;
95
+ }
96
+
97
+ /** A single recalled shipped-corpus knowledge entry. */
98
+ export interface CorpusRecallEntry {
99
+ id: string;
100
+ score: number;
101
+ matchedTokens: string[];
102
+ }
103
+
104
+ /** Recall block embedded in a prompt plan when `withRecall: true`. */
105
+ export interface PromptRecall {
106
+ query: string;
107
+ mode: string;
108
+ candidateCount: number;
109
+ selectedCount: number;
110
+ selected: CorpusRecallEntry[];
111
+ markdown: string;
112
+ }
113
+
114
+ /** A stored learning-profile entry, as surfaced in a prompt plan's learning context. */
115
+ export interface LearningEntry {
116
+ id: string;
117
+ category: string;
118
+ text: string;
119
+ source: string;
120
+ createdAt: string;
121
+ }
122
+
123
+ export interface LearningSelection {
124
+ mode: string;
125
+ query: string;
126
+ candidateCount: number;
127
+ matchedCount: number;
128
+ queryTokenCount: number;
129
+ fallbackEnabled: boolean;
130
+ selectedCount: number;
131
+ fallbackCount: number;
132
+ selected: LearningRecallEntry[];
133
+ }
134
+
135
+ export interface LearningAuditSummary {
136
+ status: string;
137
+ failures: number;
138
+ warnings: number;
139
+ }
140
+
141
+ /** Learning context embedded in a prompt plan when `withLearning: true`. */
142
+ export interface LearningContext {
143
+ file: string;
144
+ category: string;
145
+ limit: number;
146
+ query: string;
147
+ selection: LearningSelection;
148
+ entries: LearningEntry[];
149
+ empty: boolean;
150
+ auditSummary: LearningAuditSummary;
151
+ markdown: string;
152
+ }
153
+
154
+ export interface PromptPlan {
155
+ brief: string;
156
+ version: string;
157
+ route: RouteResult;
158
+ slashCommand: string;
159
+ referenceExamples: ReferenceExample[];
160
+ filesToRead: string[];
161
+ checklist: string[];
162
+ qualityCommand: string;
163
+ prompt: string;
164
+ /** Present only when `prompt(brief, { withLearning: true })` is requested. */
165
+ learningContext?: LearningContext;
166
+ /** Present only when `prompt(brief, { withRecall: true })` is requested. */
167
+ recall?: PromptRecall;
168
+ }
169
+
170
+ export interface PackSummary {
171
+ totalFiles: number;
172
+ includedFiles: number;
173
+ truncatedFiles: number;
174
+ missingFiles: number;
175
+ usedBytes: number;
176
+ maxBytes: number;
177
+ remainingBytes: number;
178
+ usedRatio: number;
179
+ status: string;
180
+ }
181
+
182
+ export interface PackFile {
183
+ path: string;
184
+ bytes: number;
185
+ includedBytes: number;
186
+ included: boolean;
187
+ truncated: boolean;
188
+ content: string;
189
+ }
190
+
191
+ export interface Pack {
192
+ brief: string;
193
+ version: string;
194
+ maxBytes: number;
195
+ usedBytes: number;
196
+ summary: PackSummary;
197
+ warnings: string[];
198
+ plan: PromptPlan;
199
+ files: PackFile[];
200
+ markdown: string;
201
+ }
202
+
203
+ // ── search ────────────────────────────────────────────────────────────────
204
+
205
+ /** A plain line hit (default `search`). */
206
+ export interface SearchHit {
207
+ file: string;
208
+ lineNumber: number;
209
+ relPath: string;
210
+ preview: string;
211
+ }
212
+
213
+ /** A ranked hit (`search(query, { ranked: true })`) with a BM25 score. */
214
+ export interface RankedSearchHit {
215
+ relPath: string;
216
+ file: string;
217
+ score: number;
218
+ matchedTokens: string[];
219
+ preview: string;
220
+ }
221
+
222
+ // ── recall ────────────────────────────────────────────────────────────────
223
+
224
+ export interface RecallCorpus {
225
+ candidateCount: number;
226
+ selectedCount: number;
227
+ selected: CorpusRecallEntry[];
228
+ }
229
+
230
+ export interface RecallLearning {
231
+ mode: string;
232
+ candidateCount: number;
233
+ selectedCount: number;
234
+ selected: LearningRecallEntry[];
235
+ }
236
+
237
+ export interface RecallResult {
238
+ corpus: RecallCorpus;
239
+ learning: RecallLearning;
240
+ }
241
+
242
+ // ── check ─────────────────────────────────────────────────────────────────
243
+
244
+ export type CheckLevel = "pass" | "warn" | "fail";
245
+
246
+ export interface CheckResult {
247
+ id: string;
248
+ title: string;
249
+ level: CheckLevel;
250
+ passed: boolean;
251
+ message: string;
252
+ /** Present only when the check has supporting evidence. */
253
+ evidence?: string;
254
+ }
255
+
256
+ export interface CheckReport {
257
+ filePath: string;
258
+ /** Present only when a `routeId` was supplied. */
259
+ routeId?: string;
260
+ status: CheckLevel;
261
+ passes: number;
262
+ warnings: number;
263
+ failures: number;
264
+ total: number;
265
+ /** Human-readable ratio, e.g. "6/8". */
266
+ score: string;
267
+ results: CheckResult[];
268
+ }
269
+
270
+ export interface VersionReport {
271
+ cli: string;
272
+ corpus: string;
273
+ }
274
+
275
+ // ── Option objects ────────────────────────────────────────────────────────
276
+
277
+ export interface RouteOptions {
278
+ /** Max routes to return. Default 3, range 1–10. */
279
+ limit?: number;
280
+ /** Include the advisory explanation + related-knowledge section. Default false. */
281
+ explain?: boolean;
282
+ }
283
+
284
+ export interface PromptOptions {
285
+ /** Force a specific route id instead of scoring the brief. */
286
+ routeId?: string;
287
+ /** Include local learning-profile guidance (read-only; never recorded). Default false. */
288
+ withLearning?: boolean;
289
+ /** Restrict learning guidance to a category. */
290
+ learningCategory?: string;
291
+ /** Max learning entries. Range 1–100. */
292
+ learningLimit?: number;
293
+ /** Include a brief-relevant corpus recall block. Default false. */
294
+ withRecall?: boolean;
295
+ /** Max recall entries. Range 1–20. */
296
+ recallLimit?: number;
297
+ }
298
+
299
+ export interface PackOptions extends PromptOptions {
300
+ /** Byte budget for the bundled context files. Default 120000, range 1000–1000000. */
301
+ maxBytes?: number;
302
+ }
303
+
304
+ export interface SearchOptions {
305
+ /** Restrict to one corpus directory (must be a known search dir). */
306
+ dir?: string;
307
+ /** Max hits. Default 20, range 1–500. */
308
+ limit?: number;
309
+ /** Return ranked BM25 hits (score + matchedTokens) instead of plain line hits. */
310
+ ranked?: boolean;
311
+ }
312
+
313
+ export interface RecallOptions {
314
+ /** Max entries per side (corpus/learning). Range 1–20. */
315
+ limit?: number;
316
+ /** Restrict learning recall to a category. */
317
+ category?: string;
318
+ }
319
+
320
+ export interface CheckOptions {
321
+ /** Apply route-specific requirements for this route id. */
322
+ routeId?: string;
323
+ /** Validated for parity with the CLI; has no effect in-process (no exit code). */
324
+ strict?: boolean;
325
+ }
326
+
327
+ // ── The eight Phase A verbs ─────────────────────────────────────────────────
328
+
329
+ /** Recommend the best route(s), commands, skills, and knowledge for a brief. */
330
+ export function route(brief: string, opts?: RouteOptions): RouteResult[];
331
+
332
+ /** List the full route catalog independent of any brief. */
333
+ export function routes(): RouteCatalog;
334
+
335
+ /** Build a ready-to-use agent prompt plan from a brief. */
336
+ export function prompt(brief: string, opts?: PromptOptions): PromptPlan;
337
+
338
+ /** Build a prompt plan plus a bounded context-file bundle from a brief. */
339
+ export function pack(brief: string, opts?: PackOptions): Pack;
340
+
341
+ /** Search the local corpus. Returns ranked hits when `ranked: true`, else plain line hits. */
342
+ export function search(query: string, opts: SearchOptions & { ranked: true }): RankedSearchHit[];
343
+ export function search(query: string, opts?: SearchOptions): SearchHit[];
344
+
345
+ /** Recall brief-relevant corpus knowledge and local learning-profile entries. */
346
+ export function recall(query: string, opts?: RecallOptions): RecallResult;
347
+
348
+ /** Check a Markdown artifact's content for grounding, accessibility, and route requirements. */
349
+ export function check(artifact: string, opts?: CheckOptions): CheckReport;
350
+
351
+ /** Report the CLI package version and the plugin/corpus version. */
352
+ export function version(): VersionReport;
353
+
354
+ // ── learn.* (Phase B — local writes) ─────────────────────────────────────
355
+ //
356
+ // The ONLY writing verbs in the SDK. Each writes exclusively to the local
357
+ // learning profile (`DESIGN_AI_LEARNING_FILE` / defaultLearningFile()), never
358
+ // the network. There is no `filePath` or `now`/timestamp option on any of
359
+ // these — target a specific profile via the `DESIGN_AI_LEARNING_FILE` env
360
+ // var, exactly like the CLI. See docs/SDK.md "Phase B — local writes".
361
+
362
+ export interface LearnRememberOptions {
363
+ /** Learning-profile category. Default "preference". */
364
+ category?: string;
365
+ }
366
+
367
+ export interface LearnFeedbackOptions {
368
+ /** Feedback outcome; normalized by the underlying lib (e.g. "keep" | "avoid" | "improve"). Default "improve". */
369
+ outcome?: string;
370
+ /** Learning-profile category. Default "workflow". */
371
+ category?: string;
372
+ }
373
+
374
+ export interface LearnCaptureOptions {
375
+ /** Add route-specific check requirements for this route id before capturing. */
376
+ routeId?: string;
377
+ }
378
+
379
+ /** A single stored learning-profile entry, as written by learn.remember/feedback/captureFromCheck. */
380
+ export interface LearningProfileEntry {
381
+ id: string;
382
+ category: string;
383
+ text: string;
384
+ source: string;
385
+ createdAt: string;
386
+ }
387
+
388
+ export interface LearningProfile {
389
+ version: number;
390
+ updatedAt: string;
391
+ entries: LearningProfileEntry[];
392
+ }
393
+
394
+ /** Return shape of learn.remember() and learn.feedback(). */
395
+ export interface RememberResult {
396
+ file: string;
397
+ entry: LearningProfileEntry;
398
+ profile: LearningProfile;
399
+ }
400
+
401
+ /** An entry skipped by learn.captureFromCheck() because it duplicates an existing profile entry. */
402
+ export interface CaptureSkippedEntry {
403
+ category: string;
404
+ text: string;
405
+ source: string;
406
+ reason: string;
407
+ }
408
+
409
+ /** Return shape of learn.captureFromCheck() — the `captureLearningEntries` result. */
410
+ export interface CaptureResult {
411
+ file: string;
412
+ dryRun: boolean;
413
+ applied: boolean;
414
+ source: string;
415
+ candidateCount: number;
416
+ addedCount: number;
417
+ skippedCount: number;
418
+ count: number;
419
+ entries: LearningProfileEntry[];
420
+ skipped: CaptureSkippedEntry[];
421
+ }
422
+
423
+ /**
424
+ * Phase B: the explicit, opt-in LOCAL-WRITE namespace. `learn.remember`,
425
+ * `learn.feedback`, and `learn.captureFromCheck` are the only SDK verbs that
426
+ * write files — each writes only the local learning profile.
427
+ */
428
+ export declare const learn: {
429
+ /** Record a local learning-profile preference. */
430
+ remember(text: string, opts?: LearnRememberOptions): RememberResult;
431
+ /** Record feedback (keep/avoid/improve) as a local learning-profile entry. */
432
+ feedback(text: string, opts?: LearnFeedbackOptions): RememberResult;
433
+ /** Check a Markdown artifact and capture its non-pass results as local learning-profile entries. */
434
+ captureFromCheck(artifact: string, opts?: LearnCaptureOptions): CaptureResult;
435
+ };
package/cli/sdk/index.mjs CHANGED
@@ -1,19 +1,27 @@
1
- // design-ai Agent SDK — Phase A (read-only). See docs/AGENT-SDK.md and docs/SDK.md.
1
+ // design-ai Agent SDK — Phase A (read-only) + Phase B (local writes). See
2
+ // docs/AGENT-SDK.md and docs/SDK.md.
2
3
  //
3
4
  // A curated, semver-stable adapter over the same cli/lib functions the CLI and
4
5
  // MCP server call. Each verb validates its inputs, resolves the package root
5
6
  // the same way the CLI does, and returns a plain JSON-serializable object —
6
7
  // 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.
8
+ // dependencies. The 8 Phase A verbs below perform no file writes: no
9
+ // learning-usage sidecar writes, even from prompt/pack's withLearning option.
10
+ //
11
+ // Phase B adds a single `learn` namespace grouping the three explicit,
12
+ // opt-in LOCAL-WRITE verbs (`learn.remember`, `learn.feedback`,
13
+ // `learn.captureFromCheck`). This keeps the write boundary explicit: the 8
14
+ // Phase A verbs stay read-only and unchanged; `learn` is the only place the
15
+ // SDK writes files, and it only ever writes the local learning profile.
9
16
  //
10
17
  // Import path: `@design-ai/cli/sdk` (see the "exports" map in package.json).
11
18
  // `cli/lib/*` stays internal and unstable; this barrel is the only supported
12
19
  // 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.
20
+ // package — only the 8 named function exports plus the `learn` namespace
21
+ // below are covered by the semver stability contract described in docs/SDK.md.
15
22
 
16
23
  export { check } from "./check-adapter.mjs";
24
+ export { learn } from "./learn-adapter.mjs";
17
25
  export { pack } from "./pack-adapter.mjs";
18
26
  export { prompt } from "./prompt-adapter.mjs";
19
27
  export { recall } from "./recall-adapter.mjs";
@@ -0,0 +1,100 @@
1
+ // SDK namespace: learn.*. See docs/AGENT-SDK.md (Phase B) and docs/SDK.md.
2
+ //
3
+ // The ONLY writing verbs in the SDK. Phase A (route, prompt, pack, search,
4
+ // recall, check, routes, version) stays read-only and unchanged — this
5
+ // namespace is the explicit, opt-in write boundary: every verb here writes
6
+ // only the local learning profile (`DESIGN_AI_LEARNING_FILE` /
7
+ // `defaultLearningFile()`), never the network. Consumers target a specific
8
+ // profile file via the `DESIGN_AI_LEARNING_FILE` env var, exactly like the
9
+ // CLI — there is no `filePath` option and no `now`/timestamp option; the
10
+ // underlying cli/lib functions use their own defaults (the env var and
11
+ // `new Date()`).
12
+
13
+ import { checkArtifactContent, buildCheckLearningCapture } from "../lib/check.mjs";
14
+ import { rememberLearning, recordLearningFeedback } from "../lib/learn-profile.mjs";
15
+ import { assertKnownRouteId } from "../lib/route.mjs";
16
+ import { optionalString, requireNonEmptyString, requireOptions } from "./validate.mjs";
17
+
18
+ /**
19
+ * Record a local learning-profile preference. Writes only the local learning
20
+ * profile (`DESIGN_AI_LEARNING_FILE` / `defaultLearningFile()`); never the
21
+ * network. Pure adapter over `rememberLearning` from cli/lib/learn-profile.mjs.
22
+ *
23
+ * @param {string} text - Preference text to remember.
24
+ * @param {{category?: string}} [opts]
25
+ * @returns {{file: string, entry: object, profile: object}} RememberResult.
26
+ */
27
+ function remember(text, opts = {}) {
28
+ requireNonEmptyString(text, "text");
29
+ const options = requireOptions(opts, "learn.remember");
30
+ const category = optionalString(options.category, "category", "preference");
31
+
32
+ return rememberLearning({
33
+ text,
34
+ category,
35
+ source: "sdk",
36
+ });
37
+ }
38
+
39
+ /**
40
+ * Record feedback (keep/avoid/improve) as a local learning-profile entry.
41
+ * Writes only the local learning profile; never the network. Pure adapter
42
+ * over `recordLearningFeedback` from cli/lib/learn-profile.mjs.
43
+ *
44
+ * @param {string} text - Feedback text.
45
+ * @param {{outcome?: string, category?: string}} [opts]
46
+ * @returns {{file: string, entry: object, profile: object}} RememberResult.
47
+ */
48
+ function feedback(text, opts = {}) {
49
+ requireNonEmptyString(text, "text");
50
+ const options = requireOptions(opts, "learn.feedback");
51
+ // The lib normalizes the outcome itself (normalizeFeedbackOutcome); the SDK
52
+ // only validates that, if given, it is a string.
53
+ const outcome = optionalString(options.outcome, "outcome", "improve");
54
+ const category = optionalString(options.category, "category", "workflow");
55
+
56
+ return recordLearningFeedback({
57
+ text,
58
+ outcome,
59
+ category,
60
+ });
61
+ }
62
+
63
+ /**
64
+ * Check a Markdown artifact, then capture its non-pass results as local
65
+ * learning-profile entries. Writes only the local learning profile; never the
66
+ * network. Pure adapter over `checkArtifactContent` + `buildCheckLearningCapture`
67
+ * from cli/lib/check.mjs (the same path as the CLI's `check --learn --yes`).
68
+ *
69
+ * @param {string} artifact - Markdown artifact content to check and capture.
70
+ * @param {{routeId?: string}} [opts]
71
+ * @returns {object} CaptureResult — the `captureLearningEntries` return shape:
72
+ * { file, dryRun, applied, source, candidateCount, addedCount, skippedCount,
73
+ * count, entries, skipped }.
74
+ */
75
+ function captureFromCheck(artifact, opts = {}) {
76
+ requireNonEmptyString(artifact, "artifact");
77
+ const options = requireOptions(opts, "learn.captureFromCheck");
78
+
79
+ const routeId = optionalString(options.routeId, "routeId");
80
+ if (routeId) assertKnownRouteId(routeId, { allowEmpty: false });
81
+
82
+ const report = checkArtifactContent({
83
+ content: artifact,
84
+ filePath: "sdk",
85
+ routeId,
86
+ });
87
+
88
+ return buildCheckLearningCapture(report, { dryRun: false });
89
+ }
90
+
91
+ /**
92
+ * The Phase B local-write namespace. `learn.remember`, `learn.feedback`, and
93
+ * `learn.captureFromCheck` are the only SDK verbs that write files; all three
94
+ * write exclusively to the local learning profile. See docs/SDK.md.
95
+ */
96
+ export const learn = Object.freeze({
97
+ remember,
98
+ feedback,
99
+ captureFromCheck,
100
+ });
package/docs/AGENT-SDK.md CHANGED
@@ -75,9 +75,9 @@ Verification gates:
75
75
  - `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.
76
76
  - `npm run release:metadata` unchanged (README stance preserved).
77
77
 
78
- ### Phase B — optional, explicit local writes
78
+ ### Phase B — explicit local writes (shipped)
79
79
 
80
- 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.
80
+ Implemented as a single `learn` namespace export grouping three opt-in adapters that mirror the CLI's local-write commands: `learn.remember`, `learn.feedback`, and `learn.captureFromCheck` (capture from a `check()` report). Each writes only the local learning profile (`DESIGN_AI_LEARNING_FILE` / `defaultLearningFile()`), never the network no `filePath` or `now`/timestamp option; consumers target a profile via the env var, exactly like the CLI. The 8 Phase A verbs are unchanged and stay read-only; `check()` in particular gets no capture option — capture is reached only through the separate, explicitly-named `learn.captureFromCheck`, keeping the write boundary visible at the call site. See [`SDK.md`](SDK.md#phase-b--local-writes) for the full reference.
81
81
 
82
82
  ## Integration points
83
83
 
package/docs/ROADMAP.md CHANGED
@@ -1,5 +1,63 @@
1
1
  # Roadmap
2
2
 
3
+ ## Phase 761 — Agent SDK types + Phase B Release (v4.60.0) ✓ ready
4
+
5
+ Ships the Agent SDK TypeScript declarations (Phase 759) and the `learn.*` local-write namespace (Phase 760) together as one npm version. Both are additive and backward-compatible: the eight Phase A verbs stay read-only and unchanged, and TypeScript types resolve automatically for `@design-ai/cli/sdk` with no build step on either side.
6
+
7
+ ### Verified
8
+ - All 8 audits passed.
9
+ - `npm run release:check` (unit tests incl. SDK type-sync guard and `learn.*` write tests, strict audits, whitespace, package contents, release metadata, release self-tests, packed-tarball smoke that imports `@design-ai/cli/sdk` and exercises `learn.remember`).
10
+ - `npm run release:metadata`.
11
+ - `git diff --check`.
12
+ - The declarations compile under `tsc --strict` against a real consumer (transient check; TypeScript is not a package dependency).
13
+ - Main-branch GitHub Actions (`Design-AI audit`, `Deploy doc site`) passed for the constituent commits.
14
+
15
+ ### Versions
16
+ - `package.json` + `.claude-plugin/plugin.json`: 4.59.0 → 4.60.0.
17
+ - `vscode-extension/package.json`: remains 0.4.1.
18
+
19
+ ### What this enables
20
+ - TypeScript/editor tooling resolves the full SDK surface (verbs, option interfaces, return types) with zero `@types` install and zero build step, and agents gain an explicit, opt-in `learn.*` write namespace for recording preferences, feedback, and check-derived learnings — while every read-only verb keeps its no-write guarantee.
21
+
22
+ ### What's still ahead
23
+ - SDK surface is feature-complete for the planned phases; further verbs are demand-driven. Remaining roadmap work returns to other surfaces (see later phases).
24
+
25
+ ## Phase 760 — Agent SDK Phase B (local writes, implemented, unreleased)
26
+
27
+ Adds the Phase B local-write surface described in [AGENT-SDK.md](AGENT-SDK.md#phase-b--explicit-local-writes-shipped): a single `learn` namespace export grouping three explicit, opt-in local-write verbs (`learn.remember`, `learn.feedback`, `learn.captureFromCheck`). This is the only place the SDK writes files; the 8 Phase A verbs are unchanged and stay read-only — `check()` in particular gets no capture option, so its read-only contract test still holds. Landed in `main` as `cli/sdk/learn-adapter.mjs`; code-complete and gated but not yet released as an npm version.
28
+
29
+ ### Delivered
30
+ - [x] `learn.remember(text, opts?)` — records a local learning-profile preference. Adapter over `rememberLearning` from `cli/lib/learn-profile.mjs`; always writes `source: "sdk"`.
31
+ - [x] `learn.feedback(text, opts?)` — records keep/avoid/improve feedback as a learning-profile entry. Adapter over `recordLearningFeedback`; the library normalizes `outcome` and folds it into the stored entry's `text` and `source`.
32
+ - [x] `learn.captureFromCheck(artifact, opts?)` — checks a Markdown artifact, then captures its non-pass results as learning-profile entries. Adapter over `checkArtifactContent` + `buildCheckLearningCapture` from `cli/lib/check.mjs` — the SDK equivalent of the CLI's `check --learn --yes`. Duplicate captures are skipped (`reason: "duplicate-entry-text"`).
33
+ - [x] All three write **only** the local learning profile (`DESIGN_AI_LEARNING_FILE` / `defaultLearningFile()`), never the network; no `filePath` or `now`/timestamp option on any of them — consumers target a profile via the env var, exactly like the CLI.
34
+ - [x] `learn` is a frozen object (`Object.freeze`); `cli/sdk/index.mjs` re-exports it alongside the 8 unchanged Phase A verbs.
35
+ - [x] Input validation reused from `cli/sdk/validate.mjs` (`requireNonEmptyString`, `requireOptions`, `optionalString`); `routeId` validated with the same `assertKnownRouteId` helper the read-only `check` adapter uses.
36
+ - [x] `cli/sdk/learn-adapter.test.mjs` — per-verb coverage: persistence (re-reading the written profile), `remember`'s `source: "sdk"`, `feedback`'s outcome-to-text/source mapping, `captureFromCheck`'s added/skipped results and duplicate-capture skip, and bad-input rejection. 16 new tests.
37
+ - [x] `cli/sdk/index.test.mjs` contract test updated: the 8 read-only function verbs are pinned unchanged; `learn` is pinned as a frozen object export with exactly `["captureFromCheck", "feedback", "remember"]`, plus return-shape keys for each verb (including the full `captureLearningEntries` result shape: `file, dryRun, applied, source, candidateCount, addedCount, skippedCount, count, entries, skipped`).
38
+ - [x] `cli/sdk/types.test.mjs` unchanged and passing (it filters to `typeof === "function"`, so the `learn` object export is naturally excluded); a new assertion added confirming `index.d.ts` declares `export declare const learn:`.
39
+ - [x] `cli/sdk/index.d.ts` — hand-written types for `LearnRememberOptions`, `LearnFeedbackOptions`, `LearnCaptureOptions`, `LearningProfileEntry`, `LearningProfile`, `RememberResult`, `CaptureSkippedEntry`, `CaptureResult`, and the `learn` const declaration. Verified to compile under `tsc --strict` against a transient consumer (zero-toolchain stance preserved — TypeScript is not added to `package.json`).
40
+ - [x] `docs/SDK.md` — "Phase B — local writes" section replaces the "not yet shipped" placeholder: full reference for all three verbs, options, return shapes, the `DESIGN_AI_LEARNING_FILE` target, and the write-boundary rationale.
41
+ - [x] `docs/AGENT-SDK.md` Phase B section updated from "only if demand" to shipped.
42
+ - [x] `npm test` (563/563), `npm run audit` (8/8), `npm run release:metadata` all pass.
43
+
44
+ ### Remaining
45
+ - [ ] Release decision: bundle Phase B into the next npm version alongside or after Phase 759's TypeScript declarations.
46
+ - [ ] Optional follow-up: extend the packed-tarball SDK smoke (`tools/audit/package-smoke.py`) to exercise `learn.remember` against a temp `DESIGN_AI_LEARNING_FILE`.
47
+
48
+ ## Phase 759 — Agent SDK TypeScript declarations (implemented, unreleased)
49
+
50
+ Ships hand-written type declarations for the eight SDK verbs so TypeScript and editor tooling resolve `@design-ai/cli/sdk` with full types — no `@types` package, no build step (the `.d.ts` is maintained by hand to preserve the zero-toolchain stance). Landed in `main` as `cli/sdk/index.d.ts`; additive (types only), pending bundling into the next npm version.
51
+
52
+ ### Delivered
53
+ - [x] `cli/sdk/index.d.ts` — precise signatures + return interfaces for all eight verbs (incl. `search` ranked/plain overloads, the `RouteConfidence` union, and optional `learningContext`/`recall`/`relatedKnowledge` fields), derived from the runtime shapes the contract test pins.
54
+ - [x] `exports` `types` condition (`"./sdk": { "types": …, "default": … }`) so the subpath resolves declarations under `moduleResolution: node16`/`nodenext`/`bundler`.
55
+ - [x] `cli/sdk/types.test.mjs` — zero-dependency guard asserting the `.d.ts` declares exactly the runtime-exported verbs (drift anchor). Declarations independently verified to compile under `tsc --strict` against a real consumer.
56
+ - [x] `docs/SDK.md` TypeScript section.
57
+
58
+ ### Remaining
59
+ - [ ] Release: bundle into the next npm version (with Phase B, or standalone).
60
+
3
61
  ## Phase 758 — Agent SDK Phase A Release (v4.59.0) ✓ ready
4
62
 
5
63
  Ships the Agent SDK Phase A work (Phase 757) as an npm version: the curated, semver-stable `@design-ai/cli/sdk` surface — eight read-only adapter verbs (`route`, `prompt`, `pack`, `search`, `recall`, `check`, `routes`, `version`) over the same `cli/lib` functions the CLI and MCP already call. No new capabilities, no runtime dependencies, no network, no telemetry; read-only, deterministic, backward-compatible.
@@ -19,7 +77,8 @@ Ships the Agent SDK Phase A work (Phase 757) as an npm version: the curated, sem
19
77
  - design-ai's routing, prompt/pack generation, ranked search, recall, and artifact checks become plain importable functions for agent runtimes, build scripts, and custom tools — deterministic, in-process, zero-dependency — behind a semver-stable contract that insulates callers from internal `cli/lib` refactors.
20
78
 
21
79
  ### What's still ahead
22
- - Optional `cli/sdk/index.d.ts` hand-written type declarations (zero-build), and Phase B explicit local-write adapters — both deferred until an adopter needs them.
80
+ - Phase B explicit local-write adapters (`learn.remember`/`feedback`, capture) implemented and landed in `main` (see Phase 760); pending release.
81
+ - Hand-written `cli/sdk/index.d.ts` TypeScript declarations shipped after this release (see Phase 759).
23
82
 
24
83
  ## Phase 757 — Agent SDK (Phase A implemented, unreleased)
25
84
 
package/docs/SDK.md CHANGED
@@ -1,10 +1,10 @@
1
1
  # Agent SDK reference
2
2
 
3
- > Status: shipped (Phase A) — read-only verbs, semver-stable surface
3
+ > Status: shipped (Phase A + Phase B) — 8 read-only verbs plus the opt-in `learn.*` local-write namespace, semver-stable surface
4
4
 
5
5
  `@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.
6
6
 
7
- 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.
7
+ 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 (read-only) and Phase B (local-write) surface.
8
8
 
9
9
  ## Install and import
10
10
 
@@ -20,6 +20,16 @@ import { route, prompt, pack, search, recall, check, routes, version } from "@de
20
20
 
21
21
  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.
22
22
 
23
+ ### TypeScript
24
+
25
+ Hand-written type declarations ship with the package at `cli/sdk/index.d.ts` and are wired through the `exports` `types` condition, so TypeScript and editors resolve them automatically for `@design-ai/cli/sdk` — no `@types` install, and no build step on our side (the declarations are maintained by hand to preserve the zero-toolchain stance). Use `moduleResolution: node16`/`nodenext` (or `bundler`) so the subpath `types` condition is honored. All exported types (`RouteResult`, `PromptPlan`, `Pack`, `SearchHit`, `RankedSearchHit`, `RecallResult`, `CheckReport`, option interfaces, …) are importable:
26
+
27
+ ```ts
28
+ import { route, type RouteResult, type SearchOptions } from "@design-ai/cli/sdk";
29
+ ```
30
+
31
+ A `node --test` guard (`cli/sdk/types.test.mjs`) asserts the declaration file stays in exact sync with the runtime exports.
32
+
23
33
  ## Phase A: read-only
24
34
 
25
35
  Every verb below is a pure, read-only adapter:
@@ -57,7 +67,7 @@ prompt(brief: string, opts?: {
57
67
  learningLimit?: number,
58
68
  withRecall?: boolean,
59
69
  recallLimit?: number,
60
- }): PromptPlan | null
70
+ }): PromptPlan
61
71
  ```
62
72
 
63
73
  - `routeId` — force a route id instead of scoring the brief.
@@ -153,10 +163,87 @@ version(): { cli: string, corpus: string }
153
163
 
154
164
  - Additive changes (new optional fields, new opt-in options) are minor version bumps.
155
165
  - Signature or return-shape changes are major version bumps.
156
- - 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.
166
+ - The 8 read-only function exports (`route`, `prompt`, `pack`, `search`, `recall`, `check`, `routes`, `version`) plus the frozen `learn` namespace object (`learn.remember`, `learn.feedback`, `learn.captureFromCheck`) 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.
157
167
  - `cli/lib/*` remains internal and unstable. Only `@design-ai/cli/sdk` is a supported import path.
158
168
  - Determinism: the same inputs always produce the same outputs, with no randomness or time-dependence added by the adapter layer.
159
169
 
160
- ## Phase B (not yet shipped)
170
+ ## Phase B local writes
171
+
172
+ Phase A (above) is read-only by design: the 8 verbs never write a file. Phase B adds a single namespace export, `learn`, grouping the three explicit, opt-in **LOCAL-WRITE** verbs. This is the only place the SDK writes files — every Phase A verb stays read-only and unchanged.
173
+
174
+ ```js
175
+ import { learn } from "@design-ai/cli/sdk";
176
+
177
+ learn.remember(text, opts?) // opts: { category?: string }
178
+ learn.feedback(text, opts?) // opts: { outcome?: string, category?: string }
179
+ learn.captureFromCheck(artifact, opts?) // opts: { routeId?: string }
180
+ ```
181
+
182
+ **Write boundary rationale:** an SDK consumer should never be surprised by a file write. Phase A's 8 verbs are safe to call from any context — a build script, a read path, a CI check — with zero side effects. `learn.*` is the deliberate, narrow exception: three verbs, one destination, no ambiguity about what gets written or where.
183
+
184
+ All three verbs write **only** the local learning profile — `DESIGN_AI_LEARNING_FILE` if set, otherwise the CLI's `defaultLearningFile()` default path — and never touch the network. There is no `filePath` option and no `now`/timestamp option on any of them: target a specific profile the same way the CLI does, by setting the `DESIGN_AI_LEARNING_FILE` environment variable before calling. The underlying library functions supply their own defaults (the env var and `new Date()`).
185
+
186
+ ### `learn.remember(text, opts)`
187
+
188
+ Record a local learning-profile preference. Adapter over `rememberLearning` from `cli/lib/learn-profile.mjs`.
189
+
190
+ ```js
191
+ learn.remember(text: string, opts?: { category?: string }): RememberResult
192
+ ```
193
+
194
+ - `text` — required, non-empty string.
195
+ - `category` — one of the learning-profile categories (e.g. `preference`, `brand`, `workflow`, `constraint`, `accessibility`, `korean`, `other`); the library normalizes and validates it. Default: `"preference"`.
196
+
197
+ Returns a `RememberResult`: `{ file, entry, profile }`, where `entry` is `{ id, category, text, source, createdAt }` (`source` is always `"sdk"`) and `profile` is the full updated learning profile `{ version, updatedAt, entries }`.
198
+
199
+ ### `learn.feedback(text, opts)`
200
+
201
+ Record feedback (keep/avoid/improve) as a local learning-profile entry. Adapter over `recordLearningFeedback` from `cli/lib/learn-profile.mjs`.
202
+
203
+ ```js
204
+ learn.feedback(text: string, opts?: { outcome?: string, category?: string }): RememberResult
205
+ ```
206
+
207
+ - `text` — required, non-empty string.
208
+ - `outcome` — any string; the library normalizes it (`normalizeFeedbackOutcome`) to one of `keep` / `avoid` / `improve` and folds it into both the stored entry's `text` (e.g. `"Avoid in future outputs: …"`) and its `source` (e.g. `"feedback:avoid"`). Default: `"improve"`.
209
+ - `category` — same category enum as `remember`. Default: `"workflow"`.
210
+
211
+ Returns the same `RememberResult` shape as `learn.remember`.
212
+
213
+ ### `learn.captureFromCheck(artifact, opts)`
214
+
215
+ Check a Markdown artifact, then capture its non-pass results as local learning-profile entries — the SDK equivalent of the CLI's `check --learn --yes`. Adapter over `checkArtifactContent` + `buildCheckLearningCapture` from `cli/lib/check.mjs`.
216
+
217
+ ```js
218
+ learn.captureFromCheck(artifact: string, opts?: { routeId?: string }): CaptureResult
219
+ ```
220
+
221
+ - `artifact` — required, non-empty Markdown string.
222
+ - `routeId` — add route-specific check requirements before capturing; must be a known route id (validated with the same `assertKnownRouteId` helper the read-only `check` adapter uses).
223
+
224
+ Returns a `CaptureResult` — the `captureLearningEntries` return shape:
225
+
226
+ ```
227
+ {
228
+ file: string, // the learning profile file written to
229
+ dryRun: false, // always false; captureFromCheck always applies
230
+ applied: true, // always true when dryRun is false
231
+ source: string, // "check:<routeId>" or "check:artifact"
232
+ candidateCount: number, // non-pass check results considered
233
+ addedCount: number, // entries newly written
234
+ skippedCount: number, // entries skipped as duplicates of existing entries
235
+ count: number, // total entries in the profile after this call
236
+ entries: LearningProfileEntry[], // the entries that were added
237
+ skipped: Array<{ category, text, source, reason }>, // reason is "duplicate-entry-text"
238
+ }
239
+ ```
240
+
241
+ Calling `captureFromCheck` twice with the same artifact adds nothing the second time — every candidate is skipped with `reason: "duplicate-entry-text"`.
242
+
243
+ ### Validation
244
+
245
+ All three verbs use the same `cli/sdk/validate.mjs` helpers as Phase A: `requireNonEmptyString` for `text`/`artifact`, `requireOptions` for the options bag, and `optionalString` for string options. `routeId` is validated with `assertKnownRouteId`, exactly like the read-only `check` adapter. Bad input throws a plain `Error` with a clear message — no process exit code, no silent fallback.
246
+
247
+ ### What stays unchanged
161
248
 
162
- 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.
249
+ `check()` (the Phase A read-only verb) is untouched: it still never writes, and it has no capture option. Capture is exclusively reached through `learn.captureFromCheck`, which is a separate, explicitly-named write verb this keeps the read/write boundary visible at the call site rather than hidden behind an option flag on a read-only function.
@@ -5,18 +5,18 @@
5
5
 
6
6
  ## Summary
7
7
 
8
- 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.
8
+ npm is publicly published at `@design-ai/cli@4.59.0` (npm dist-tag `latest` = `4.59.0`); publish run from tag `v4.59.0` succeeded with provenance and all pre-publish gates green. This release adds the read-only Agent SDK surface (`@design-ai/cli/sdk`, eight adapter verbs) — additive only, so CLI output is unchanged. The live `npm run registry:smoke` passes cleanly against published `@design-ai/cli@4.59.0` ("Registry smoke passed"), covering the retrieval surfaces (index/ranked/embeddings/recall) and route enrichment. GitHub Release `v4.59.0` is published, and the Homebrew tap formula points at the `v4.59.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.
9
9
 
10
10
  ## Results
11
11
 
12
12
  | Surface | Checked target | Result | Evidence |
13
13
  |---|---|---|---|
14
- | 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" |
14
+ | npm registry | `@design-ai/cli` | Published latest is `4.59.0` (tag `v4.59.0`, provenance). Pre-publish packed-tarball smoke (incl. SDK import) and live `npm run registry:smoke` both pass for `@design-ai/cli@4.59.0`. | `npm view @design-ai/cli version` → `4.59.0`; live registry smoke "Registry smoke passed" |
15
15
  | 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` |
16
- | 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` |
16
+ | Homebrew tap | `Formula/design-ai.rb` | Formula pinned to `v4.59.0` release source tarball with SHA-256 `e711203fb96fee5f6e7f25d809bc42108553bc8108ddf3e8cd181f0a76f6e0f5` (recomputed from the published tag tarball) | `Formula/design-ai.rb` |
17
17
  | 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` |
18
- | GitHub Release | `v4.58.0` | Published for tag `v4.58.0` at commit `22dc8a9` | `gh release view v4.58.0` |
19
- | 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` |
18
+ | GitHub Release | `v4.59.0` | Published for tag `v4.59.0` at commit `9f53345` | `gh release view v4.59.0` |
19
+ | MCP server | `@design-ai/cli@4.59.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` |
20
20
 
21
21
  ## Interpretation
22
22
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@design-ai/cli",
3
- "version": "4.59.0",
3
+ "version": "4.60.0",
4
4
  "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.",
5
5
  "bin": {
6
6
  "design-ai": "cli/bin/design-ai.mjs",
@@ -8,7 +8,10 @@
8
8
  },
9
9
  "type": "module",
10
10
  "exports": {
11
- "./sdk": "./cli/sdk/index.mjs",
11
+ "./sdk": {
12
+ "types": "./cli/sdk/index.d.ts",
13
+ "default": "./cli/sdk/index.mjs"
14
+ },
12
15
  "./package.json": "./package.json"
13
16
  },
14
17
  "engines": {
@@ -763,15 +763,20 @@ def assert_version_json_smoke(cmd: list[str], *, env: dict[str, str], cwd: Path
763
763
  assert_version_json(result.stdout, context=context, cmd=cmd)
764
764
 
765
765
 
766
- # Phase A Agent SDK packed-tarball smoke (docs/AGENT-SDK.md, docs/SDK.md): imports
766
+ # Agent SDK packed-tarball smoke (docs/AGENT-SDK.md, docs/SDK.md): imports
767
767
  # `@design-ai/cli/sdk` from the installed package (proving the package.json
768
768
  # "exports" map resolves) and exercises route/search(ranked)/recall, asserting
769
769
  # each call is deterministic (same input -> same JSON-serialized output) and
770
770
  # that Phase A's read-only contract holds (prompt/pack never include
771
- # learningUsage). This is a Node script, not a design-ai CLI invocation, so it
772
- # is run directly rather than through npm_exec_cmd/design-ai bin helpers.
771
+ # learningUsage). Also exercises Phase B's `learn.remember` against a temp
772
+ # DESIGN_AI_LEARNING_FILE and asserts it actually wrote the profile. This is a
773
+ # Node script, not a design-ai CLI invocation, so it is run directly rather
774
+ # than through npm_exec_cmd/design-ai bin helpers.
773
775
  SDK_SMOKE_SCRIPT = """
774
- import { check, pack, prompt, recall, route, routes, search, version } from "@design-ai/cli/sdk";
776
+ import { existsSync, mkdtempSync, readFileSync, rmSync } from "node:fs";
777
+ import { tmpdir } from "node:os";
778
+ import path from "node:path";
779
+ import { check, learn, pack, prompt, recall, route, routes, search, version } from "@design-ai/cli/sdk";
775
780
 
776
781
  const brief = "Spec a Button component API with variants, props, and keyboard accessibility";
777
782
 
@@ -808,6 +813,23 @@ const ck = check(
808
813
  );
809
814
  if (typeof ck.status !== "string") throw new Error("check() bad shape");
810
815
 
816
+ // Phase B: learn.remember must write only DESIGN_AI_LEARNING_FILE.
817
+ const smokeDir = mkdtempSync(path.join(tmpdir(), "design-ai-sdk-learn-smoke-"));
818
+ const learningFile = path.join(smokeDir, "learning.json");
819
+ process.env.DESIGN_AI_LEARNING_FILE = learningFile;
820
+ try {
821
+ const result = learn.remember("Package smoke: prefer 8px spacing grid.", { category: "preference" });
822
+ if (result.entry.source !== "sdk") throw new Error("learn.remember() entry.source must be 'sdk'");
823
+ if (!existsSync(learningFile)) throw new Error("learn.remember() did not write DESIGN_AI_LEARNING_FILE");
824
+ const onDisk = JSON.parse(readFileSync(learningFile, "utf8"));
825
+ if (!Array.isArray(onDisk.entries) || onDisk.entries.length !== 1) {
826
+ throw new Error("learn.remember() did not persist exactly one entry");
827
+ }
828
+ } finally {
829
+ rmSync(smokeDir, { recursive: true, force: true });
830
+ delete process.env.DESIGN_AI_LEARNING_FILE;
831
+ }
832
+
811
833
  console.log("design-ai sdk smoke passed");
812
834
  """.strip()
813
835