@design-ai/cli 4.59.0 → 4.61.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.
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +51 -0
- package/README.ko.md +3 -3
- package/README.md +3 -3
- package/cli/commands/help.mjs +1 -1
- package/cli/commands/search.mjs +81 -2
- package/cli/lib/search-eval.mjs +241 -0
- package/cli/lib/search.mjs +56 -1
- package/cli/sdk/index.d.ts +435 -0
- package/cli/sdk/index.mjs +13 -5
- package/cli/sdk/learn-adapter.mjs +100 -0
- package/docs/AGENT-SDK.md +2 -2
- package/docs/AI-LEARNING-PHASE2.md +2 -2
- package/docs/NEXT-SURFACE-DECISION.md +11 -2
- package/docs/ROADMAP.md +97 -1
- package/docs/SDK.md +93 -6
- package/docs/external-status.md +6 -6
- package/package.json +5 -2
- package/tools/audit/package-smoke.py +26 -4
- package/tools/audit/smoke_assertions.py +10 -5
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "design-ai",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.61.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,57 @@
|
|
|
2
2
|
|
|
3
3
|
User-facing release notes for design-ai. Versions follow semver.
|
|
4
4
|
|
|
5
|
+
## v4.61.0 — Ranked-search eval checkpoint (2026-07)
|
|
6
|
+
|
|
7
|
+
Closes the last open follow-up from the AI-learning Phase A review (FU-3 in `docs/AI-LEARNING-PHASE2.md`): `design-ai search` gains eval-checkpoint modes mirroring the route/prompt/pack/learn pattern, run through the shipped ranked (BM25) search path. This is the standing evidence artifact for any future decision to promote `--ranked` to the default search mode. Additive and backward-compatible; default `search` behavior is unchanged.
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
- `search --eval-template [--json]` — emit a versioned ranked-search checkpoint template. The default template ships six cases that all pass against the shipped corpus, including the Korean particle-form regression queries (`버튼`, `버튼을`, `접근성이`) from the Hangul-bigram work, so the eval doubles as the retrieval-level Korean tokenization regression.
|
|
11
|
+
- `search --eval [--strict] [--json]` with `--from-file file` / `--stdin` — run a checkpoint file's cases through the ranked search path and report pass/warn/fail per case; `--strict` exits nonzero on any failure. Case assertions: `expectRelPathIn` (top hit), `minHits`, and `matchedTokenIncludes`.
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
- `search` help/usage lines (CLI `help` topic and command help) document the new eval modes; the shared smoke assertion pins were updated in lockstep.
|
|
15
|
+
|
|
16
|
+
### Verified
|
|
17
|
+
- All 8 audits passed.
|
|
18
|
+
- `npm run release:check`.
|
|
19
|
+
- `npm run release:metadata`.
|
|
20
|
+
- `git diff --check`.
|
|
21
|
+
- Main-branch GitHub Actions (`Design-AI audit`, `Deploy doc site`) passed for the constituent commits.
|
|
22
|
+
|
|
23
|
+
### Versions
|
|
24
|
+
- `package.json` + `.claude-plugin/plugin.json`: 4.60.0 → 4.61.0.
|
|
25
|
+
- `vscode-extension/package.json`: remains 0.4.1.
|
|
26
|
+
|
|
27
|
+
### What this enables
|
|
28
|
+
- Any future `--ranked` default promotion is now evidence-backed and announced: the checkpoint demonstrates ranked-search quality (including Korean agglutinative queries) reproducibly, per the FU-3 gate set in the Phase A implementation review.
|
|
29
|
+
|
|
30
|
+
## v4.60.0 — Agent SDK types and local-write namespace (2026-07)
|
|
31
|
+
|
|
32
|
+
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.
|
|
33
|
+
|
|
34
|
+
### Added
|
|
35
|
+
- `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`.
|
|
36
|
+
- `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.
|
|
37
|
+
|
|
38
|
+
### Changed
|
|
39
|
+
- 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.
|
|
40
|
+
- `tools/audit/package-smoke.py` now also exercises `learn.remember` from the installed tarball against a temp `DESIGN_AI_LEARNING_FILE`, asserting the write.
|
|
41
|
+
|
|
42
|
+
### Verified
|
|
43
|
+
- All 8 audits passed.
|
|
44
|
+
- `npm run release:check`.
|
|
45
|
+
- `npm run release:metadata`.
|
|
46
|
+
- `git diff --check`.
|
|
47
|
+
- Main-branch GitHub Actions (`Design-AI audit`, `Deploy doc site`) passed for the constituent commits.
|
|
48
|
+
|
|
49
|
+
### Versions
|
|
50
|
+
- `package.json` + `.claude-plugin/plugin.json`: 4.59.0 → 4.60.0.
|
|
51
|
+
- `vscode-extension/package.json`: remains 0.4.1.
|
|
52
|
+
|
|
53
|
+
### What this enables
|
|
54
|
+
- 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.
|
|
55
|
+
|
|
5
56
|
## v4.59.0 — Agent SDK Phase A (2026-07)
|
|
6
57
|
|
|
7
58
|
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-
|
|
15
|
+
> **배포 상태, 2026-07-05 확인:** 로컬 `npm run release:check`는 통과했고, GitHub Pages 문서는 live 상태이며, GitHub Release `v4.60.0`과 npm `@design-ai/cli@4.60.0`(`latest`) publish가 provenance와 함께 확인됐어요. 이번 릴리스는 Agent SDK(`@design-ai/cli/sdk`)에 TypeScript 선언과 opt-in `learn.*` 로컬 쓰기 네임스페이스를 추가했고, 읽기 전용 8개 verb는 그대로라 CLI 출력도 변함없어요. live `npm run registry:smoke`가 published `@design-ai/cli@4.60.0`에 대해 깨끗이 통과했어요(retrieval 표면과 route enrichment 포함). Homebrew formula는 `v4.60.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
|
|
|
@@ -75,7 +75,7 @@ design-ai install
|
|
|
75
75
|
/design-from-brief 프리랜서를 위한 한국 핀테크
|
|
76
76
|
```
|
|
77
77
|
|
|
78
|
-
CLI 명령어: `design-ai install [--json]`, `update [--dry-run] [--json]`, `uninstall [--json]`, `status [--json]`, `list [skills|commands|agents] [--json]`, `route <brief|--from-file file|--stdin|--list|--eval-template|--eval> [--limit N] [--explain] [--strict] [--json]`, `routes [--json]`, `prompt <brief|--from-file file|--stdin|--eval-template|--eval> [--out file] [--route id] [--with-learning] [--learning-category kind] [--learning-limit N] [--with-recall] [--recall-limit N] [--strict] [--json]`, `pack <brief|--from-file file|--stdin|--eval-template|--eval> [--out file] [--route id] [--with-learning] [--learning-category kind] [--learning-limit N] [--with-recall] [--recall-limit N] [--max-bytes N] [--strict] [--json]`, `learn [--init|--remember text|--feedback text|--list|--export|--query text|--explain|--recall query|--backup|--redact|--verify|--diff|--restore|--restore-backups [--prune]|--import|--audit [--fix]|--curate|--stats|--usage|--signals [--strict]|--agent-backlog [--strict]|--propose-skills [--min-evidence N] [--review-file path] [--review-check|--apply-plan] [--strict]|--eval-template|--eval [--strict]|--forget id|--clear] [--json|--report|--patch|--review-template] [--out file]`, `check <artifact.md|--stdin|--examples> [--route id|--all-routes] [--issues-only] [--strict] [--learn [--yes] [--learning-file path]] [--json]`, `workspace [--root path] [--learning-file path] [--learning-usage path] [--learning-eval path] [--strict] [--json]`, `site <workspace.json|--stdin> [--strict] [--json|--mcp-check [--probes]|--mcp-plan [--probes] [--json]|--next-actions [--json]|--graph|--tasks|--bundle|--report|--prompts|--prompt id [--task id]] [--out file] | site <bundle-dir> --bundle-check [--json] | site <bundle-dir> --bundle-compare other-bundle-dir [--json] | site <bundle-dir> --bundle-handoff [--task id] [--json] | site <bundle-dir> --bundle-repair [--yes] [--json] [--out file] | site --init --name name --live-url url [--next-actions] [--out file] | site --init --name name --live-url url --bundle --out dir | site --from-intake file.md|--stdin [--json|--next-actions [--json]|--tasks|--bundle [--tasks] --out dir] [--out file] | site --intake-template [--language en|ko] [--json] [--out file] | site --sample [--out file] | site --prompt-list [--json]`, `examples [query|--route id] [--limit N] [--json]`, `search <query> [--dir kind] [--limit N] [--ranked] [--embeddings [--provider "cmd args"]] [--json]`, `index [--build|--status|--verify] [--json] [--embeddings [--provider "cmd args"]]`, `show <file[:line]> [--lines N:M] [--context N] [--json]`, `audit [--strict] [--quiet] [--json]`, `doctor [--strict] [--json] [--fix]`, `mcp`, `version [--json]`, `help [command|--json]`.
|
|
78
|
+
CLI 명령어: `design-ai install [--json]`, `update [--dry-run] [--json]`, `uninstall [--json]`, `status [--json]`, `list [skills|commands|agents] [--json]`, `route <brief|--from-file file|--stdin|--list|--eval-template|--eval> [--limit N] [--explain] [--strict] [--json]`, `routes [--json]`, `prompt <brief|--from-file file|--stdin|--eval-template|--eval> [--out file] [--route id] [--with-learning] [--learning-category kind] [--learning-limit N] [--with-recall] [--recall-limit N] [--strict] [--json]`, `pack <brief|--from-file file|--stdin|--eval-template|--eval> [--out file] [--route id] [--with-learning] [--learning-category kind] [--learning-limit N] [--with-recall] [--recall-limit N] [--max-bytes N] [--strict] [--json]`, `learn [--init|--remember text|--feedback text|--list|--export|--query text|--explain|--recall query|--backup|--redact|--verify|--diff|--restore|--restore-backups [--prune]|--import|--audit [--fix]|--curate|--stats|--usage|--signals [--strict]|--agent-backlog [--strict]|--propose-skills [--min-evidence N] [--review-file path] [--review-check|--apply-plan] [--strict]|--eval-template|--eval [--strict]|--forget id|--clear] [--json|--report|--patch|--review-template] [--out file]`, `check <artifact.md|--stdin|--examples> [--route id|--all-routes] [--issues-only] [--strict] [--learn [--yes] [--learning-file path]] [--json]`, `workspace [--root path] [--learning-file path] [--learning-usage path] [--learning-eval path] [--strict] [--json]`, `site <workspace.json|--stdin> [--strict] [--json|--mcp-check [--probes]|--mcp-plan [--probes] [--json]|--next-actions [--json]|--graph|--tasks|--bundle|--report|--prompts|--prompt id [--task id]] [--out file] | site <bundle-dir> --bundle-check [--json] | site <bundle-dir> --bundle-compare other-bundle-dir [--json] | site <bundle-dir> --bundle-handoff [--task id] [--json] | site <bundle-dir> --bundle-repair [--yes] [--json] [--out file] | site --init --name name --live-url url [--next-actions] [--out file] | site --init --name name --live-url url --bundle --out dir | site --from-intake file.md|--stdin [--json|--next-actions [--json]|--tasks|--bundle [--tasks] --out dir] [--out file] | site --intake-template [--language en|ko] [--json] [--out file] | site --sample [--out file] | site --prompt-list [--json]`, `examples [query|--route id] [--limit N] [--json]`, `search <query|--eval-template|--eval> [--dir kind] [--limit N] [--ranked] [--embeddings [--provider "cmd args"]] [--strict] [--json]`, `index [--build|--status|--verify] [--json] [--embeddings [--provider "cmd args"]]`, `show <file[:line]> [--lines N:M] [--context N] [--json]`, `audit [--strict] [--quiet] [--json]`, `doctor [--strict] [--json] [--fix]`, `mcp`, `version [--json]`, `help [command|--json]`.
|
|
79
79
|
|
|
80
80
|
자세한 내용은 [`docs/DISTRIBUTION.ko.md`](docs/DISTRIBUTION.ko.md)를 확인하세요.
|
|
81
81
|
|
|
@@ -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.
|
|
175
|
+
전체 단계 로그는 [`docs/ROADMAP.md`](docs/ROADMAP.md), 현재 완료 범위는 [`docs/PRODUCT-READINESS.md`](docs/PRODUCT-READINESS.md)에서 확인하세요. 현재 **v4.60.0**: Agent SDK(`@design-ai/cli/sdk`) — TypeScript 선언 + opt-in `learn.*` 로컬 쓰기 네임스페이스, 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-
|
|
15
|
+
> **Distribution status, checked 2026-07-05:** local `npm run release:check` passes, GitHub Pages docs are live, GitHub Release `v4.60.0` is published, `@design-ai/cli@4.60.0` is public on npm (`latest`) with provenance, and the live `npm run registry:smoke` passes cleanly against `@design-ai/cli@4.60.0`. The Homebrew formula is pinned to `v4.60.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
|
|
|
@@ -75,7 +75,7 @@ Any available path installs all 20 skills, 17 commands, and 4 agents under `~/.c
|
|
|
75
75
|
/design-from-brief Korean fintech for freelancers
|
|
76
76
|
```
|
|
77
77
|
|
|
78
|
-
CLI commands: `design-ai install [--json]`, `update [--dry-run] [--json]`, `uninstall [--json]`, `status [--json]`, `list [skills|commands|agents] [--json]`, `route <brief|--from-file file|--stdin|--list|--eval-template|--eval> [--limit N] [--explain] [--strict] [--json]`, `routes [--json]`, `prompt <brief|--from-file file|--stdin|--eval-template|--eval> [--out file] [--route id] [--with-learning] [--learning-category kind] [--learning-limit N] [--with-recall] [--recall-limit N] [--strict] [--json]`, `pack <brief|--from-file file|--stdin|--eval-template|--eval> [--out file] [--route id] [--with-learning] [--learning-category kind] [--learning-limit N] [--with-recall] [--recall-limit N] [--max-bytes N] [--strict] [--json]`, `learn [--init|--remember text|--feedback text|--list|--export|--query text|--explain|--recall query|--backup|--redact|--verify|--diff|--restore|--restore-backups [--prune]|--import|--audit [--fix]|--curate|--stats|--usage|--signals [--strict]|--agent-backlog [--strict]|--propose-skills [--min-evidence N] [--review-file path] [--review-check|--apply-plan] [--strict]|--eval-template|--eval [--strict]|--forget id|--clear] [--json|--report|--patch|--review-template] [--out file]`, `check <artifact.md|--stdin|--examples> [--route id|--all-routes] [--issues-only] [--strict] [--learn [--yes] [--learning-file path]] [--json]`, `workspace [--root path] [--learning-file path] [--learning-usage path] [--learning-eval path] [--strict] [--json]`, `site <workspace.json|--stdin> [--strict] [--json|--mcp-check [--probes]|--mcp-plan [--probes] [--json]|--next-actions [--json]|--graph|--tasks|--bundle|--report|--prompts|--prompt id [--task id]] [--out file] | site <bundle-dir> --bundle-check [--json] | site <bundle-dir> --bundle-compare other-bundle-dir [--json] | site <bundle-dir> --bundle-handoff [--task id] [--json] | site <bundle-dir> --bundle-repair [--yes] [--json] [--out file] | site --init --name name --live-url url [--next-actions] [--out file] | site --init --name name --live-url url --bundle --out dir | site --from-intake file.md|--stdin [--json|--next-actions [--json]|--tasks|--bundle [--tasks] --out dir] [--out file] | site --intake-template [--language en|ko] [--json] [--out file] | site --sample [--out file] | site --prompt-list [--json]`, `examples [query|--route id] [--limit N] [--json]`, `search <query> [--dir kind] [--limit N] [--ranked] [--embeddings [--provider "cmd args"]] [--json]`, `index [--build|--status|--verify] [--json] [--embeddings [--provider "cmd args"]]`, `show <file[:line]> [--lines N:M] [--context N] [--json]`, `audit [--strict] [--quiet] [--json]`, `doctor [--strict] [--json] [--fix]`, `mcp`, `version [--json]`, `help [command|--json]`.
|
|
78
|
+
CLI commands: `design-ai install [--json]`, `update [--dry-run] [--json]`, `uninstall [--json]`, `status [--json]`, `list [skills|commands|agents] [--json]`, `route <brief|--from-file file|--stdin|--list|--eval-template|--eval> [--limit N] [--explain] [--strict] [--json]`, `routes [--json]`, `prompt <brief|--from-file file|--stdin|--eval-template|--eval> [--out file] [--route id] [--with-learning] [--learning-category kind] [--learning-limit N] [--with-recall] [--recall-limit N] [--strict] [--json]`, `pack <brief|--from-file file|--stdin|--eval-template|--eval> [--out file] [--route id] [--with-learning] [--learning-category kind] [--learning-limit N] [--with-recall] [--recall-limit N] [--max-bytes N] [--strict] [--json]`, `learn [--init|--remember text|--feedback text|--list|--export|--query text|--explain|--recall query|--backup|--redact|--verify|--diff|--restore|--restore-backups [--prune]|--import|--audit [--fix]|--curate|--stats|--usage|--signals [--strict]|--agent-backlog [--strict]|--propose-skills [--min-evidence N] [--review-file path] [--review-check|--apply-plan] [--strict]|--eval-template|--eval [--strict]|--forget id|--clear] [--json|--report|--patch|--review-template] [--out file]`, `check <artifact.md|--stdin|--examples> [--route id|--all-routes] [--issues-only] [--strict] [--learn [--yes] [--learning-file path]] [--json]`, `workspace [--root path] [--learning-file path] [--learning-usage path] [--learning-eval path] [--strict] [--json]`, `site <workspace.json|--stdin> [--strict] [--json|--mcp-check [--probes]|--mcp-plan [--probes] [--json]|--next-actions [--json]|--graph|--tasks|--bundle|--report|--prompts|--prompt id [--task id]] [--out file] | site <bundle-dir> --bundle-check [--json] | site <bundle-dir> --bundle-compare other-bundle-dir [--json] | site <bundle-dir> --bundle-handoff [--task id] [--json] | site <bundle-dir> --bundle-repair [--yes] [--json] [--out file] | site --init --name name --live-url url [--next-actions] [--out file] | site --init --name name --live-url url --bundle --out dir | site --from-intake file.md|--stdin [--json|--next-actions [--json]|--tasks|--bundle [--tasks] --out dir] [--out file] | site --intake-template [--language en|ko] [--json] [--out file] | site --sample [--out file] | site --prompt-list [--json]`, `examples [query|--route id] [--limit N] [--json]`, `search <query|--eval-template|--eval> [--dir kind] [--limit N] [--ranked] [--embeddings [--provider "cmd args"]] [--strict] [--json]`, `index [--build|--status|--verify] [--json] [--embeddings [--provider "cmd args"]]`, `show <file[:line]> [--lines N:M] [--context N] [--json]`, `audit [--strict] [--quiet] [--json]`, `doctor [--strict] [--json] [--fix]`, `mcp`, `version [--json]`, `help [command|--json]`.
|
|
79
79
|
|
|
80
80
|
See [`docs/DISTRIBUTION.md`](docs/DISTRIBUTION.md) for the full distribution guide.
|
|
81
81
|
|
|
@@ -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.
|
|
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.60.0**: the Agent SDK (`@design-ai/cli/sdk`) with shipped TypeScript types and the opt-in `learn.*` local-write namespace, 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
|
|
package/cli/commands/help.mjs
CHANGED
|
@@ -33,7 +33,7 @@ export const HELP_COMMANDS = [
|
|
|
33
33
|
{ topic: "uninstall", usage: "uninstall [--json]", description: "Remove symlinks (keeps source files)" },
|
|
34
34
|
{ topic: "status", usage: "status [--json]", description: "Show what's installed (use VERBOSE=1 for full list)" },
|
|
35
35
|
{ topic: "list", usage: "list [skills|commands|agents] [--json]", description: "List catalog from the plugin manifest" },
|
|
36
|
-
{ topic: "search", usage: "search <query> [--dir kind] [--limit N] [--ranked] [--embeddings [--provider \"cmd args\"]] [--json]", description: "Search the local markdown corpus" },
|
|
36
|
+
{ topic: "search", usage: "search <query|--eval-template|--eval> [--dir kind] [--limit N] [--ranked] [--embeddings [--provider \"cmd args\"]] [--strict] [--json]", description: "Search the local markdown corpus and ranked-search eval checkpoints" },
|
|
37
37
|
{ topic: "index", usage: "index [--build|--status|--verify] [--json] [--embeddings [--provider \"cmd args\"]]", description: "Build, inspect, and verify the local retrieval index" },
|
|
38
38
|
{ topic: "show", usage: "show <file[:line]> [--lines N:M] [--context N] [--json]", description: "Print a corpus file or line range" },
|
|
39
39
|
{ topic: "route", usage: "route <brief|--from-file file|--stdin|--list|--eval-template|--eval> [--limit N]", description: "Recommend commands, skills, knowledge, and route eval checkpoints" },
|
package/cli/commands/search.mjs
CHANGED
|
@@ -2,13 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
import { DESIGN_AI_HOME } from "../lib/paths.mjs";
|
|
4
4
|
import { dim, header, info, warn } from "../lib/log.mjs";
|
|
5
|
+
import { resolveBriefInput } from "../lib/brief.mjs";
|
|
5
6
|
import { configuredEmbeddingProvider, defaultConfigFile } from "../lib/local-config.mjs";
|
|
7
|
+
import { buildSearchEvalTemplate, searchEvalReport } from "../lib/search-eval.mjs";
|
|
6
8
|
import { embeddingRerankSearch, rankedSearchCorpus } from "../lib/search-ranked.mjs";
|
|
7
9
|
import { formatSearchJson, parseSearchArgs, searchCorpus } from "../lib/search.mjs";
|
|
8
10
|
|
|
9
11
|
function printHelp() {
|
|
10
|
-
console.log("Usage: design-ai search <query> [--limit N] [--dir kind] [--ranked] [--embeddings [--provider \"cmd args\"]] [--json]
|
|
11
|
-
console.log("
|
|
12
|
+
console.log("Usage: design-ai search <query> [--limit N] [--dir kind] [--ranked] [--embeddings [--provider \"cmd args\"]] [--json]");
|
|
13
|
+
console.log(" design-ai search --eval-template [--json]");
|
|
14
|
+
console.log(" design-ai search --eval --from-file search-eval.json [--strict] [--json]");
|
|
15
|
+
console.log(" cat search-eval.json | design-ai search --eval --stdin [--strict] [--json]\n");
|
|
16
|
+
console.log("Searches markdown files across knowledge, examples, skills, docs, agents, and commands.");
|
|
17
|
+
console.log("Search eval is read-only and checks deterministic ranked-search fixtures.\n");
|
|
12
18
|
console.log("Options:");
|
|
13
19
|
console.log(" --limit N Maximum hits to return, 1-500. Default: 20");
|
|
14
20
|
console.log(" --dir kind Restrict to one corpus directory. Repeatable.");
|
|
@@ -18,7 +24,18 @@ function printHelp() {
|
|
|
18
24
|
console.log(" Requires --ranked and a configured provider; falls back to the");
|
|
19
25
|
console.log(" lexical ranking (with a notice) if no provider or sidecar is available");
|
|
20
26
|
console.log(" --provider \"cmd args\" Embedding provider command for this invocation (overrides config)");
|
|
27
|
+
console.log(" --from-file file Read ranked-search eval JSON from a file (with --eval)");
|
|
28
|
+
console.log(" --stdin Read ranked-search eval JSON from standard input (with --eval)");
|
|
29
|
+
console.log(" --eval-template Generate a runnable ranked-search eval checkpoint JSON template");
|
|
30
|
+
console.log(" --eval Run deterministic ranked-search checkpoint cases");
|
|
31
|
+
console.log(" --strict With --eval, exit non-zero on warning or failure");
|
|
21
32
|
console.log(" --json Emit machine-readable results");
|
|
33
|
+
console.log("");
|
|
34
|
+
console.log("Examples:");
|
|
35
|
+
console.log(" design-ai search Pretendard");
|
|
36
|
+
console.log(" design-ai search \"button component\" --ranked");
|
|
37
|
+
console.log(" design-ai search --eval-template --json > search-eval.json");
|
|
38
|
+
console.log(" design-ai search --eval --from-file search-eval.json --strict --json");
|
|
22
39
|
}
|
|
23
40
|
|
|
24
41
|
function parseProviderFlag(value) {
|
|
@@ -45,6 +62,37 @@ function printRankedHits(query, hits, backend) {
|
|
|
45
62
|
info(`Hits: ${hits.length}`);
|
|
46
63
|
}
|
|
47
64
|
|
|
65
|
+
function printSearchEvalTemplate(template) {
|
|
66
|
+
header("design-ai search", "Ranked-search eval checkpoint template");
|
|
67
|
+
info(`Source: ${DESIGN_AI_HOME}`);
|
|
68
|
+
info(`Cases: ${template.cases.length}`);
|
|
69
|
+
console.log();
|
|
70
|
+
console.log("Write the JSON below to a file, edit cases if needed, then run:");
|
|
71
|
+
console.log("design-ai search --eval --from-file search-eval.json --strict");
|
|
72
|
+
console.log();
|
|
73
|
+
console.log(formatSearchJson(template));
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function printSearchEvalReport(report) {
|
|
77
|
+
header("design-ai search", "Ranked-search eval report");
|
|
78
|
+
info(`Source: ${report.source}`);
|
|
79
|
+
info(`Status: ${report.status}`);
|
|
80
|
+
info(`Cases: ${report.summary.total} (${report.summary.pass} pass, ${report.summary.warn} warn, ${report.summary.fail} fail)`);
|
|
81
|
+
console.log();
|
|
82
|
+
|
|
83
|
+
for (const result of report.cases) {
|
|
84
|
+
const top = result.topRelPath || "none";
|
|
85
|
+
console.log(`${result.status.toUpperCase()} ${result.id}`);
|
|
86
|
+
console.log(` query: ${result.query}`);
|
|
87
|
+
console.log(` top: ${top} ${dim(`(hits ${result.hitCount})`)}`);
|
|
88
|
+
if (result.matchedTokens.length > 0) {
|
|
89
|
+
console.log(` matched: ${result.matchedTokens.join(", ")}`);
|
|
90
|
+
}
|
|
91
|
+
console.log(` result: ${result.message}`);
|
|
92
|
+
console.log();
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
48
96
|
export async function runSearch(args) {
|
|
49
97
|
const parsed = parseSearchArgs(args);
|
|
50
98
|
if (parsed.help) {
|
|
@@ -52,6 +100,37 @@ export async function runSearch(args) {
|
|
|
52
100
|
return;
|
|
53
101
|
}
|
|
54
102
|
|
|
103
|
+
if (parsed.evalTemplate) {
|
|
104
|
+
const template = buildSearchEvalTemplate({ sourceRoot: DESIGN_AI_HOME });
|
|
105
|
+
if (parsed.json) {
|
|
106
|
+
console.log(formatSearchJson(template));
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
printSearchEvalTemplate(template);
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (parsed.eval) {
|
|
114
|
+
const evalText = resolveBriefInput(parsed);
|
|
115
|
+
const report = searchEvalReport({
|
|
116
|
+
evalText,
|
|
117
|
+
source: parsed.fromFile || "stdin",
|
|
118
|
+
sourceRoot: DESIGN_AI_HOME,
|
|
119
|
+
limit: parsed.limit,
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
if (parsed.json) {
|
|
123
|
+
console.log(formatSearchJson(report));
|
|
124
|
+
} else {
|
|
125
|
+
printSearchEvalReport(report);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (parsed.strict && report.status !== "pass") {
|
|
129
|
+
process.exitCode = 1;
|
|
130
|
+
}
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
|
|
55
134
|
if (parsed.embeddings && !parsed.ranked) {
|
|
56
135
|
throw new Error("--embeddings requires --ranked");
|
|
57
136
|
}
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
// Ranked-search eval checkpoint generation and reports for `design-ai search`.
|
|
2
|
+
// Mirrors cli/lib/route.mjs's eval-checkpoint pattern (docs/AI-LEARNING-PHASE2.md
|
|
3
|
+
// FU-3): runs hand-authored / generated cases through the shipped deterministic
|
|
4
|
+
// ranked search path (rankedSearchCorpus, BM25-style lexical scorer) and reports
|
|
5
|
+
// pass/warn/fail. This is the standing evidence artifact for any future decision
|
|
6
|
+
// to promote `--ranked` to the default `search` behavior.
|
|
7
|
+
|
|
8
|
+
import { rankedSearchCorpus } from "./search-ranked.mjs";
|
|
9
|
+
import { DEFAULT_SEARCH_DIRS } from "./search.mjs";
|
|
10
|
+
|
|
11
|
+
export const SEARCH_EVAL_VERSION = 1;
|
|
12
|
+
// Default per-case result limit when a case does not specify its own `limit`.
|
|
13
|
+
// Kept small and eval-appropriate (route's default is 3; search corpus hits are
|
|
14
|
+
// noisier, so 10 gives enough headroom for `minHits` assertions without needing
|
|
15
|
+
// a large recall window).
|
|
16
|
+
export const SEARCH_EVAL_DEFAULT_LIMIT = 10;
|
|
17
|
+
|
|
18
|
+
function isoTimestamp(now = new Date()) {
|
|
19
|
+
return (now instanceof Date ? now : new Date(now)).toISOString();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function buildSearchEvalTemplate({ sourceRoot, generatedAt = new Date() } = {}) {
|
|
23
|
+
return {
|
|
24
|
+
version: SEARCH_EVAL_VERSION,
|
|
25
|
+
generatedAt: isoTimestamp(generatedAt),
|
|
26
|
+
description: "Deterministic ranked-search checkpoints for design-ai's BM25-style lexical retrieval.",
|
|
27
|
+
cases: [
|
|
28
|
+
{
|
|
29
|
+
id: "korean-button-stem",
|
|
30
|
+
query: "버튼",
|
|
31
|
+
minHits: 1,
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
id: "korean-button-particle",
|
|
35
|
+
query: "버튼을",
|
|
36
|
+
minHits: 1,
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
id: "korean-accessibility-particle",
|
|
40
|
+
query: "접근성이",
|
|
41
|
+
minHits: 1,
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
id: "accessibility-english",
|
|
45
|
+
query: "accessibility",
|
|
46
|
+
minHits: 5,
|
|
47
|
+
matchedTokenIncludes: ["accessibility"],
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
id: "button-component-spec-english",
|
|
51
|
+
query: "button component spec",
|
|
52
|
+
minHits: 5,
|
|
53
|
+
matchedTokenIncludes: ["button", "component", "spec"],
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
id: "color-palette-english",
|
|
57
|
+
query: "color palette",
|
|
58
|
+
expectRelPathIn: ["commands/palette-from-brand.md"],
|
|
59
|
+
minHits: 5,
|
|
60
|
+
matchedTokenIncludes: ["color", "palette"],
|
|
61
|
+
},
|
|
62
|
+
],
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function searchEvalStatus(counts) {
|
|
67
|
+
if (counts.fail > 0) return "fail";
|
|
68
|
+
if (counts.warn > 0) return "warn";
|
|
69
|
+
return "pass";
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function normalizeSearchEvalPayload(evalText, source = "search-eval.json") {
|
|
73
|
+
let payload;
|
|
74
|
+
try {
|
|
75
|
+
payload = JSON.parse(evalText);
|
|
76
|
+
} catch (err) {
|
|
77
|
+
throw new Error(`Could not parse search eval JSON from ${source}: ${err.message}`);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
|
|
81
|
+
throw new Error("Search eval payload must be a JSON object");
|
|
82
|
+
}
|
|
83
|
+
if (payload.version !== SEARCH_EVAL_VERSION) {
|
|
84
|
+
throw new Error(`Search eval payload version must be ${SEARCH_EVAL_VERSION}`);
|
|
85
|
+
}
|
|
86
|
+
if (!Array.isArray(payload.cases)) {
|
|
87
|
+
throw new Error("Search eval payload must include a cases array");
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return payload;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function normalizeStringList(value, { field, id }) {
|
|
94
|
+
if (value === undefined || value === null) return [];
|
|
95
|
+
if (!Array.isArray(value)) {
|
|
96
|
+
throw new Error(`Search eval case ${id} field ${field} must be an array`);
|
|
97
|
+
}
|
|
98
|
+
return value.map((item) => String(item || "").trim()).filter(Boolean);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function normalizeSearchEvalCase(rawCase, index) {
|
|
102
|
+
if (!rawCase || typeof rawCase !== "object" || Array.isArray(rawCase)) {
|
|
103
|
+
throw new Error(`Search eval case ${index + 1} must be a JSON object`);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const id = String(rawCase.id || `case-${index + 1}`).trim();
|
|
107
|
+
if (!id) throw new Error(`Search eval case ${index + 1} is missing id`);
|
|
108
|
+
|
|
109
|
+
const query = String(rawCase.query || "").trim();
|
|
110
|
+
if (!query) throw new Error(`Search eval case ${id} is missing query`);
|
|
111
|
+
|
|
112
|
+
const dirs = normalizeStringList(rawCase.dirs, { field: "dirs", id });
|
|
113
|
+
for (const dir of dirs) {
|
|
114
|
+
if (!DEFAULT_SEARCH_DIRS.includes(dir)) {
|
|
115
|
+
throw new Error(`Search eval case ${id} field dirs has invalid value: ${dir}`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const limit = rawCase.limit === undefined || rawCase.limit === null
|
|
120
|
+
? SEARCH_EVAL_DEFAULT_LIMIT
|
|
121
|
+
: Number(rawCase.limit);
|
|
122
|
+
if (!Number.isInteger(limit) || limit < 1 || limit > 500) {
|
|
123
|
+
throw new Error(`Search eval case ${id} limit must be an integer from 1 to 500`);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const expectRelPathIn = normalizeStringList(rawCase.expectRelPathIn, { field: "expectRelPathIn", id });
|
|
127
|
+
const matchedTokenIncludes = normalizeStringList(rawCase.matchedTokenIncludes, { field: "matchedTokenIncludes", id })
|
|
128
|
+
.map((token) => token.toLowerCase());
|
|
129
|
+
|
|
130
|
+
let minHits;
|
|
131
|
+
if (rawCase.minHits === undefined || rawCase.minHits === null) {
|
|
132
|
+
minHits = undefined;
|
|
133
|
+
} else {
|
|
134
|
+
minHits = Number(rawCase.minHits);
|
|
135
|
+
if (!Number.isInteger(minHits) || minHits < 0) {
|
|
136
|
+
throw new Error(`Search eval case ${id} field minHits must be a non-negative integer`);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return {
|
|
141
|
+
id,
|
|
142
|
+
query,
|
|
143
|
+
dirs: dirs.length > 0 ? dirs : DEFAULT_SEARCH_DIRS,
|
|
144
|
+
limit,
|
|
145
|
+
expectRelPathIn,
|
|
146
|
+
minHits,
|
|
147
|
+
matchedTokenIncludes,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export function evaluateSearchEvalCase(testCase, sourceRoot, defaultLimit) {
|
|
152
|
+
const limit = testCase.limit || defaultLimit || SEARCH_EVAL_DEFAULT_LIMIT;
|
|
153
|
+
const { hits } = rankedSearchCorpus({
|
|
154
|
+
query: testCase.query,
|
|
155
|
+
designAiPath: sourceRoot,
|
|
156
|
+
dirs: testCase.dirs,
|
|
157
|
+
limit,
|
|
158
|
+
});
|
|
159
|
+
const topHit = hits[0] || null;
|
|
160
|
+
const topRelPath = topHit?.relPath || "";
|
|
161
|
+
const topMatchedTokens = (topHit?.matchedTokens || []).map((token) => token.toLowerCase());
|
|
162
|
+
|
|
163
|
+
const hasAssertions = testCase.expectRelPathIn.length > 0
|
|
164
|
+
|| testCase.minHits !== undefined
|
|
165
|
+
|| testCase.matchedTokenIncludes.length > 0;
|
|
166
|
+
|
|
167
|
+
let status = "pass";
|
|
168
|
+
let message = "All checkpoint expectations were satisfied.";
|
|
169
|
+
|
|
170
|
+
if (!hasAssertions) {
|
|
171
|
+
status = "warn";
|
|
172
|
+
message = "Case has no expectRelPathIn, minHits, or matchedTokenIncludes assertion.";
|
|
173
|
+
} else if (testCase.expectRelPathIn.length > 0 && !testCase.expectRelPathIn.includes(topRelPath)) {
|
|
174
|
+
status = "fail";
|
|
175
|
+
message = topRelPath
|
|
176
|
+
? `Expected top hit in [${testCase.expectRelPathIn.join(", ")}], but top hit was ${topRelPath}.`
|
|
177
|
+
: `Expected top hit in [${testCase.expectRelPathIn.join(", ")}], but there were no hits.`;
|
|
178
|
+
} else if (testCase.minHits !== undefined && hits.length < testCase.minHits) {
|
|
179
|
+
status = "fail";
|
|
180
|
+
message = `Expected at least ${testCase.minHits} hit(s), got ${hits.length}.`;
|
|
181
|
+
} else if (
|
|
182
|
+
testCase.matchedTokenIncludes.length > 0
|
|
183
|
+
&& !testCase.matchedTokenIncludes.every((token) => topMatchedTokens.includes(token))
|
|
184
|
+
) {
|
|
185
|
+
status = "fail";
|
|
186
|
+
const missing = testCase.matchedTokenIncludes.filter((token) => !topMatchedTokens.includes(token));
|
|
187
|
+
message = topHit
|
|
188
|
+
? `Top hit ${topRelPath} did not match expected token(s): ${missing.join(", ")}.`
|
|
189
|
+
: `Expected matched tokens [${missing.join(", ")}], but there were no hits.`;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return {
|
|
193
|
+
id: testCase.id,
|
|
194
|
+
status,
|
|
195
|
+
message,
|
|
196
|
+
query: testCase.query,
|
|
197
|
+
dirs: testCase.dirs,
|
|
198
|
+
limit,
|
|
199
|
+
topRelPath,
|
|
200
|
+
hitCount: hits.length,
|
|
201
|
+
matchedTokens: topHit?.matchedTokens || [],
|
|
202
|
+
expectRelPathIn: testCase.expectRelPathIn,
|
|
203
|
+
minHits: testCase.minHits ?? null,
|
|
204
|
+
matchedTokenIncludes: testCase.matchedTokenIncludes,
|
|
205
|
+
hits,
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
export function searchEvalReport({
|
|
210
|
+
evalText,
|
|
211
|
+
source = "search-eval.json",
|
|
212
|
+
sourceRoot,
|
|
213
|
+
limit = SEARCH_EVAL_DEFAULT_LIMIT,
|
|
214
|
+
generatedAt = new Date(),
|
|
215
|
+
}) {
|
|
216
|
+
const payload = normalizeSearchEvalPayload(evalText, source);
|
|
217
|
+
const normalizedCases = payload.cases.map((testCase, index) => normalizeSearchEvalCase(testCase, index));
|
|
218
|
+
const results = normalizedCases.map((testCase) => evaluateSearchEvalCase(testCase, sourceRoot, limit));
|
|
219
|
+
const counts = results.reduce(
|
|
220
|
+
(acc, result) => ({
|
|
221
|
+
...acc,
|
|
222
|
+
[result.status]: acc[result.status] + 1,
|
|
223
|
+
}),
|
|
224
|
+
{ pass: 0, warn: 0, fail: 0 },
|
|
225
|
+
);
|
|
226
|
+
const summary = {
|
|
227
|
+
total: results.length,
|
|
228
|
+
pass: counts.pass,
|
|
229
|
+
warn: counts.warn,
|
|
230
|
+
fail: counts.fail,
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
return {
|
|
234
|
+
evalVersion: payload.version,
|
|
235
|
+
source,
|
|
236
|
+
generatedAt: isoTimestamp(generatedAt),
|
|
237
|
+
status: searchEvalStatus(counts),
|
|
238
|
+
summary,
|
|
239
|
+
cases: results,
|
|
240
|
+
};
|
|
241
|
+
}
|
package/cli/lib/search.mjs
CHANGED
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
} from "node:fs";
|
|
8
8
|
import path from "node:path";
|
|
9
9
|
|
|
10
|
+
import { parseBriefSourceFlag } from "./brief.mjs";
|
|
10
11
|
import { expectedValueMessage, unknownOptionMessage } from "./suggest.mjs";
|
|
11
12
|
|
|
12
13
|
export const DEFAULT_SEARCH_DIRS = [
|
|
@@ -20,7 +21,21 @@ export const DEFAULT_SEARCH_DIRS = [
|
|
|
20
21
|
|
|
21
22
|
const PREVIEW_LEN = 120;
|
|
22
23
|
const PREVIEW_BEFORE = 50;
|
|
23
|
-
const SEARCH_OPTIONS = [
|
|
24
|
+
const SEARCH_OPTIONS = [
|
|
25
|
+
"-h",
|
|
26
|
+
"--help",
|
|
27
|
+
"--json",
|
|
28
|
+
"--limit",
|
|
29
|
+
"--dir",
|
|
30
|
+
"--ranked",
|
|
31
|
+
"--embeddings",
|
|
32
|
+
"--provider",
|
|
33
|
+
"--eval-template",
|
|
34
|
+
"--eval",
|
|
35
|
+
"--strict",
|
|
36
|
+
"--from-file",
|
|
37
|
+
"--stdin",
|
|
38
|
+
];
|
|
24
39
|
|
|
25
40
|
function exists(p) {
|
|
26
41
|
try {
|
|
@@ -118,11 +133,17 @@ export function parseSearchArgs(args) {
|
|
|
118
133
|
ranked: false,
|
|
119
134
|
embeddings: false,
|
|
120
135
|
provider: "",
|
|
136
|
+
evalTemplate: false,
|
|
137
|
+
eval: false,
|
|
138
|
+
strict: false,
|
|
139
|
+
fromFile: "",
|
|
140
|
+
stdin: false,
|
|
121
141
|
help: false,
|
|
122
142
|
};
|
|
123
143
|
|
|
124
144
|
for (let i = 0; i < args.length; i += 1) {
|
|
125
145
|
const arg = args[i];
|
|
146
|
+
out.index = i;
|
|
126
147
|
if (arg === "-h" || arg === "--help") {
|
|
127
148
|
out.help = true;
|
|
128
149
|
} else if (arg === "--json") {
|
|
@@ -131,9 +152,17 @@ export function parseSearchArgs(args) {
|
|
|
131
152
|
out.ranked = true;
|
|
132
153
|
} else if (arg === "--embeddings") {
|
|
133
154
|
out.embeddings = true;
|
|
155
|
+
} else if (arg === "--eval-template") {
|
|
156
|
+
out.evalTemplate = true;
|
|
157
|
+
} else if (arg === "--eval") {
|
|
158
|
+
out.eval = true;
|
|
159
|
+
} else if (arg === "--strict") {
|
|
160
|
+
out.strict = true;
|
|
134
161
|
} else if (arg === "--provider") {
|
|
135
162
|
out.provider = args[i + 1] || "";
|
|
136
163
|
i += 1;
|
|
164
|
+
} else if (parseBriefSourceFlag(args, out)) {
|
|
165
|
+
i = out.index;
|
|
137
166
|
} else if (arg === "--limit") {
|
|
138
167
|
const next = args[i + 1];
|
|
139
168
|
const limit = Number(next);
|
|
@@ -156,8 +185,34 @@ export function parseSearchArgs(args) {
|
|
|
156
185
|
}
|
|
157
186
|
}
|
|
158
187
|
|
|
188
|
+
if (out.eval && out.evalTemplate) {
|
|
189
|
+
throw new Error("Choose either --eval-template or --eval, not both");
|
|
190
|
+
}
|
|
191
|
+
if (out.strict && !out.eval) {
|
|
192
|
+
throw new Error("--strict can only be used with --eval");
|
|
193
|
+
}
|
|
194
|
+
if (
|
|
195
|
+
out.evalTemplate
|
|
196
|
+
&& (out.fromFile || out.stdin || out.queryParts.length > 0 || out.ranked || out.embeddings || out.provider || out.dirs.length > 0)
|
|
197
|
+
) {
|
|
198
|
+
throw new Error("--eval-template cannot be combined with a query, --from-file, --stdin, --ranked, --embeddings, --provider, or --dir");
|
|
199
|
+
}
|
|
200
|
+
if (out.eval && (!out.fromFile && !out.stdin)) {
|
|
201
|
+
throw new Error("--eval requires --from-file or --stdin");
|
|
202
|
+
}
|
|
203
|
+
if ((out.fromFile || out.stdin) && !out.eval) {
|
|
204
|
+
throw new Error("--from-file and --stdin require --eval");
|
|
205
|
+
}
|
|
206
|
+
if (
|
|
207
|
+
out.eval
|
|
208
|
+
&& (out.queryParts.length > 0 || out.ranked || out.embeddings || out.provider || out.dirs.length > 0)
|
|
209
|
+
) {
|
|
210
|
+
throw new Error("--eval cannot be combined with an inline query, --ranked, --embeddings, --provider, or --dir");
|
|
211
|
+
}
|
|
212
|
+
|
|
159
213
|
return {
|
|
160
214
|
...out,
|
|
215
|
+
index: undefined,
|
|
161
216
|
query: out.queryParts.join(" ").trim(),
|
|
162
217
|
dirs: out.dirs.length > 0 ? out.dirs : DEFAULT_SEARCH_DIRS,
|
|
163
218
|
};
|