project-librarian 0.4.1 → 0.4.2

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/README.ko.md CHANGED
@@ -75,17 +75,19 @@ npm run benchmark:release:preview
75
75
  npm run benchmark:release -- --allow-codex-run
76
76
  ```
77
77
 
78
+ 실측 릴리스 실행은 stderr에 `[benchmark:progress]` 줄을 스트리밍하여 시나리오 수, 예상 Codex 실행 총량, 현재 실행 순번, 단계, 프롬프트 ID, 종료 상태, 경과 시간, 원시 JSONL 경로를 보여줍니다. stdout은 최종 JSON 결과 전용으로 유지됩니다.
79
+
78
80
  ### 위키 트랙 (계획 문서 라우팅)
79
81
 
80
82
  비용 가중 토큰, Project Librarian 대 대조군:
81
83
 
82
84
  | 규모 | decision_lookup | aggregation | multi_session (2번째 세션) |
83
85
  | --- | --- | --- | --- |
84
- | 소형 | 43.8% 적음 | 144.5% 많음 | 5.4% 많음 |
85
- | 중형 | 53.4% 적음 | 4.4% 적음 | 68.5% 적음 |
86
- | 대형 | 71.6% 적음* | 12.8% 적음 | 66.0% 적음 |
86
+ | 소형 | 14.4% 적음 | 81.0% 많음 | 22.0% 적음 |
87
+ | 중형 | 52.0% 적음 | 19.0% 적음 | 54.1% 적음 |
88
+ | 대형 | 71.1% 적음 | 29.0% 적음 | 71.8% 적음 |
87
89
 
88
- 최신 합성 위키 트랙 릴리스 후보는 2026-06-16에 `gpt-5.5`로 측정했으며, 42개 시나리오를 각 3회 측정하고 1회 예열했습니다. 전체 주장 게이트는 **실패**했습니다. 42개 41개 시나리오는 정확성 검사를 통과했지만, Project Librarian이 없는 대형 `decision_lookup` 대조군이 3회 2회만 정답이었습니다. 실패한 대조군 실행은 날짜가 붙은 히스토리 파일에서 `2026-05-04`를 골랐고, 기대값은 결정 로그의 최신 벤치마크 근거 결정인 `2026-06-10`이었습니다. 따라서 표는 공개 릴리스 주장이 아니라 진단 근거로만 보아야 하며, 깨끗한 릴리스 실행이 주장 게이트를 통과해야 공개 주장으로 쓸 수 있습니다. 숨기지 않는 한계도 그대로 남습니다. 소형 `aggregation`은 위키를 켰을 때 훨씬 비싸고, 소형 `multi_session`도 약간 비싸며, `aggregation`은 토큰 비용이 줄어드는 규모에서도 실행 시간은 매번 더 느립니다.
90
+ 최신 합성 위키 트랙 릴리스 후보는 2026-06-19에 `gpt-5.5`로 측정했으며, 42개 시나리오를 각 3회 측정하고 1회 예열했습니다. 전체 주장 게이트는 **통과**했습니다. 42/42시나리오가 정확성 검사를 통과했고, 42개 시나리오가 모두 claimable이었으며, 모든 corpus 게이트가 3회 실행 최소 조건을 충족했습니다. 릴리스 주장은 여전히 합성 위키 라우팅 트랙과 표기된 작업 유형에 한정됩니다. 코드 그래프 동작, 실제 저장소, 모든 에이전트 표면, 모든 질문 형태에 대한 주장이 아닙니다. 숨기지 않는 한계도 그대로 남습니다. 소형 `aggregation`은 위키를 켰을 때 여전히 81.0% 비싸고, 전체 보고서의 소형 `release_policy`도 9.4% 비싸며, `aggregation`은 토큰 비용이 줄어드는 규모에서도 실행 시간은 매번 더 느립니다.
89
91
 
90
92
  ### 코드 그래프 트랙 (코드 근거 인덱스, 실제 저장소)
91
93
 
@@ -143,6 +145,8 @@ npx project-librarian install-skill --scope project --agents all
143
145
 
144
146
  `--agents`는 `codex,claude,cursor,gemini`처럼 쉼표로 구분한 값도 받습니다. `all`은 지원하는 모든 에이전트를 대상으로 합니다. `--scope`는 `user` 또는 `project`를 받습니다.
145
147
 
148
+ 프로젝트 설정/갱신 실행기도 `--agents`를 받습니다. 새로 설정할 때는 프로젝트 범위 Project Librarian 스킬 설치가 없을 때만 지원하는 모든 에이전트 표면을 기본으로 만듭니다. 저장소에 이미 `.codex/skills/project-librarian/`, `.claude/skills/project-librarian/` 같은 프로젝트 범위 스킬이 있으면 첫 설정도 그 설치된 에이전트 집합을 기본값으로 사용합니다. 마이그레이션 없는 기존 설정 갱신은 저장소에 이미 있는 에이전트 표면만 보존해서 갱신합니다. 따라서 Codex와 Claude 파일만 있던 저장소는 일반 갱신만으로 Cursor나 Gemini 파일이 새로 생기지 않습니다. 새 표면을 의도적으로 추가하려면 `project-librarian update --agents cursor` 또는 `project-librarian update --agents all`처럼 명시합니다. 목록에 없는 표면을 삭제하지는 않습니다.
149
+
146
150
  ## 실행 경로
147
151
 
148
152
  이 경로들은 주로 에이전트와 자동화를 위한 참조입니다. 설치 후 에이전트는 `npx`가 아니라 설치된 로컬 복사본을 `node`로 실행해야 합니다. 이렇게 하면 제한된 에이전트 환경에서 네트워크 접근과 버전이 고정되지 않은 패키지 실행을 피할 수 있습니다.
@@ -168,6 +172,7 @@ npx project-librarian install-skill --scope project --agents all
168
172
  | --- | --- | --- |
169
173
  | 위키 생성 또는 갱신 | "Project Librarian으로 이 저장소의 계획 위키를 설정하거나 갱신해줘." | `[init]` |
170
174
  | 마이그레이션 없이 기존 설정 갱신 | "위키를 마이그레이션하지 말고 이 저장소의 Project Librarian 설정을 갱신해줘." | `update` |
175
+ | 기존 설정에 특정 에이전트 표면 추가 | "위키 마이그레이션 없이 Cursor Project Librarian 표면을 추가해줘." | `update --agents cursor` |
171
176
  | 기존 문서/위키 마이그레이션 | "Project Librarian으로 기존 docs/wiki 내용을 마이그레이션해줘." | `--migrate` |
172
177
  | 생성된 설정 검증 | "Project Librarian 검증을 실행해줘." | `--lint` |
173
178
  | 링크와 문서 품질 점검 | "Project Librarian 진단을 실행해줘." | `--doctor` |
@@ -188,7 +193,7 @@ npx project-librarian install-skill --scope project --agents all
188
193
  | 증분 갱신 요구 | "Project Librarian 코드 근거 인덱스를 증분 갱신해줘." | `--code-index --incremental` |
189
194
  | 전체 재생성 강제 | "Project Librarian 코드 근거 인덱스를 전체 재생성해줘." | `--code-index --code-index-full` |
190
195
  | 선택적 Tree-sitter 백엔드 사용 | "Tree-sitter 파서로 Project Librarian 코드 근거를 만들어줘." | `--code-index --code-parser tree-sitter` |
191
- | 캐시 호환성 진단 | "Project Librarian 코드 근거 캐시 health를 확인해줘." | `--code-index-health` |
196
+ | 캐시 호환성 진단 | "Project Librarian 코드 근거 캐시 상태와 호환성을 진단해줘." | `--code-index-health` |
192
197
  | 캐시 상태 보기 | "Project Librarian 코드 근거 상태를 보여줘." | `--code-status` |
193
198
  | 인덱싱된 파일 목록 | "Project Librarian 코드 근거에 인덱싱된 파일을 보여줘." | `--code-files` |
194
199
  | 아키텍처/소유권 보고서 출력 | "Project Librarian 코드 보고서를 보여줘." | `--code-report` |
@@ -202,6 +207,8 @@ npx project-librarian install-skill --scope project --agents all
202
207
 
203
208
  ## 설치되는 파일
204
209
 
210
+ 새로 설정할 때는 `--agents`로 좁히지 않았고 프로젝트 범위 Project Librarian 스킬 설치도 없을 때 아래 지원 에이전트 표면을 설치합니다. 마이그레이션 없는 기존 설정 갱신은 기본적으로 감지된 표면만 보존해서 갱신하고, 공통 위키/git 훅 파일은 계속 갱신합니다.
211
+
205
212
  프로젝트 지침 파일:
206
213
 
207
214
  - `AGENTS.md`
@@ -369,6 +376,9 @@ npm install
369
376
  npm run typecheck
370
377
  npm run build
371
378
  npm test
379
+ npm run test:coverage
380
+ npm run benchmark:llm:raw-audit
381
+ npm run benchmark:llm:delta-analysis
372
382
  npm run benchmark:claim-ledger
373
383
  npm run release:check
374
384
  npm pack --dry-run
@@ -376,19 +386,34 @@ npm pack --dry-run
376
386
 
377
387
  `src/` 아래 TypeScript를 수정할 때는 커밋 전에 빌드해 `dist/`를 최신 상태로 유지하세요.
378
388
 
379
- `npm run release:check`는 로컬 전용 관리자 게이트입니다. 테스트, 벤치마크 파서 smoke, 벤치마크 release preview, 벤치마크 claim ledger 분류, package dry-run 검사, dist 실행 가능 여부, README 벤치마크 claim 경계 문구를 확인합니다. publish하지 않고 measured Codex 벤치마크도 실행하지 않습니다.
389
+ `npm run test:coverage`는 Node 내장 test coverage에 보수적인 line, branch, function threshold를 적용하므로 coverage를 단순 보고서가 아니라 회귀 게이트로 사용합니다.
390
+
391
+ `npm run release:check`는 로컬 전용 관리자 게이트입니다. 테스트, Node 내장 coverage, 벤치마크 파서 smoke, real-corpus 오프라인 데모, 벤치마크 release preview, 벤치마크 claim ledger 분류, raw 보관 상태 감사, package dry-run 검사, dist 실행 가능 여부, README 벤치마크 claim 경계 문구를 확인합니다. publish하지 않고 raw 벤치마크 산출물을 삭제하지 않으며 measured Codex 벤치마크도 실행하지 않습니다.
392
+
393
+ `release:check` 통과는 런타임 보증이 아니라 재현 가능한 릴리스 준비 근거로 봐야 합니다. 현재 checkout에서 위 로컬 게이트를 통과했음을 증명하며, package dry run이 예상 publish 경계(`agents/`, `dist/`, `LICENSE`, `README.md`, `README.ko.md`, `SKILL.md`) 안에 머물고 소스 파일, 테스트, 저장소 로컬 위키/워크플로 상태, raw 벤치마크 출력, 로컬 캐시를 제외하는지도 확인합니다.
394
+
395
+ 배포는 GitHub Release가 published 상태가 된 뒤 `.github/workflows/publish.yml`에서 처리합니다. 이 워크플로는 GitHub OIDC 기반 npm trusted publishing(`id-token: write`)과 `npm publish --access public`을 사용하므로 npm provenance가 자동 생성됩니다. `NODE_AUTH_TOKEN`이나 npm token secret을 쓰면 안 되며, 릴리스 핵심 GitHub 공식 Actions는 전체 commit SHA로 고정합니다. `release:check`는 이 워크플로 계약도 로컬에서 검사합니다.
396
+
397
+ trusted publishing과 npm provenance는 패키지가 이 GitHub OIDC 워크플로를 통해 게시되었음을 증명합니다. 벤치마크 정확성, 최종 사용자 저장소의 코드 근거 freshness, 보안 감사를 증명하지는 않으며, 그런 항목은 별도의 근거 트랙으로 다룹니다.
380
398
 
381
399
  관리자 벤치마크 명령은 [benchmarks/README.md](benchmarks/README.md)에 있습니다. 이 명령은 릴리스 근거와 공개 주장 검증을 위한 것이며, 일반 사용자 설정 절차가 아닙니다.
382
400
 
383
- 코드 근거 런타임/스토리지 점검에는 `npm run perf:code-efficiency`를 사용합니다. 이 명령은 3k/10k/50k 픽스처를 생성하고 `benchmarks/reports/code-performance-efficiency/current.json`과 `.md`를 작성합니다. 명령 시간에는 CLI 시작과 freshness 확인이 포함되며, `query_groups` 섹션은 대표 file/symbol/route/import/edge 쿼리의 직접 DB 시간을 따로 보고합니다. 보고서에는 `mixed-monorepo`를 포함한 체크인 샘플 corpus도 합성 scale fixture와 분리해 포함됩니다.
401
+ 코드 근거 런타임/스토리지 점검에는 `npm run perf:code-efficiency`를 사용합니다. 이 명령은 3k/10k/50k 픽스처를 생성하고 `benchmarks/reports/code-performance-efficiency/current.json`과 `.md`를 작성합니다. 명령 시간에는 CLI 시작과 freshness 확인이 포함되며, `query_groups` 섹션은 대표 file/symbol/route/import/edge 쿼리의 직접 DB 시간을 따로 보고합니다. 보고서는 `mixed-monorepo`, `web-service`, `python-cli`, `docs-heavy` 체크인 corpus도 합성 scale fixture와 분리해 측정합니다.
384
402
 
385
- 무시된 오래된 LLM 벤치마크 raw 출력은 격리 Codex home을 삭제하기 전에 dry-run-first 헬퍼로 먼저 감사할 수 있습니다.
403
+ 측정형 LLM 벤치마크 실행은 1일보다 오래된 이전 raw run 디렉터리와 격리 Codex home을 자동 삭제하고, claimable 실행 실패 시에도 현재 실행의 home을 삭제한 뒤 종료합니다. raw JSONL, stderr, 보고서, manifest는 보존 기간 안의 실행에 대해서만 감사용으로 유지됩니다. 무시된 오래된 raw 출력은 격리 Codex home을 삭제하기 전에 dry-run-first 헬퍼로 여전히 먼저 감사할 수 있습니다.
386
404
 
387
405
  ```bash
406
+ npm run benchmark:llm:raw-audit -- --older-than-days 14
388
407
  npm run benchmark:llm:prune-raw -- --older-than-days 14
389
408
  npm run benchmark:llm:prune-raw -- --older-than-days 14 --execute
390
409
  ```
391
410
 
411
+ `npm run benchmark:llm:delta-analysis`는 체크인된 measured LLM 보고서를 읽고 Codex를 실행하지 않은 채 cost-weighted regression을 순위화합니다. 대표 raw JSONL command trace와 broad-search/router-read driver 분류까지 보려면 `-- --include-traces`를 붙입니다. 작은 scale aggregation처럼 약한 셀을 새 공개 claim보다 먼저 진단하는 첫 경로입니다.
412
+
413
+ 측정형 LLM 실행은 기본적으로 `--scenario-order run-major-balanced`를 사용합니다. 각 measured run index마다 선택된 모든 scenario를 실행하고 반복마다 순서를 뒤집어, 반복 실행에서 with/without 조건이 한쪽으로 몰리지 않게 합니다. 이전의 scenario별 묶음 실행 진단을 재현할 때만 `--scenario-order scenario-major`를 사용하세요.
414
+
415
+ `npm run typecheck:ts7`은 opt-in TypeScript 7 RC 호환성 probe입니다. `npx`를 사용하며, compiler API와 이 프로젝트 TypeScript extractor의 parity 기록이 생기기 전까지 `test`, `release:check`, CI 게이트 밖에 둡니다.
416
+
392
417
  ## 영감
393
418
 
394
419
  이 프로젝트는 Andrej Karpathy의 [LLM Wiki](https://gist.github.com/karpathy/442a6bf555914893e9891c11519de94f) 패턴에서 영감을 받았습니다. 긴 대화 기록에서 프로젝트 상태를 재구성하는 대신, 지속되는 markdown 컨텍스트를 작업 가까이에 둡니다.
package/README.md CHANGED
@@ -75,17 +75,19 @@ npm run benchmark:release:preview
75
75
  npm run benchmark:release -- --allow-codex-run
76
76
  ```
77
77
 
78
+ Measured release runs stream `[benchmark:progress]` lines to stderr for the scenario count, expected Codex exec total, current exec ordinal, phase, prompt id, exit status, elapsed time, and raw JSONL path. stdout stays reserved for the final JSON result.
79
+
78
80
  ### Wiki track (planning-doc routing)
79
81
 
80
82
  Cost-weighted tokens, Project Librarian vs control:
81
83
 
82
84
  | Scale | decision_lookup | aggregation | multi_session (2nd session) |
83
85
  | --- | --- | --- | --- |
84
- | Small | 43.8% less | 144.5% more | 5.4% more |
85
- | Medium | 53.4% less | 4.4% less | 68.5% less |
86
- | Large | 71.6% less* | 12.8% less | 66.0% less |
86
+ | Small | 14.4% less | 81.0% more | 22.0% less |
87
+ | Medium | 52.0% less | 19.0% less | 54.1% less |
88
+ | Large | 71.1% less | 29.0% less | 71.8% less |
87
89
 
88
- Latest synthetic wiki-track release candidate: 2026-06-16, `gpt-5.5`, 42 scenarios, 3 measured runs plus 1 warmup each. The overall claim gate **failed**: 41/42 scenarios passed correctness, but `decision_lookup` at large scale on the no-Project-Librarian control had only 2/3 correct measured runs. The failed control run selected `2026-05-04` from a dated history file instead of the expected latest benchmark-evidence decision `2026-06-10` from the decision log. Treat this table as diagnostic evidence, not a public release claim, until a clean release run passes the claim gate. Published boundaries remain visible: small `aggregation` is much more expensive with the wiki, small `multi_session` is slightly more expensive, and `aggregation` stays slower at every scale even when token cost drops.
90
+ Latest synthetic wiki-track release candidate: 2026-06-19, `gpt-5.5`, 42 scenarios, 3 measured runs plus 1 warmup each. The overall claim gate **passed**: 42/42 scenarios passed correctness, all 42 scenarios were claimable, and every corpus gate met the 3-run minimum. The release claim is still bounded to the synthetic wiki-routing track and the listed task families; it is not a claim about code-graph behavior, real repositories, every agent surface, or every question shape. Published boundaries remain visible: small `aggregation` still costs 81.0% more with the wiki, small `release_policy` costs 9.4% more in the full report, and `aggregation` stays slower at every scale even when token cost drops.
89
91
 
90
92
  ### Code-graph track (code evidence index, real repositories)
91
93
 
@@ -143,6 +145,8 @@ npx project-librarian install-skill --scope project --agents all
143
145
 
144
146
  `--agents` also accepts comma-separated values such as `codex,claude,cursor,gemini`. `all` targets every supported agent. `--scope` accepts `user` or `project`.
145
147
 
148
+ The project setup/update runner also accepts `--agents`. Fresh setup defaults to all supported agent surfaces only when no project-scoped Project Librarian skill install is present. If the repository already has project-scoped skills such as `.codex/skills/project-librarian/` and `.claude/skills/project-librarian/`, the first setup uses that installed agent set by default. Existing non-migration updates preserve the agent surfaces already present in the repository, so a repo that has only Codex and Claude files will not gain Cursor or Gemini files on a plain update. Use `project-librarian update --agents cursor` or `project-librarian update --agents all` when you intentionally want to add newly supported surfaces; unlisted surfaces are not deleted.
149
+
146
150
  ## Runner Paths
147
151
 
148
152
  These paths are mainly for agents and automation. After installation, agents should run the installed local copy with `node`, not `npx`. This avoids network access and unpinned package execution in restricted agent environments.
@@ -168,6 +172,7 @@ Wiki setup and maintenance:
168
172
  | --- | --- | --- |
169
173
  | Create or update the wiki | "Use Project Librarian to set up or update this repository's planning wiki." | `[init]` |
170
174
  | Update existing setup without migration | "Update this repository's Project Librarian setup without migrating the wiki." | `update` |
175
+ | Add a specific agent surface to an existing setup | "Add the Cursor Project Librarian surface without migrating the wiki." | `update --agents cursor` |
171
176
  | Migrate existing docs/wiki content | "Use Project Librarian to migrate the existing docs/wiki content." | `--migrate` |
172
177
  | Validate generated setup | "Run Project Librarian validation." | `--lint` |
173
178
  | Check links and document quality | "Run Project Librarian diagnostics." | `--doctor` |
@@ -188,6 +193,7 @@ Code evidence:
188
193
  | Require incremental update | "Update the Project Librarian code evidence index incrementally." | `--code-index --incremental` |
189
194
  | Force a full rebuild | "Fully rebuild the Project Librarian code evidence index." | `--code-index --code-index-full` |
190
195
  | Use optional Tree-sitter backend | "Build Project Librarian code evidence with the Tree-sitter parser." | `--code-index --code-parser tree-sitter` |
196
+ | Inspect cache compatibility | "Inspect Project Librarian code evidence cache health." | `--code-index-health` |
191
197
  | Show cache status | "Show Project Librarian code evidence status." | `--code-status` |
192
198
  | List indexed files | "List files in the Project Librarian code evidence index." | `--code-files` |
193
199
  | Print architecture and ownership report | "Show the Project Librarian code report." | `--code-report` |
@@ -201,6 +207,8 @@ Only one code evidence mode can run at a time. `--incremental`, `--code-index-fu
201
207
 
202
208
  ## What Gets Installed
203
209
 
210
+ Fresh setup installs the supported agent surfaces below unless you pass `--agents` or the repository already has project-scoped Project Librarian skills for a narrower agent set. Existing non-migration updates preserve the detected surface set by default and update only those selected surfaces plus the common wiki/git-hook files.
211
+
204
212
  Project instruction files:
205
213
 
206
214
  - `AGENTS.md`
@@ -368,6 +376,9 @@ npm install
368
376
  npm run typecheck
369
377
  npm run build
370
378
  npm test
379
+ npm run test:coverage
380
+ npm run benchmark:llm:raw-audit
381
+ npm run benchmark:llm:delta-analysis
371
382
  npm run benchmark:claim-ledger
372
383
  npm run release:check
373
384
  npm pack --dry-run
@@ -375,19 +386,34 @@ npm pack --dry-run
375
386
 
376
387
  When editing TypeScript under `src/`, rebuild before committing so `dist/` stays current.
377
388
 
378
- `npm run release:check` is a local-only maintainer gate: it runs tests, benchmark parser smoke, benchmark release preview, benchmark claim-ledger classification, package dry-run inspection, dist executable checks, and README benchmark-claim boundary checks. It never publishes and never launches a measured Codex benchmark.
389
+ `npm run test:coverage` uses Node's native test coverage with conservative line, branch, and function thresholds so coverage is a regression gate, not only a report.
390
+
391
+ `npm run release:check` is a local-only maintainer gate: it runs tests, native Node coverage, benchmark parser smoke, the real-corpus offline demo, benchmark release preview, benchmark claim-ledger classification, raw hygiene audit, package dry-run inspection, dist executable checks, and README benchmark-claim boundary checks. It never publishes, never deletes raw benchmark artifacts, and never launches a measured Codex benchmark.
392
+
393
+ Treat a green `release:check` as a reproducible release-readiness bundle, not a runtime guarantee: it proves those local gates on the current checkout, including that the package dry run stays inside the expected publish boundary (`agents/`, `dist/`, `LICENSE`, `README.md`, `README.ko.md`, and `SKILL.md`) and excludes source files, tests, repo-local wiki/workflow state, raw benchmark output, and local caches.
394
+
395
+ Publishing is handled by `.github/workflows/publish.yml` after a GitHub Release is published. The workflow uses npm trusted publishing through GitHub OIDC (`id-token: write`) and `npm publish --access public`, which generates npm provenance automatically; it must not use `NODE_AUTH_TOKEN` or npm token secrets, and release-critical first-party GitHub Actions are pinned to full commit SHAs. `release:check` verifies this workflow contract locally.
396
+
397
+ Trusted publishing and npm provenance prove the package was published through that GitHub OIDC workflow. They do not prove benchmark correctness, code-evidence freshness in an end-user repository, or a security audit; those remain separate evidence tracks.
379
398
 
380
399
  Maintainer benchmark commands live in [benchmarks/README.md](benchmarks/README.md). They are for release evidence and public claim validation, not normal end-user setup.
381
400
 
382
- For code-evidence runtime/storage checks, `npm run perf:code-efficiency` generates 3k/10k/50k fixtures and writes `benchmarks/reports/code-performance-efficiency/current.json` plus `.md`. Command timings include CLI startup and freshness checks; the `query_groups` section reports direct DB timings for representative file/symbol/route/import/edge queries. The report also includes checked-in sample corpora, including `mixed-monorepo`, separately from synthetic scale fixtures.
401
+ For code-evidence runtime/storage checks, `npm run perf:code-efficiency` generates 3k/10k/50k fixtures and writes `benchmarks/reports/code-performance-efficiency/current.json` plus `.md`. Command timings include CLI startup and freshness checks; the `query_groups` section reports direct DB timings for representative file/symbol/route/import/edge queries. The report also measures checked-in `mixed-monorepo`, `web-service`, `python-cli`, and `docs-heavy` corpora separately from synthetic scale fixtures.
383
402
 
384
- Old ignored LLM benchmark raw output can be audited with the dry-run-first helper before deleting retained isolated Codex homes:
403
+ Measured LLM benchmark runs automatically prune stale prior raw run directories and isolated Codex homes older than 1 day, then prune the current run's homes even on claimable-run failure; raw JSONL, stderr, reports, and manifests remain for runs inside the retention window. Old ignored raw output can still be audited with the dry-run-first helper before deleting retained isolated Codex homes:
385
404
 
386
405
  ```bash
406
+ npm run benchmark:llm:raw-audit -- --older-than-days 14
387
407
  npm run benchmark:llm:prune-raw -- --older-than-days 14
388
408
  npm run benchmark:llm:prune-raw -- --older-than-days 14 --execute
389
409
  ```
390
410
 
411
+ `npm run benchmark:llm:delta-analysis` reads the checked-in measured LLM report and ranks cost-weighted regressions without launching Codex. Add `-- --include-traces` to include representative raw JSONL command traces and classify broad-search/router-read drivers. It is the first diagnostic stop for weak cells such as small-scale aggregation before adding new benchmark claims.
412
+
413
+ Measured LLM runs default to `--scenario-order run-major-balanced`: each measured run index executes all selected scenarios, reversing the order on alternating repetitions so with/without pairs are not grouped by condition across repeated runs. Use `--scenario-order scenario-major` only when reproducing older scenario-grouped diagnostics.
414
+
415
+ `npm run typecheck:ts7` is an opt-in TypeScript 7 RC compatibility probe. It uses `npx` and is intentionally outside `test`, `release:check`, and CI gates until the compiler API and this project's TypeScript extractor have a measured parity record.
416
+
391
417
  ## Inspiration
392
418
 
393
419
  This project is inspired by Andrej Karpathy's [LLM Wiki](https://gist.github.com/karpathy/442a6bf555914893e9891c11519de94f) pattern: keep persistent markdown context close to the work instead of reconstructing project state from long chat history.
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.agentSurfaceRequiredFiles = exports.allAgentSurfaces = void 0;
4
+ exports.parseAgentSurfaceValues = parseAgentSurfaceValues;
5
+ exports.hasProjectLibrarianInstall = hasProjectLibrarianInstall;
6
+ exports.activeAgentSurfaces = activeAgentSurfaces;
7
+ exports.resolveBootstrapAgentSurfaces = resolveBootstrapAgentSurfaces;
8
+ exports.includesAgentSurface = includesAgentSurface;
9
+ exports.allAgentSurfaces = ["codex", "claude", "cursor", "gemini"];
10
+ exports.agentSurfaceRequiredFiles = {
11
+ codex: [".codex/hooks/wiki-session-start.js", ".codex/hooks.json"],
12
+ claude: ["CLAUDE.md", ".claude/hooks/wiki-session-start.js", ".claude/settings.json"],
13
+ cursor: [".cursor/rules/project-librarian.mdc", ".cursor/hooks/wiki-session-start.js", ".cursor/hooks.json"],
14
+ gemini: ["GEMINI.md", ".gemini/hooks/wiki-session-start.js", ".gemini/settings.json"],
15
+ };
16
+ const agentSurfaceProjectSkillFiles = {
17
+ codex: [".codex/skills/project-librarian/SKILL.md"],
18
+ claude: [".claude/skills/project-librarian/SKILL.md"],
19
+ cursor: [".cursor/skills/project-librarian/SKILL.md"],
20
+ gemini: [".gemini/skills/project-librarian/SKILL.md"],
21
+ };
22
+ const projectLibrarianCommonInstallFiles = [
23
+ "wiki/startup.md",
24
+ "wiki/index.md",
25
+ "wiki/AGENTS.md",
26
+ ];
27
+ function parseAgentSurfaceValues(values) {
28
+ const surfaces = new Set();
29
+ const invalid = [];
30
+ for (const value of values) {
31
+ if (value === "all") {
32
+ for (const surface of exports.allAgentSurfaces)
33
+ surfaces.add(surface);
34
+ }
35
+ else if (exports.allAgentSurfaces.includes(value)) {
36
+ surfaces.add(value);
37
+ }
38
+ else {
39
+ invalid.push(value);
40
+ }
41
+ }
42
+ return { surfaces: Array.from(surfaces), invalid };
43
+ }
44
+ function hasProjectLibrarianInstall(fileExists, readFile) {
45
+ if (projectLibrarianCommonInstallFiles.some((file) => fileExists(file)))
46
+ return true;
47
+ if (exports.allAgentSurfaces.some((surface) => agentSurfaceProjectSkillFiles[surface].some((file) => fileExists(file))))
48
+ return true;
49
+ if (fileExists("AGENTS.md") && readFile) {
50
+ return readFile("AGENTS.md").includes("PROJECT-WIKI-FIRST:START");
51
+ }
52
+ return false;
53
+ }
54
+ function activeAgentSurfaces(fileExists) {
55
+ return exports.allAgentSurfaces.filter((surface) => (exports.agentSurfaceRequiredFiles[surface].some((file) => fileExists(file))
56
+ || agentSurfaceProjectSkillFiles[surface].some((file) => fileExists(file))));
57
+ }
58
+ function resolveBootstrapAgentSurfaces(explicitSurfaces, fileExists, readFile) {
59
+ if (explicitSurfaces.length > 0)
60
+ return Array.from(explicitSurfaces);
61
+ if (hasProjectLibrarianInstall(fileExists, readFile))
62
+ return activeAgentSurfaces(fileExists);
63
+ return Array.from(exports.allAgentSurfaces);
64
+ }
65
+ function includesAgentSurface(surfaces, surface) {
66
+ return surfaces.includes(surface);
67
+ }
package/dist/args.js CHANGED
@@ -1,80 +1,74 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.codeSearchSymbol = exports.codeReportSection = exports.codeQuerySql = exports.codeImpactTarget = exports.codeContextPackTarget = exports.wikiVisualizeOutput = exports.wikiVisualizeMode = exports.wikiImpactTarget = exports.wikiImpactMode = exports.queryTerm = exports.codeSearchSymbolMode = exports.codeQueryMode = exports.codeImpactMode = exports.codeParserMode = exports.codeContextPackMode = exports.codeFilesMode = exports.codeStatusMode = exports.codeReportMode = exports.codeIndexHealthMode = exports.codeIndexFullMode = exports.codeIndexIncrementalMode = exports.codeIndexMode = exports.acknowledgeSmallRepoMode = exports.noGitConfigMode = exports.reviewMigrationMode = exports.pruneCheckMode = exports.captureInboxMode = exports.refreshIndexMode = exports.issueDraftMode = exports.issueCreateMode = exports.glossaryMode = exports.fixMode = exports.doctorMode = exports.qualityCheckMode = exports.linkCheckMode = exports.migrationQualityCheckMode = exports.migrationLintMode = exports.migrationDoctorMode = exports.lintMode = exports.migrateMode = exports.missingValueOptions = exports.unexpectedValueOptions = exports.unknownOptions = exports.args = exports.commandArgs = exports.command = exports.unknownCommand = exports.helpMode = exports.parsedArgs = exports.rawArgs = void 0;
4
- exports.issueDraftTitle = exports.issueBodyFile = exports.captureCategory = exports.captureContent = exports.captureTitle = exports.codeIndexScopes = exports.codeParser = exports.codeIndexOutput = void 0;
3
+ exports.codeQuerySql = exports.codeImpactTarget = exports.codeContextPackTarget = exports.wikiVisualizeOutput = exports.wikiVisualizeMode = exports.wikiImpactTarget = exports.wikiImpactMode = exports.queryTerm = exports.codeSearchSymbolMode = exports.codeQueryMode = exports.codeImpactMode = exports.codeParserMode = exports.codeContextPackMode = exports.codeFilesMode = exports.codeStatusMode = exports.codeReportMode = exports.codeIndexHealthMode = exports.codeIndexFullMode = exports.codeIndexIncrementalMode = exports.codeIndexMode = exports.acknowledgeSmallRepoMode = exports.noGitConfigMode = exports.reviewMigrationMode = exports.pruneCheckMode = exports.captureInboxMode = exports.refreshIndexMode = exports.issueDraftMode = exports.issueCreateMode = exports.glossaryMode = exports.fixMode = exports.doctorMode = exports.qualityCheckMode = exports.linkCheckMode = exports.migrationQualityCheckMode = exports.migrationLintMode = exports.migrationDoctorMode = exports.lintMode = exports.migrateMode = exports.invalidAgentTargets = exports.missingValueOptions = exports.unexpectedValueOptions = exports.unknownOptions = exports.args = exports.commandArgs = exports.command = exports.unknownCommand = exports.helpMode = exports.agentTargets = exports.parsedArgs = exports.rawArgs = void 0;
4
+ exports.issueDraftTitle = exports.issueBodyFile = exports.captureCategory = exports.captureContent = exports.captureTitle = exports.codeIndexScopes = exports.codeParser = exports.codeIndexOutput = exports.codeSearchSymbol = exports.codeReportSection = void 0;
5
5
  exports.parseArgs = parseArgs;
6
6
  exports.argValue = argValue;
7
7
  exports.argValues = argValues;
8
+ const agent_surfaces_1 = require("./agent-surfaces");
8
9
  exports.rawArgs = process.argv.slice(2);
9
10
  const knownCommands = new Set(["init", "update", "install-skill", "mcp"]);
10
- const flagsWithoutValues = new Set([
11
- "--acknowledge-small-repo",
12
- "--adopt-existing",
13
- "--capture-inbox",
14
- "--code-evidence-files",
15
- "--code-evidence-index",
16
- "--code-evidence-status",
17
- "--code-files",
18
- "--code-incremental",
19
- "--code-index",
20
- "--code-index-full",
21
- "--code-index-health",
22
- "--code-index-incremental",
23
- "--code-evidence-index-full",
24
- "--code-evidence-index-incremental",
25
- "--code-report",
26
- "--code-status",
27
- "--code-evidence-report",
28
- "--dry-run",
29
- "--glossary-init",
30
- "--doctor",
31
- "--fix",
32
- "--issue-create",
33
- "--issue-draft",
34
- "--incremental",
35
- "--link-check",
36
- "--lint",
37
- "--migrate",
38
- "--migration-doctor",
39
- "--migration-lint",
40
- "--migration-quality-check",
41
- "--no-git-config",
42
- "--prune-check",
43
- "--quality-check",
44
- "--refresh-index",
45
- "--review-migration",
46
- "--semantic-migrate",
47
- "--wiki-graph-html",
48
- "--wiki-visualize",
49
- ]);
50
- const flagsWithValues = new Set([
51
- "--agents",
52
- "--category",
53
- "--code-evidence-context-pack",
54
- "--code-evidence-impact",
55
- "--code-context-pack",
56
- "--code-evidence-out",
57
- "--code-evidence-parser",
58
- "--code-evidence-query",
59
- "--code-evidence-report-section",
60
- "--code-evidence-scope",
61
- "--code-evidence-symbol",
62
- "--code-impact",
63
- "--code-index-out",
64
- "--code-parser",
65
- "--code-query",
66
- "--code-report-section",
67
- "--code-scope",
68
- "--code-search-symbol",
69
- "--content",
70
- "--issue-body-file",
71
- "--issue-title",
72
- "--query",
73
- "--scope",
74
- "--title",
75
- "--wiki-impact",
76
- "--wiki-visualize-out",
77
- ]);
11
+ const flagDefinitions = [
12
+ { name: "--acknowledge-small-repo", value: "none" },
13
+ { name: "--adopt-existing", value: "none" },
14
+ { name: "--agents", value: "value" },
15
+ { name: "--capture-inbox", value: "none" },
16
+ { name: "--category", value: "value" },
17
+ { name: "--code-context-pack", value: "value", aliases: ["--code-evidence-context-pack"] },
18
+ { name: "--code-files", value: "none", aliases: ["--code-evidence-files"] },
19
+ { name: "--code-impact", value: "value", aliases: ["--code-evidence-impact"] },
20
+ { name: "--code-index", value: "none", aliases: ["--code-evidence-index"] },
21
+ { name: "--code-index-full", value: "none", aliases: ["--code-evidence-index-full"] },
22
+ { name: "--code-index-health", value: "none" },
23
+ { name: "--code-index-incremental", value: "none", aliases: ["--incremental", "--code-incremental", "--code-evidence-index-incremental"] },
24
+ { name: "--code-index-out", value: "value", aliases: ["--code-evidence-out"] },
25
+ { name: "--code-parser", value: "value", aliases: ["--code-evidence-parser"] },
26
+ { name: "--code-query", value: "value", aliases: ["--code-evidence-query"] },
27
+ { name: "--code-report", value: "none", aliases: ["--code-evidence-report"] },
28
+ { name: "--code-report-section", value: "value", aliases: ["--code-evidence-report-section"] },
29
+ { name: "--code-scope", value: "value", aliases: ["--code-evidence-scope"] },
30
+ { name: "--code-search-symbol", value: "value", aliases: ["--code-evidence-symbol"] },
31
+ { name: "--code-status", value: "none", aliases: ["--code-evidence-status"] },
32
+ { name: "--content", value: "value" },
33
+ { name: "--doctor", value: "none" },
34
+ { name: "--dry-run", value: "none" },
35
+ { name: "--fix", value: "none" },
36
+ { name: "--glossary-init", value: "none" },
37
+ { name: "--issue-body-file", value: "value" },
38
+ { name: "--issue-create", value: "none" },
39
+ { name: "--issue-draft", value: "none" },
40
+ { name: "--issue-title", value: "value" },
41
+ { name: "--link-check", value: "none" },
42
+ { name: "--lint", value: "none" },
43
+ { name: "--migrate", value: "none" },
44
+ { name: "--migration-doctor", value: "none" },
45
+ { name: "--migration-lint", value: "none" },
46
+ { name: "--migration-quality-check", value: "none" },
47
+ { name: "--no-git-config", value: "none" },
48
+ { name: "--prune-check", value: "none" },
49
+ { name: "--quality-check", value: "none" },
50
+ { name: "--query", value: "value" },
51
+ { name: "--refresh-index", value: "none" },
52
+ { name: "--review-migration", value: "none", aliases: ["--semantic-migrate"] },
53
+ { name: "--scope", value: "value" },
54
+ { name: "--title", value: "value" },
55
+ { name: "--wiki-graph-html", value: "none" },
56
+ { name: "--wiki-impact", value: "value" },
57
+ { name: "--wiki-visualize", value: "none" },
58
+ { name: "--wiki-visualize-out", value: "value" },
59
+ ];
60
+ function definitionNames(definition) {
61
+ return [definition.name, ...(definition.aliases ?? [])];
62
+ }
63
+ function flagNamesByPolicy(value) {
64
+ return flagDefinitions.filter((definition) => definition.value === value).flatMap(definitionNames);
65
+ }
66
+ function namesForFlag(name) {
67
+ const definition = flagDefinitions.find((candidate) => definitionNames(candidate).includes(name));
68
+ return definition ? definitionNames(definition) : [name];
69
+ }
70
+ const flagsWithoutValues = new Set(flagNamesByPolicy("none"));
71
+ const flagsWithValues = new Set(flagNamesByPolicy("value"));
78
72
  const knownFlags = new Set([...flagsWithoutValues, ...flagsWithValues, "--help", "-h"]);
79
73
  function flagName(arg) {
80
74
  return arg.startsWith("--") ? arg.split("=", 1)[0] ?? arg : arg;
@@ -135,37 +129,42 @@ function parseArgs(argv) {
135
129
  const hasFlag = (name) => hasFlagIn(commandArgs, name);
136
130
  const argValue = (name) => argValueFrom(commandArgs, name);
137
131
  const argValues = (name) => argValuesFrom(commandArgs, name);
138
- const codeImpactTarget = argValue("--code-impact") || argValue("--code-evidence-impact");
139
- const codeContextPackTarget = argValue("--code-context-pack") || argValue("--code-evidence-context-pack");
140
- const codeQuerySql = argValue("--code-query") || argValue("--code-evidence-query");
141
- const codeSearchSymbol = argValue("--code-search-symbol") || argValue("--code-evidence-symbol");
132
+ const hasAnyFlag = (name) => namesForFlag(name).some(hasFlag);
133
+ const argValueFromAny = (name) => namesForFlag(name).map(argValue).find(Boolean) ?? "";
134
+ const argValuesFromAny = (name) => namesForFlag(name).flatMap(argValues);
135
+ const codeImpactTarget = argValueFromAny("--code-impact");
136
+ const codeContextPackTarget = argValueFromAny("--code-context-pack");
137
+ const codeQuerySql = argValueFromAny("--code-query");
138
+ const codeSearchSymbol = argValueFromAny("--code-search-symbol");
139
+ const parsedAgentTargets = (0, agent_surfaces_1.parseAgentSurfaceValues)(argValues("--agents"));
142
140
  return {
143
141
  acknowledgeSmallRepoMode: args.has("--acknowledge-small-repo"),
142
+ agentTargets: parsedAgentTargets.surfaces,
144
143
  args,
145
144
  captureCategory: argValue("--category") || "project-candidate",
146
145
  captureContent: argValue("--content"),
147
146
  captureInboxMode: args.has("--capture-inbox"),
148
147
  captureTitle: argValue("--title"),
149
- codeContextPackMode: hasFlag("--code-context-pack") || hasFlag("--code-evidence-context-pack"),
148
+ codeContextPackMode: hasAnyFlag("--code-context-pack"),
150
149
  codeContextPackTarget,
151
- codeFilesMode: args.has("--code-files") || args.has("--code-evidence-files"),
152
- codeImpactMode: hasFlag("--code-impact") || hasFlag("--code-evidence-impact"),
150
+ codeFilesMode: hasAnyFlag("--code-files"),
151
+ codeImpactMode: hasAnyFlag("--code-impact"),
153
152
  codeImpactTarget,
154
- codeIndexFullMode: args.has("--code-index-full") || args.has("--code-evidence-index-full"),
153
+ codeIndexFullMode: hasAnyFlag("--code-index-full"),
155
154
  codeIndexHealthMode: args.has("--code-index-health"),
156
- codeIndexIncrementalMode: args.has("--incremental") || args.has("--code-incremental") || args.has("--code-index-incremental") || args.has("--code-evidence-index-incremental"),
157
- codeIndexMode: args.has("--code-index") || args.has("--code-evidence-index"),
158
- codeIndexOutput: argValue("--code-index-out") || argValue("--code-evidence-out") || ".project-wiki/code-evidence.sqlite",
159
- codeIndexScopes: [...argValues("--code-scope"), ...argValues("--code-evidence-scope")],
160
- codeParser: argValue("--code-parser") || argValue("--code-evidence-parser") || "default",
161
- codeParserMode: hasFlag("--code-parser") || hasFlag("--code-evidence-parser"),
162
- codeQueryMode: hasFlag("--code-query") || hasFlag("--code-evidence-query"),
155
+ codeIndexIncrementalMode: hasAnyFlag("--code-index-incremental"),
156
+ codeIndexMode: hasAnyFlag("--code-index"),
157
+ codeIndexOutput: argValueFromAny("--code-index-out") || ".project-wiki/code-evidence.sqlite",
158
+ codeIndexScopes: argValuesFromAny("--code-scope"),
159
+ codeParser: argValueFromAny("--code-parser") || "default",
160
+ codeParserMode: hasAnyFlag("--code-parser"),
161
+ codeQueryMode: hasAnyFlag("--code-query"),
163
162
  codeQuerySql,
164
- codeReportMode: args.has("--code-report") || args.has("--code-evidence-report"),
165
- codeReportSection: argValue("--code-report-section") || argValue("--code-evidence-report-section"),
163
+ codeReportMode: hasAnyFlag("--code-report"),
164
+ codeReportSection: argValueFromAny("--code-report-section"),
166
165
  codeSearchSymbol,
167
- codeSearchSymbolMode: hasFlag("--code-search-symbol") || hasFlag("--code-evidence-symbol"),
168
- codeStatusMode: args.has("--code-status") || args.has("--code-evidence-status"),
166
+ codeSearchSymbolMode: hasAnyFlag("--code-search-symbol"),
167
+ codeStatusMode: hasAnyFlag("--code-status"),
169
168
  command,
170
169
  commandArgs,
171
170
  doctorMode: args.has("--doctor"),
@@ -176,6 +175,7 @@ function parseArgs(argv) {
176
175
  issueCreateMode: args.has("--issue-create"),
177
176
  issueDraftMode: args.has("--issue-draft"),
178
177
  issueDraftTitle: argValue("--issue-title"),
178
+ invalidAgentTargets: parsedAgentTargets.invalid,
179
179
  linkCheckMode: args.has("--link-check"),
180
180
  lintMode: args.has("--lint"),
181
181
  migrationDoctorMode: args.has("--migration-doctor"),
@@ -189,7 +189,7 @@ function parseArgs(argv) {
189
189
  queryTerm: argValue("--query"),
190
190
  rawArgs: argv,
191
191
  refreshIndexMode: args.has("--refresh-index"),
192
- reviewMigrationMode: args.has("--review-migration") || args.has("--semantic-migrate"),
192
+ reviewMigrationMode: hasAnyFlag("--review-migration"),
193
193
  unexpectedValueOptions: Array.from(new Set(commandArgs
194
194
  .filter((arg) => arg.startsWith("--") && arg.includes("="))
195
195
  .map(flagName)
@@ -206,6 +206,7 @@ function parseArgs(argv) {
206
206
  };
207
207
  }
208
208
  exports.parsedArgs = parseArgs(exports.rawArgs);
209
+ exports.agentTargets = exports.parsedArgs.agentTargets;
209
210
  exports.helpMode = exports.parsedArgs.helpMode;
210
211
  exports.unknownCommand = exports.parsedArgs.unknownCommand;
211
212
  exports.command = exports.parsedArgs.command;
@@ -214,6 +215,7 @@ exports.args = exports.parsedArgs.args;
214
215
  exports.unknownOptions = exports.parsedArgs.unknownOptions;
215
216
  exports.unexpectedValueOptions = exports.parsedArgs.unexpectedValueOptions;
216
217
  exports.missingValueOptions = exports.parsedArgs.missingValueOptions;
218
+ exports.invalidAgentTargets = exports.parsedArgs.invalidAgentTargets;
217
219
  exports.migrateMode = exports.parsedArgs.migrateMode;
218
220
  exports.lintMode = exports.parsedArgs.lintMode;
219
221
  exports.migrationDoctorMode = exports.parsedArgs.migrationDoctorMode;
@@ -60,7 +60,13 @@ function printJson(value) {
60
60
  function requireCompatibleDatabase(database, runtime) {
61
61
  const schemaVersion = (0, schema_1.readMetaValue)(database, "schema_version");
62
62
  if (schemaVersion !== schema_1.codeIndexSchemaVersion) {
63
- runtime.fail(`code evidence index schema version ${schemaVersion || "(missing)"} is incompatible with ${schema_1.codeIndexSchemaVersion}; rebuild with --code-index`);
63
+ const databasePath = runtime.codeEvidenceDatabasePath();
64
+ runtime.fail([
65
+ `code evidence index schema version ${schemaVersion || "(missing)"} is incompatible with ${schema_1.codeIndexSchemaVersion}`,
66
+ `inspect: project-librarian --code-index-health`,
67
+ `rebuild: project-librarian --code-index --code-index-full`,
68
+ `database: ${databasePath.relativePath}`,
69
+ ].join("\n"));
64
70
  }
65
71
  }
66
72
  function runCodeIndexMode(runtime) {
@@ -2,6 +2,7 @@
2
2
  "use strict";
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
4
  const args_1 = require("./args");
5
+ const agent_surfaces_1 = require("./agent-surfaces");
5
6
  const hooks_1 = require("./hooks");
6
7
  const install_skill_1 = require("./install-skill");
7
8
  const modes_1 = require("./modes");
@@ -39,6 +40,7 @@ Options:
39
40
  --refresh-index Update the managed auto-discovered wiki index block.
40
41
  --capture-inbox Append a candidate note with --title, --content, and optional --category.
41
42
  --glossary-init Create and route the optional glossary page.
43
+ --agents <list> With init/update, write only selected agent surfaces: codex, claude, cursor, gemini, or all. Existing project skill/setup surfaces are preserved by default.
42
44
  --prune-check Report active pages with stale or unresolved signals.
43
45
  --review-migration Sync unit coverage and compatible inbox statuses into migration review files.
44
46
  --no-git-config Install hook files without changing git core.hooksPath.
@@ -107,6 +109,11 @@ if (args_1.missingValueOptions.length > 0) {
107
109
  printUsage();
108
110
  process.exit(1);
109
111
  }
112
+ if (args_1.invalidAgentTargets.length > 0) {
113
+ console.error(`invalid --agents entr${args_1.invalidAgentTargets.length === 1 ? "y" : "ies"}: ${args_1.invalidAgentTargets.join(", ")}; expected codex, claude, cursor, gemini, or all`);
114
+ printUsage();
115
+ process.exit(1);
116
+ }
110
117
  if (args_1.command === "update" && args_1.migrateMode) {
111
118
  console.error("update cannot be combined with --migrate or --adopt-existing; use project-librarian --migrate for migration.");
112
119
  process.exit(1);
@@ -247,6 +254,16 @@ function runInitCommand() {
247
254
  runRefreshIndexOnlyMode();
248
255
  process.exit(0);
249
256
  }
257
+ const selectedAgentSurfaces = args_1.agentTargets.length > 0
258
+ ? args_1.agentTargets
259
+ : args_1.migrateMode
260
+ ? Array.from(agent_surfaces_1.allAgentSurfaces)
261
+ : (0, agent_surfaces_1.resolveBootstrapAgentSurfaces)(args_1.agentTargets, workspace_1.exists, workspace_1.read);
262
+ const shouldWriteSurface = (surface) => (0, agent_surfaces_1.includesAgentSurface)(selectedAgentSurfaces, surface);
263
+ const writeCodexSurface = shouldWriteSurface("codex");
264
+ const writeClaudeSurface = shouldWriteSurface("claude");
265
+ const writeCursorSurface = shouldWriteSurface("cursor");
266
+ const writeGeminiSurface = shouldWriteSurface("gemini");
250
267
  const migrationState = args_1.migrateMode ? (0, migration_1.prepareMigrationMode)() : null;
251
268
  const results = [];
252
269
  if (migrationState)
@@ -256,11 +273,16 @@ function runInitCommand() {
256
273
  (0, workspace_1.mkdirp)("wiki/inbox");
257
274
  (0, workspace_1.mkdirp)("wiki/meta");
258
275
  (0, workspace_1.mkdirp)("wiki/sources");
259
- (0, workspace_1.mkdirp)(".codex/hooks");
260
- (0, workspace_1.mkdirp)(".claude/hooks");
261
- (0, workspace_1.mkdirp)(".cursor/hooks");
262
- (0, workspace_1.mkdirp)(".cursor/rules");
263
- (0, workspace_1.mkdirp)(".gemini/hooks");
276
+ if (writeCodexSurface)
277
+ (0, workspace_1.mkdirp)(".codex/hooks");
278
+ if (writeClaudeSurface)
279
+ (0, workspace_1.mkdirp)(".claude/hooks");
280
+ if (writeCursorSurface) {
281
+ (0, workspace_1.mkdirp)(".cursor/hooks");
282
+ (0, workspace_1.mkdirp)(".cursor/rules");
283
+ }
284
+ if (writeGeminiSurface)
285
+ (0, workspace_1.mkdirp)(".gemini/hooks");
264
286
  (0, workspace_1.mkdirp)(".githooks");
265
287
  // B1 fallback: sync the CURRENT startup.md TL;DR into the managed AGENTS.md block
266
288
  // so non-interactive `codex exec` (which does not run SessionStart hooks) still
@@ -271,23 +293,34 @@ function runInitCommand() {
271
293
  const startupForSync = (0, workspace_1.exists)("wiki/startup.md") ? (0, workspace_1.read)("wiki/startup.md") : templates_1.startup;
272
294
  const startupTldrForAgents = (0, templates_1.extractStartupTldr)(startupForSync);
273
295
  results.push(["AGENTS.md", (0, workspace_1.upsertMarkedSection)("AGENTS.md", "<!-- PROJECT-WIKI-FIRST:START -->", "<!-- PROJECT-WIKI-FIRST:END -->", (0, templates_1.agentsSection)(startupTldrForAgents))]);
274
- results.push(["CLAUDE.md", (0, workspace_1.upsertMarkedSection)("CLAUDE.md", "<!-- PROJECT-WIKI-CLAUDE:START -->", "<!-- PROJECT-WIKI-CLAUDE:END -->", templates_1.claudeSection)]);
275
- results.push(["GEMINI.md", (0, workspace_1.upsertMarkedSection)("GEMINI.md", "<!-- PROJECT-WIKI-GEMINI:START -->", "<!-- PROJECT-WIKI-GEMINI:END -->", templates_1.geminiSection)]);
276
- results.push([".cursor/rules/project-librarian.mdc", (0, workspace_1.writeManaged)(".cursor/rules/project-librarian.mdc", templates_1.cursorRule)]);
296
+ if (writeClaudeSurface)
297
+ results.push(["CLAUDE.md", (0, workspace_1.upsertMarkedSection)("CLAUDE.md", "<!-- PROJECT-WIKI-CLAUDE:START -->", "<!-- PROJECT-WIKI-CLAUDE:END -->", templates_1.claudeSection)]);
298
+ if (writeGeminiSurface)
299
+ results.push(["GEMINI.md", (0, workspace_1.upsertMarkedSection)("GEMINI.md", "<!-- PROJECT-WIKI-GEMINI:START -->", "<!-- PROJECT-WIKI-GEMINI:END -->", templates_1.geminiSection)]);
300
+ if (writeCursorSurface)
301
+ results.push([".cursor/rules/project-librarian.mdc", (0, workspace_1.writeManaged)(".cursor/rules/project-librarian.mdc", templates_1.cursorRule)]);
277
302
  results.push(["wiki/AGENTS.md", (0, workspace_1.upsertMarkedSection)("wiki/AGENTS.md", "<!-- PROJECT-WIKI-INTERNAL:START -->", "<!-- PROJECT-WIKI-INTERNAL:END -->", templates_1.wikiAgentsSection)]);
278
303
  results.push([".githooks/prepare-commit-msg", (0, workspace_1.writeManaged)(".githooks/prepare-commit-msg", hooks_1.gitPrepareCommitMsgHook)]);
279
304
  (0, workspace_1.makeExecutable)(".githooks/prepare-commit-msg");
280
305
  results.push([".githooks/wiki-commit-trailers.js", (0, workspace_1.writeManaged)(".githooks/wiki-commit-trailers.js", hooks_1.gitWikiCommitTrailersScript)]);
281
306
  (0, workspace_1.makeExecutable)(".githooks/wiki-commit-trailers.js");
282
307
  results.push(["git core.hooksPath", (0, hooks_1.upsertGitHooksPath)()]);
283
- results.push([".codex/hooks.json", (0, hooks_1.upsertHookConfig)()]);
284
- results.push([".codex/hooks/wiki-session-start.js", (0, workspace_1.writeManaged)(".codex/hooks/wiki-session-start.js", hooks_1.hookScript)]);
285
- results.push([".claude/settings.json", (0, hooks_1.upsertClaudeHookConfig)()]);
286
- results.push([".claude/hooks/wiki-session-start.js", (0, workspace_1.writeManaged)(".claude/hooks/wiki-session-start.js", hooks_1.hookScript)]);
287
- results.push([".cursor/hooks.json", (0, hooks_1.upsertCursorHookConfig)()]);
288
- results.push([".cursor/hooks/wiki-session-start.js", (0, workspace_1.writeManaged)(".cursor/hooks/wiki-session-start.js", hooks_1.cursorHookScript)]);
289
- results.push([".gemini/settings.json", (0, hooks_1.upsertGeminiHookConfig)()]);
290
- results.push([".gemini/hooks/wiki-session-start.js", (0, workspace_1.writeManaged)(".gemini/hooks/wiki-session-start.js", hooks_1.hookScript)]);
308
+ if (writeCodexSurface) {
309
+ results.push([".codex/hooks.json", (0, hooks_1.upsertHookConfig)()]);
310
+ results.push([".codex/hooks/wiki-session-start.js", (0, workspace_1.writeManaged)(".codex/hooks/wiki-session-start.js", hooks_1.hookScript)]);
311
+ }
312
+ if (writeClaudeSurface) {
313
+ results.push([".claude/settings.json", (0, hooks_1.upsertClaudeHookConfig)()]);
314
+ results.push([".claude/hooks/wiki-session-start.js", (0, workspace_1.writeManaged)(".claude/hooks/wiki-session-start.js", hooks_1.hookScript)]);
315
+ }
316
+ if (writeCursorSurface) {
317
+ results.push([".cursor/hooks.json", (0, hooks_1.upsertCursorHookConfig)()]);
318
+ results.push([".cursor/hooks/wiki-session-start.js", (0, workspace_1.writeManaged)(".cursor/hooks/wiki-session-start.js", hooks_1.cursorHookScript)]);
319
+ }
320
+ if (writeGeminiSurface) {
321
+ results.push([".gemini/settings.json", (0, hooks_1.upsertGeminiHookConfig)()]);
322
+ results.push([".gemini/hooks/wiki-session-start.js", (0, workspace_1.writeManaged)(".gemini/hooks/wiki-session-start.js", hooks_1.hookScript)]);
323
+ }
291
324
  // Bootstrap-managed MCP registration (preservation-first, idempotent). Claude
292
325
  // Code reads `.mcp.json`, Cursor reads `.cursor/mcp.json`, and Gemini reads
293
326
  // `mcpServers` inside `.gemini/settings.json`. Codex only supports user-level MCP
@@ -296,16 +329,24 @@ function runInitCommand() {
296
329
  // Registration is scale-gated (2026-06-12 decision): below the measured
297
330
  // file-count threshold with no existing .project-wiki index, the rows report the
298
331
  // skip reason instead of writing config; an existing index registers regardless.
299
- const mcpGate = (0, hooks_1.mcpRegistrationGate)();
300
- if (mcpGate.register) {
301
- results.push([".mcp.json", (0, hooks_1.upsertClaudeMcpConfig)()]);
302
- results.push([".cursor/mcp.json", (0, hooks_1.upsertCursorMcpConfig)()]);
303
- results.push([".gemini/settings.json mcpServers", (0, hooks_1.upsertGeminiMcpConfig)()]);
304
- }
305
- else {
306
- results.push([".mcp.json", mcpGate.reason]);
307
- results.push([".cursor/mcp.json", mcpGate.reason]);
308
- results.push([".gemini/settings.json mcpServers", mcpGate.reason]);
332
+ if (writeClaudeSurface || writeCursorSurface || writeGeminiSurface) {
333
+ const mcpGate = (0, hooks_1.mcpRegistrationGate)();
334
+ if (mcpGate.register) {
335
+ if (writeClaudeSurface)
336
+ results.push([".mcp.json", (0, hooks_1.upsertClaudeMcpConfig)()]);
337
+ if (writeCursorSurface)
338
+ results.push([".cursor/mcp.json", (0, hooks_1.upsertCursorMcpConfig)()]);
339
+ if (writeGeminiSurface)
340
+ results.push([".gemini/settings.json mcpServers", (0, hooks_1.upsertGeminiMcpConfig)()]);
341
+ }
342
+ else {
343
+ if (writeClaudeSurface)
344
+ results.push([".mcp.json", mcpGate.reason]);
345
+ if (writeCursorSurface)
346
+ results.push([".cursor/mcp.json", mcpGate.reason]);
347
+ if (writeGeminiSurface)
348
+ results.push([".gemini/settings.json mcpServers", mcpGate.reason]);
349
+ }
309
350
  }
310
351
  // Routers accumulate user-maintained project state after bootstrap, so they are
311
352
  // starter files: templates are written only when the file is absent, never rebuilt.
@@ -37,9 +37,9 @@ exports.runInstallSkillMode = runInstallSkillMode;
37
37
  const fs = __importStar(require("node:fs"));
38
38
  const os = __importStar(require("node:os"));
39
39
  const path = __importStar(require("node:path"));
40
+ const agent_surfaces_1 = require("./agent-surfaces");
40
41
  const args_1 = require("./args");
41
42
  const skillName = "project-librarian";
42
- const allAgentTargets = ["codex", "claude", "cursor", "gemini"];
43
43
  const packageFiles = [
44
44
  "SKILL.md",
45
45
  "dist",
@@ -65,10 +65,10 @@ function installAgents() {
65
65
  const agents = new Set();
66
66
  for (const part of parts) {
67
67
  if (part === "all") {
68
- for (const agent of allAgentTargets)
68
+ for (const agent of agent_surfaces_1.allAgentSurfaces)
69
69
  agents.add(agent);
70
70
  }
71
- else if (allAgentTargets.includes(part)) {
71
+ else if (agent_surfaces_1.allAgentSurfaces.includes(part)) {
72
72
  agents.add(part);
73
73
  }
74
74
  else {
package/dist/modes.js CHANGED
@@ -57,6 +57,7 @@ const fs = __importStar(require("node:fs"));
57
57
  const childProcess = __importStar(require("node:child_process"));
58
58
  const os = __importStar(require("node:os"));
59
59
  const path = __importStar(require("node:path"));
60
+ const agent_surfaces_1 = require("./agent-surfaces");
60
61
  const args_1 = require("./args");
61
62
  const workspace_1 = require("./workspace");
62
63
  const templates_1 = require("./templates");
@@ -66,6 +67,7 @@ const wiki_graph_1 = require("./wiki-graph");
66
67
  const wiki_corpus_1 = require("./wiki-corpus");
67
68
  const wiki_diagnostics_1 = require("./wiki-diagnostics");
68
69
  const scopedAutoIndexThreshold = 40;
70
+ const scopedAutoIndexCharLimit = 7600;
69
71
  const scopedAutoIndexMarker = "<!-- PROJECT-WIKI-SCOPED-AUTO-INDEX -->";
70
72
  function isScopedAutoIndex(file) {
71
73
  return /^wiki\/indexes\/auto-[a-z0-9-]+\.md$/.test(file);
@@ -89,16 +91,18 @@ function routeAreaForWikiFile(file) {
89
91
  return slugPart(directory);
90
92
  return "misc";
91
93
  }
92
- function scopedIndexPath(area) {
93
- return `wiki/indexes/auto-${slugPart(area)}.md`;
94
+ function scopedIndexPath(area, partIndex = 0, partCount = 1) {
95
+ const slug = slugPart(area);
96
+ return partCount <= 1 ? `wiki/indexes/auto-${slug}.md` : `wiki/indexes/auto-${slug}-${partIndex + 1}.md`;
94
97
  }
95
- function scopedIndexContent(area, files) {
98
+ function scopedIndexContent(area, files, partIndex = 0, partCount = 1) {
99
+ const title = partCount <= 1 ? area : `${area} (${partIndex + 1}/${partCount})`;
96
100
  const rows = files.map((file) => {
97
101
  const meta = (0, wiki_files_1.metadataSummary)(file, (0, workspace_1.read)(file));
98
102
  return `| ${(0, wiki_files_1.wikiLinkForFile)(file)} | ${meta.scope} | ${meta.status} | ${meta.budget} |`;
99
103
  }).join("\n");
100
104
  return `${(0, templates_1.metadata)("wiki-router", "medium", "wiki/meta/wiki-ops-v1-decisions.md", "auto-discovered scoped routes change")}${scopedAutoIndexMarker}
101
- # Auto Index: ${area}
105
+ # Auto Index: ${title}
102
106
 
103
107
  ## TL;DR
104
108
 
@@ -110,6 +114,23 @@ function scopedIndexContent(area, files) {
110
114
  ${rows}
111
115
  `;
112
116
  }
117
+ function splitScopedIndexFiles(area, files) {
118
+ const parts = [];
119
+ let current = [];
120
+ for (const file of files) {
121
+ const candidate = [...current, file];
122
+ if (current.length > 0 && scopedIndexContent(area, candidate).length > scopedAutoIndexCharLimit) {
123
+ parts.push(current);
124
+ current = [file];
125
+ }
126
+ else {
127
+ current = candidate;
128
+ }
129
+ }
130
+ if (current.length > 0)
131
+ parts.push(current);
132
+ return parts;
133
+ }
113
134
  function removeStaleScopedAutoIndexes(keepPaths) {
114
135
  if (!(0, workspace_1.exists)("wiki/indexes"))
115
136
  return;
@@ -126,16 +147,23 @@ function syncScopedAutoIndexes(files) {
126
147
  const area = routeAreaForWikiFile(file);
127
148
  groups.set(area, [...(groups.get(area) ?? []), file]);
128
149
  }
129
- const summaries = Array.from(groups.entries()).map(([area, areaFiles]) => ({
130
- area,
131
- count: areaFiles.length,
132
- file: scopedIndexPath(area),
133
- files: areaFiles.sort(),
134
- })).sort((left, right) => right.count - left.count || left.area.localeCompare(right.area));
150
+ const summaries = Array.from(groups.entries()).flatMap(([area, areaFiles]) => {
151
+ const sortedFiles = areaFiles.sort();
152
+ const parts = splitScopedIndexFiles(area, sortedFiles);
153
+ return parts.map((files, partIndex) => ({
154
+ area: parts.length <= 1 ? area : `${area} ${partIndex + 1}`,
155
+ baseArea: area,
156
+ count: files.length,
157
+ file: scopedIndexPath(area, partIndex, parts.length),
158
+ files,
159
+ partIndex,
160
+ partCount: parts.length,
161
+ }));
162
+ }).sort((left, right) => right.count - left.count || left.area.localeCompare(right.area));
135
163
  const keepPaths = new Set(summaries.map((summary) => summary.file));
136
164
  removeStaleScopedAutoIndexes(keepPaths);
137
165
  for (const summary of summaries)
138
- (0, workspace_1.write)(summary.file, scopedIndexContent(summary.area, summary.files));
166
+ (0, workspace_1.write)(summary.file, scopedIndexContent(summary.baseArea, summary.files, summary.partIndex, summary.partCount));
139
167
  return summaries.map(({ area, count, file }) => ({ area, count, file }));
140
168
  }
141
169
  function buildRefreshIndexBlock() {
@@ -149,8 +177,6 @@ function buildRefreshIndexBlock() {
149
177
  return `<!-- PROJECT-WIKI-AUTO-INDEX:START -->
150
178
  ## Auto-Discovered Pages
151
179
 
152
- This block is managed by \`--refresh-index\`. Large route sets are split into scoped generated routers to keep \`wiki/index.md\` within startup-hook budget.
153
-
154
180
  | Scoped Router | Area | Pages |
155
181
  | --- | --- | ---: |
156
182
  ${rows}
@@ -192,6 +218,10 @@ function scoreQueryBlock(block, terms) {
192
218
  function hasMigrationQueryIntent(terms) {
193
219
  return terms.some((term) => /^(migrat|legacy|coverage|unit|ledger|review)/.test(term));
194
220
  }
221
+ function hasDeepReferenceQueryIntent(terms) {
222
+ return hasMigrationQueryIntent(terms)
223
+ || terms.some((term) => /^(archive|audit|decision|detail|history|ledger|raw|record|source|trace|verification)/.test(term));
224
+ }
195
225
  function isMigrationSurface(file, meta) {
196
226
  return file.startsWith("wiki/migration/")
197
227
  || /(?:^|-)migration(?:-|$)/.test(file)
@@ -204,6 +234,9 @@ function querySurfaceScore(file, meta, rawScore, terms) {
204
234
  return Math.max(1, Math.floor(rawScore * 0.25) - 20);
205
235
  }
206
236
  let score = rawScore;
237
+ if (meta.budget === "on-demand" && !hasDeepReferenceQueryIntent(terms)) {
238
+ score = Math.max(1, Math.min(Math.floor(score * 0.5), 24));
239
+ }
207
240
  if (file.startsWith("wiki/canonical/") && meta.status === "active")
208
241
  score += 12;
209
242
  else if (meta.status === "active")
@@ -889,15 +922,9 @@ const commonLintRequiredFiles = [
889
922
  ".githooks/prepare-commit-msg",
890
923
  ".githooks/wiki-commit-trailers.js",
891
924
  ];
892
- const agentLintRequiredFiles = {
893
- codex: [".codex/hooks/wiki-session-start.js", ".codex/hooks.json"],
894
- claude: ["CLAUDE.md", ".claude/hooks/wiki-session-start.js", ".claude/settings.json"],
895
- cursor: [".cursor/rules/project-librarian.mdc", ".cursor/hooks/wiki-session-start.js", ".cursor/hooks.json"],
896
- gemini: ["GEMINI.md", ".gemini/hooks/wiki-session-start.js", ".gemini/settings.json"],
897
- };
898
925
  function activeLintAgentSurfaces() {
899
926
  const active = new Set();
900
- for (const [agent, files] of Object.entries(agentLintRequiredFiles)) {
927
+ for (const [agent, files] of Object.entries(agent_surfaces_1.agentSurfaceRequiredFiles)) {
901
928
  if (files.some((file) => (0, workspace_1.exists)(file)))
902
929
  active.add(agent);
903
930
  }
@@ -909,7 +936,7 @@ function runLintMode(corpus) {
909
936
  const activeAgents = activeLintAgentSurfaces();
910
937
  const requiredFiles = [
911
938
  ...commonLintRequiredFiles,
912
- ...Array.from(activeAgents).flatMap((agent) => agentLintRequiredFiles[agent]),
939
+ ...Array.from(activeAgents).flatMap((agent) => agent_surfaces_1.agentSurfaceRequiredFiles[agent]),
913
940
  ];
914
941
  for (const file of requiredFiles) {
915
942
  if (!(0, workspace_1.exists)(file))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "project-librarian",
3
- "version": "0.4.1",
3
+ "version": "0.4.2",
4
4
  "description": "Create and maintain compact project context for humans and LLM coding agents.",
5
5
  "license": "MIT",
6
6
  "type": "commonjs",
@@ -41,19 +41,25 @@
41
41
  "benchmark:ci": "npm run benchmark:ci-smoke",
42
42
  "benchmark:ci-smoke": "npm run benchmark:llm:parse-smoke && npm run benchmark:llm:dry-run",
43
43
  "benchmark:llm": "npm run build && node benchmarks/codex-llm-metrics.js",
44
+ "benchmark:llm:delta-analysis": "node benchmarks/tools/analyze-llm-deltas.js",
44
45
  "benchmark:llm:dry-run": "npm run build && node benchmarks/codex-llm-metrics.js --dry-run",
45
46
  "benchmark:llm:parse-smoke": "node tests/validators/codex-llm-benchmark-smoke.js",
46
47
  "benchmark:llm:prune-raw": "node benchmarks/tools/prune-llm-raw.js",
48
+ "benchmark:llm:raw-audit": "node benchmarks/tools/audit-llm-raw.js",
47
49
  "benchmark:injection-sentinel": "node benchmarks/tools/injection-sentinel.js",
50
+ "benchmark:agent-surface-smoke": "node benchmarks/tools/agent-surface-smoke.js",
48
51
  "benchmark:claim-ledger": "node benchmarks/tools/benchmark-claim-ledger.js benchmarks/llm/samples/codex-measured-report.json benchmarks/reports/llm/payload-preview.json",
49
52
  "benchmark:real-corpus:demo": "npm run build && node benchmarks/tools/real-corpus-offline-demo.js",
50
53
  "benchmark:release:preview": "npm run benchmark:llm -- --payload-preview benchmarks/reports/llm/payload-preview.json --sanitized-pack --full-matrix --runs 3 --warmup-runs 1 --min-runs-for-claim 3 --require-clean --require-claimable --model gpt-5.5",
54
+ "benchmark:release:diagnose": "npm run benchmark:llm -- --sanitized-pack --runs 1 --warmup-runs 0 --model gpt-5.5 --out benchmarks/reports/llm/diagnostic-current.json --markdown benchmarks/reports/llm/diagnostic-current.md",
51
55
  "benchmark:release": "npm run benchmark:llm -- --sanitized-pack --full-matrix --runs 3 --warmup-runs 1 --min-runs-for-claim 3 --require-clean --require-claimable --model gpt-5.5 --out benchmarks/reports/llm/current.json --markdown benchmarks/reports/llm/current.md",
52
56
  "build": "tsc && chmod +x dist/init-project-wiki.js",
53
57
  "perf:code-efficiency": "node benchmarks/tools/code-performance-efficiency.js --full",
54
58
  "release:check": "node benchmarks/tools/release-readiness.js",
55
59
  "typecheck": "tsc --noEmit",
60
+ "typecheck:ts7": "npx --yes -p typescript@rc tsc --noEmit --project tsconfig.json",
56
61
  "unit": "node --test tests/unit/*.test.js",
62
+ "test:coverage": "node --test --experimental-test-coverage --test-coverage-include=dist/**/*.js --test-coverage-include=benchmarks/**/*.js --test-coverage-lines=65 --test-coverage-branches=65 --test-coverage-functions=65 tests/unit/*.test.js",
57
63
  "test": "npm run build && npm run typecheck && npm run unit && bash tests/smoke.sh",
58
64
  "prepack": "npm run build"
59
65
  },