project-librarian 0.5.8 → 0.6.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/README.ko.md +5 -2
- package/README.md +5 -2
- package/dist/args.js +7 -8
- package/dist/init-project-wiki.js +5 -17
- package/dist/modes.js +13 -2
- package/dist/native/linux-arm64-musl/project-librarian-indexer +0 -0
- package/dist/native/linux-x64/project-librarian-indexer +0 -0
- package/dist/native/project-librarian-indexer-manifest.json +6 -6
- package/dist/native/win32-arm64/project-librarian-indexer.exe +0 -0
- package/dist/native/win32-x64/project-librarian-indexer.exe +0 -0
- package/dist/wiki-diagnostics.js +111 -1
- package/dist/wiki-graph.js +123 -0
- package/docs/benchmarks.md +31 -11
- package/docs/cli-reference.md +13 -3
- package/docs/ko/benchmarks.md +31 -11
- package/docs/ko/cli-reference.md +13 -3
- package/docs/ko/usage.md +3 -5
- package/docs/usage.md +3 -5
- package/package.json +2 -2
- package/dist/wiki-concepts.js +0 -49
- package/dist/wiki-visualizer.js +0 -558
package/README.ko.md
CHANGED
|
@@ -77,9 +77,9 @@ Project Librarian은 작은 첫 읽기와 더 깊은 정본으로 가는 경로
|
|
|
77
77
|
- **첫 읽기를 작게.** 시작 훅은 `wiki/startup.md`와 `wiki/index.md`만 주입하고, 에이전트는 필요할 때 더 깊은 페이지로 이동합니다.
|
|
78
78
|
- **한 번 설정, 네 에이전트.** Codex, Claude Code, Cursor, Gemini CLI가 같은 저장소 로컬 기억 계약을 공유합니다.
|
|
79
79
|
- **구조적인 위키 작성.** 새 프로젝트 내용은 작성하거나 취합하기 전에 분류하므로 PRD, 정책, UX, 데이터, API, QA, 릴리즈, 운영 메모가 하나의 잡다한 페이지로 합쳐지지 않습니다.
|
|
80
|
-
- **검토 가능한 위키 그래프.** `--wiki-visualize`는 시작 컨텍스트를 늘리지 않고 `.project-wiki/` 아래에 정적 HTML 그래프를 작성합니다.
|
|
81
80
|
- **측정된 주장.** 벤치마크에서 이긴 경우와 손해를 본 경우를 주장 경계와 함께 공개합니다.
|
|
82
81
|
- **로컬 세션 핸드오프.** `--handoff-save`는 `.project-wiki/session/` 아래에 생성형 재개 메모를 저장하되 실행 기억을 프로젝트 정본으로 바꾸지 않습니다.
|
|
82
|
+
- **답변형 위키 토폴로지.** `--wiki-impact`, `--wiki-neighborhood`, topology diagnostics가 graph visualizer 없이 backlinks, decision 근거, router depth, 가까운 읽기 순서를 노출합니다.
|
|
83
83
|
- **선택적 코드 근거.** SQLite 인덱스와 답변 형태 MCP 도구가 큰 저장소의 비싼 탐색 질문에 답하며, MCP SDK 의존성은 추가하지 않습니다.
|
|
84
84
|
- **다시 실행해도 안전.** 부트스트랩은 멱등하고 기존 내용 보존을 우선합니다.
|
|
85
85
|
|
|
@@ -94,6 +94,7 @@ Project Librarian은 작은 첫 읽기와 더 깊은 정본으로 가는 경로
|
|
|
94
94
|
| 기존 문서/위키 마이그레이션 | "Project Librarian으로 기존 docs/wiki 내용을 마이그레이션해줘." |
|
|
95
95
|
| 진단 실행 | "Project Librarian 진단을 실행해줘." |
|
|
96
96
|
| 프로젝트 기억 검색 | "Project Librarian 위키에서 authentication decisions를 찾아줘." |
|
|
97
|
+
| 가까운 위키 맥락 찾기 | "canonical/project-brief의 Project Librarian wiki neighborhood를 보여줘." |
|
|
97
98
|
| 코드 근거 구축 | "Project Librarian 코드 근거를 `src`에 대해 구축해줘." |
|
|
98
99
|
| 코드 영향 확인 | "`healthHandler`에 대한 Project Librarian 영향 근거를 보여줘." |
|
|
99
100
|
| 세션 핸드오프 저장 | "현재 작업에 대한 Project Librarian 세션 핸드오프를 저장해줘." |
|
|
@@ -115,7 +116,7 @@ Project Librarian은 작은 첫 읽기와 더 깊은 정본으로 가는 경로
|
|
|
115
116
|
| 중형 | 45.95% 적음 | 37.42% 적음 | 11.88% 적음 | 4.30% 적음 | 69.12% 적음 |
|
|
116
117
|
| 대형 | 66.97% 적음 | 69.87% 적음 | 31.90% 적음 | 35.19% 적음 | 95.58% 적음 |
|
|
117
118
|
|
|
118
|
-
위키 트랙 작업 유형별 비용 가중 토큰
|
|
119
|
+
위키 트랙 작업 유형별 비용 가중 토큰 변화, 전체 규모 합산:
|
|
119
120
|
|
|
120
121
|
| 작업 유형 | 변화 |
|
|
121
122
|
| --- | ---: |
|
|
@@ -127,6 +128,8 @@ Project Librarian은 작은 첫 읽기와 더 깊은 정본으로 가는 경로
|
|
|
127
128
|
| multi_session | 52.58% 적음 |
|
|
128
129
|
| aggregation | 42.53% 적음 |
|
|
129
130
|
|
|
131
|
+
규모별 값은 서로 다릅니다. 소형/중형/대형 작업 유형별 실제 행렬과 아직 역전이 남은 셀은 [벤치마크 근거](docs/ko/benchmarks.md#위키-트랙)에 있습니다.
|
|
132
|
+
|
|
130
133
|
실행 시간과 명령 수의 한계도 있습니다. 이번 보고서에서 `code_impact`, `change_location`, `aggregation`은 토큰과 출력 바이트가 모두 개선됐지만, 실행 시간이나 명령 수에서는 일부 역전이 남아 있습니다.
|
|
131
134
|
|
|
132
135
|
코드 그래프 트랙, 대조군 대비 비용 가중 토큰:
|
package/README.md
CHANGED
|
@@ -77,9 +77,9 @@ The core idea is not "write more docs." It is "keep the first agent read small,
|
|
|
77
77
|
- **Small first read.** Startup hooks inject only `wiki/startup.md` and `wiki/index.md`; agents route to deeper pages on demand.
|
|
78
78
|
- **One setup, four agents.** Codex, Claude Code, Cursor, and Gemini CLI share the same repo-local memory contract.
|
|
79
79
|
- **Structured wiki writing.** New project content is classified before it is written or consolidated, so PRDs, policies, UX, data, APIs, QA, release, and operations notes do not collapse into one catch-all page.
|
|
80
|
-
- **Inspectable wiki graph.** `--wiki-visualize` writes a static HTML graph under `.project-wiki/` without adding to startup context.
|
|
81
80
|
- **Measured claims.** Benchmark wins and losses are published together, with claim boundaries attached.
|
|
82
81
|
- **Local session handoff.** `--handoff-save` stores generated resume notes under `.project-wiki/session/` without turning execution memory into canonical project truth.
|
|
82
|
+
- **Answer-shaped wiki topology.** `--wiki-impact`, `--wiki-neighborhood`, and topology diagnostics expose backlinks, decision evidence, router depth, and nearby read order without a graph visualizer.
|
|
83
83
|
- **Optional code evidence.** A SQLite index plus answer-shaped MCP tools answer expensive traversal questions on large repositories without adding an MCP SDK dependency.
|
|
84
84
|
- **Safe to re-run.** Bootstrap is idempotent and preservation-first.
|
|
85
85
|
|
|
@@ -94,6 +94,7 @@ Ask your agent for the outcome you want; the installed skill maps the request to
|
|
|
94
94
|
| Migrate existing docs/wiki content | "Use Project Librarian to migrate the existing docs/wiki content." |
|
|
95
95
|
| Run diagnostics | "Run Project Librarian diagnostics." |
|
|
96
96
|
| Search project memory | "Search the Project Librarian wiki for authentication decisions." |
|
|
97
|
+
| Find nearby wiki context | "Show Project Librarian wiki neighborhood for canonical/project-brief." |
|
|
97
98
|
| Build code evidence | "Build Project Librarian code evidence for `src`." |
|
|
98
99
|
| Inspect code impact | "Show Project Librarian impact evidence for `healthHandler`." |
|
|
99
100
|
| Save a handoff | "Save a Project Librarian session handoff for the current work." |
|
|
@@ -115,7 +116,7 @@ Wiki track aggregate deltas vs control:
|
|
|
115
116
|
| Medium | 45.95% less | 37.42% less | 11.88% less | 4.30% less | 69.12% less |
|
|
116
117
|
| Large | 66.97% less | 69.87% less | 31.90% less | 35.19% less | 95.58% less |
|
|
117
118
|
|
|
118
|
-
Wiki track task-family cost-weighted token deltas vs control:
|
|
119
|
+
Wiki track task-family cost-weighted token deltas vs control, all scales combined:
|
|
119
120
|
|
|
120
121
|
| Task family | Delta |
|
|
121
122
|
| --- | ---: |
|
|
@@ -127,6 +128,8 @@ Wiki track task-family cost-weighted token deltas vs control:
|
|
|
127
128
|
| multi_session | 52.58% less |
|
|
128
129
|
| aggregation | 42.53% less |
|
|
129
130
|
|
|
131
|
+
Scale-specific values vary; see [Benchmark Evidence](docs/benchmarks.md#wiki-track) for the small/medium/large task-family matrix and the cells that still regress.
|
|
132
|
+
|
|
130
133
|
Timing and command-count caveat: `code_impact`, `change_location`, and `aggregation` still had wall-time or command-count regressions in this report, even though token and output-byte metrics improved for every task family.
|
|
131
134
|
|
|
132
135
|
Code-graph track, cost-weighted tokens vs control:
|
package/dist/args.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.codeIndexMode = exports.acknowledgeSmallRepoMode = exports.noGitConfigMode = exports.reviewMigrationMode = exports.pruneCheckStrictMode = exports.pruneCheckMode = exports.captureInboxMode = exports.refreshIndexMode = exports.issueDraftMode = exports.issueCreateMode = exports.handoffVerification = exports.handoffStatusMode = exports.handoffState = exports.handoffShowMode = exports.handoffSaveMode = exports.handoffPromoteInboxMode = exports.handoffInjectionStatusMode = exports.handoffInjectionEnableMode = exports.handoffInjectionDisableMode = exports.handoffOpenQuestions = exports.handoffNextActions = exports.handoffLastSuccessCommand = exports.handoffLastFailureCommand = exports.handoffInputMode = exports.handoffGoal = exports.handoffDecisions = exports.handoffClearMode = exports.handoffBlocked = 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 = exports.codeQuerySql = exports.codeImpactTarget = exports.codeContextPackTarget = exports.
|
|
4
|
+
exports.issueDraftTitle = exports.issueBodyFile = exports.captureCategory = exports.captureContent = exports.captureTitle = exports.codeIndexScopes = exports.codeParser = exports.codeIndexOutput = exports.codeSearchSymbol = exports.codeReportSection = exports.codeQuerySql = exports.codeImpactTarget = exports.codeContextPackTarget = exports.wikiNeighborhoodTarget = exports.wikiNeighborhoodMode = 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.codeIndexMigrateMode = exports.codeIndexIncrementalMode = exports.codeIndexEngineMode = exports.codeIndexEngine = void 0;
|
|
5
5
|
exports.parseArgs = parseArgs;
|
|
6
6
|
exports.argValue = argValue;
|
|
7
7
|
exports.argValues = argValues;
|
|
@@ -72,10 +72,8 @@ const flagDefinitions = [
|
|
|
72
72
|
{ name: "--review-migration", value: "none", aliases: ["--semantic-migrate"] },
|
|
73
73
|
{ name: "--scope", value: "value" },
|
|
74
74
|
{ name: "--title", value: "value" },
|
|
75
|
-
{ name: "--wiki-graph-html", value: "none" },
|
|
76
75
|
{ name: "--wiki-impact", value: "value" },
|
|
77
|
-
{ name: "--wiki-
|
|
78
|
-
{ name: "--wiki-visualize-out", value: "value" },
|
|
76
|
+
{ name: "--wiki-neighborhood", value: "value" },
|
|
79
77
|
];
|
|
80
78
|
function definitionNames(definition) {
|
|
81
79
|
return [definition.name, ...(definition.aliases ?? [])];
|
|
@@ -156,6 +154,7 @@ function parseArgs(argv) {
|
|
|
156
154
|
const codeContextPackTarget = argValueFromAny("--code-context-pack");
|
|
157
155
|
const codeQuerySql = argValueFromAny("--code-query");
|
|
158
156
|
const codeSearchSymbol = argValueFromAny("--code-search-symbol");
|
|
157
|
+
const wikiNeighborhoodTarget = argValue("--wiki-neighborhood");
|
|
159
158
|
const parsedAgentTargets = (0, agent_surfaces_1.parseAgentSurfaceValues)(argValues("--agents"));
|
|
160
159
|
const handoffInputMode = [
|
|
161
160
|
"--blocked",
|
|
@@ -254,8 +253,8 @@ function parseArgs(argv) {
|
|
|
254
253
|
.filter((arg) => !knownFlags.has(arg)))),
|
|
255
254
|
wikiImpactMode: hasFlag("--wiki-impact"),
|
|
256
255
|
wikiImpactTarget: argValue("--wiki-impact"),
|
|
257
|
-
|
|
258
|
-
|
|
256
|
+
wikiNeighborhoodMode: hasFlag("--wiki-neighborhood"),
|
|
257
|
+
wikiNeighborhoodTarget,
|
|
259
258
|
};
|
|
260
259
|
}
|
|
261
260
|
exports.parsedArgs = parseArgs(exports.rawArgs);
|
|
@@ -330,8 +329,8 @@ function argValues(name) {
|
|
|
330
329
|
exports.queryTerm = exports.parsedArgs.queryTerm;
|
|
331
330
|
exports.wikiImpactMode = exports.parsedArgs.wikiImpactMode;
|
|
332
331
|
exports.wikiImpactTarget = exports.parsedArgs.wikiImpactTarget;
|
|
333
|
-
exports.
|
|
334
|
-
exports.
|
|
332
|
+
exports.wikiNeighborhoodMode = exports.parsedArgs.wikiNeighborhoodMode;
|
|
333
|
+
exports.wikiNeighborhoodTarget = exports.parsedArgs.wikiNeighborhoodTarget;
|
|
335
334
|
exports.codeContextPackTarget = exports.parsedArgs.codeContextPackTarget;
|
|
336
335
|
exports.codeImpactTarget = exports.parsedArgs.codeImpactTarget;
|
|
337
336
|
exports.codeQuerySql = exports.parsedArgs.codeQuerySql;
|
|
@@ -9,7 +9,6 @@ const modes_1 = require("./modes");
|
|
|
9
9
|
const migration_1 = require("./migration");
|
|
10
10
|
const session_handoff_1 = require("./session-handoff");
|
|
11
11
|
const templates_1 = require("./templates");
|
|
12
|
-
const wiki_visualizer_1 = require("./wiki-visualizer");
|
|
13
12
|
const workspace_1 = require("./workspace");
|
|
14
13
|
function codeIndex() {
|
|
15
14
|
return require("./code-index");
|
|
@@ -37,8 +36,8 @@ Options:
|
|
|
37
36
|
--dry-run With install, preview copied skill files without writing them.
|
|
38
37
|
--query <terms> Search wiki paths, metadata, titles, and bodies (answer-shaped, capped output).
|
|
39
38
|
--wiki-impact <page-or-term> Show wiki backlinks, decision_ref citations, and router depth for matching pages.
|
|
40
|
-
--wiki-
|
|
41
|
-
|
|
39
|
+
--wiki-neighborhood <page-or-term>
|
|
40
|
+
Show a bounded read order for nearby wiki pages.
|
|
42
41
|
--refresh-index Update the managed auto-discovered wiki index block.
|
|
43
42
|
--capture-inbox Append a candidate note with --title, --content, and optional --category.
|
|
44
43
|
--handoff-save Save local generated session handoff state under .project-wiki/session/.
|
|
@@ -147,10 +146,6 @@ if (args_1.codeReportSection && !args_1.codeReportMode) {
|
|
|
147
146
|
console.error("--code-report-section is only supported with --code-report.");
|
|
148
147
|
process.exit(1);
|
|
149
148
|
}
|
|
150
|
-
if (args_1.wikiVisualizeOutput && !args_1.wikiVisualizeMode) {
|
|
151
|
-
console.error("--wiki-visualize-out is only supported with --wiki-visualize.");
|
|
152
|
-
process.exit(1);
|
|
153
|
-
}
|
|
154
149
|
if (args_1.pruneCheckStrictMode && !args_1.pruneCheckMode) {
|
|
155
150
|
console.error("--prune-check-strict is only supported with --prune-check.");
|
|
156
151
|
process.exit(1);
|
|
@@ -266,16 +261,9 @@ function runInitCommand() {
|
|
|
266
261
|
exitAfterStdoutDrain(0);
|
|
267
262
|
return;
|
|
268
263
|
}
|
|
269
|
-
if (args_1.
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
console.log(`Project wiki visualizer written: ${output}`);
|
|
273
|
-
exitAfterStdoutDrain(0);
|
|
274
|
-
}
|
|
275
|
-
catch (error) {
|
|
276
|
-
console.error(error instanceof Error ? error.message : String(error));
|
|
277
|
-
process.exit(1);
|
|
278
|
-
}
|
|
264
|
+
if (args_1.wikiNeighborhoodMode) {
|
|
265
|
+
(0, modes_1.runWikiNeighborhoodMode)();
|
|
266
|
+
exitAfterStdoutDrain(0);
|
|
279
267
|
return;
|
|
280
268
|
}
|
|
281
269
|
if (args_1.queryTerm) {
|
package/dist/modes.js
CHANGED
|
@@ -36,6 +36,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
36
36
|
exports.buildRefreshIndexBlock = buildRefreshIndexBlock;
|
|
37
37
|
exports.runQueryMode = runQueryMode;
|
|
38
38
|
exports.runWikiImpactMode = runWikiImpactMode;
|
|
39
|
+
exports.runWikiNeighborhoodMode = runWikiNeighborhoodMode;
|
|
39
40
|
exports.projectCandidatesContent = projectCandidatesContent;
|
|
40
41
|
exports.appendProjectCandidate = appendProjectCandidate;
|
|
41
42
|
exports.appendCaptureInbox = appendCaptureInbox;
|
|
@@ -325,6 +326,14 @@ function runWikiImpactMode() {
|
|
|
325
326
|
const corpus = (0, wiki_corpus_1.loadWikiCorpus)();
|
|
326
327
|
console.log((0, wiki_graph_1.wikiImpactAnswer)(corpus.pages, args_1.wikiImpactTarget.trim(), (0, wiki_corpus_1.wikiCorpusGraph)(corpus)));
|
|
327
328
|
}
|
|
329
|
+
function runWikiNeighborhoodMode() {
|
|
330
|
+
if (!args_1.wikiNeighborhoodTarget.trim()) {
|
|
331
|
+
console.error("missing wiki neighborhood target: use --wiki-neighborhood \"page-or-term\"");
|
|
332
|
+
process.exit(1);
|
|
333
|
+
}
|
|
334
|
+
const corpus = (0, wiki_corpus_1.loadWikiCorpus)();
|
|
335
|
+
console.log((0, wiki_graph_1.wikiNeighborhoodAnswer)(corpus.pages, args_1.wikiNeighborhoodTarget.trim(), (0, wiki_corpus_1.wikiCorpusGraph)(corpus)));
|
|
336
|
+
}
|
|
328
337
|
function projectCandidatesContent() {
|
|
329
338
|
return `${(0, templates_1.metadata)("inbox", "on-demand", "wiki/meta/wiki-ops-v1-decisions.md", "candidates are adopted, rejected, or stale")}
|
|
330
339
|
# Project Candidates Inbox
|
|
@@ -699,6 +708,7 @@ function collectLinkDiagnostics(corpus = (0, wiki_corpus_1.loadWikiCorpus)()) {
|
|
|
699
708
|
}
|
|
700
709
|
}
|
|
701
710
|
}
|
|
711
|
+
diagnostics.push(...(0, wiki_diagnostics_1.collectTopologyDiagnostics)(corpus));
|
|
702
712
|
return diagnostics.sort((a, b) => a.severity.localeCompare(b.severity) || a.file.localeCompare(b.file) || a.code.localeCompare(b.code));
|
|
703
713
|
}
|
|
704
714
|
function legacyWikiRoots() {
|
|
@@ -725,9 +735,10 @@ function migrationLegacyReferenceDiagnostics(files, corpus) {
|
|
|
725
735
|
message: "new project truth must not link to or cite wiki_legacy*; migrate the meaning or keep unresolved material in migration inboxes",
|
|
726
736
|
}));
|
|
727
737
|
}
|
|
728
|
-
function collectQualityDiagnostics(corpus = (0, wiki_corpus_1.loadWikiCorpus)()) {
|
|
738
|
+
function collectQualityDiagnostics(corpus = (0, wiki_corpus_1.loadWikiCorpus)(), options = {}) {
|
|
729
739
|
const diagnostics = [];
|
|
730
740
|
const files = corpus.files;
|
|
741
|
+
const currentDate = options.today ?? workspace_1.today;
|
|
731
742
|
const titles = new Map();
|
|
732
743
|
for (const file of files) {
|
|
733
744
|
const text = (0, wiki_corpus_1.wikiCorpusText)(corpus, file);
|
|
@@ -742,7 +753,7 @@ function collectQualityDiagnostics(corpus = (0, wiki_corpus_1.loadWikiCorpus)())
|
|
|
742
753
|
if (tldrExpected && !/##\s+TL;DR/.test(body)) {
|
|
743
754
|
diagnostics.push({ code: "missing-tldr", severity: "warn", file, message: "add a compact TL;DR near the top" });
|
|
744
755
|
}
|
|
745
|
-
const reviewAge = updated ? (0, wiki_diagnostics_1.staleReviewAge)(updated,
|
|
756
|
+
const reviewAge = updated ? (0, wiki_diagnostics_1.staleReviewAge)(updated, currentDate) : null;
|
|
746
757
|
if (status === "active" && reviewAge !== null && /project-canonical|project-decisions|source-summary|wiki-meta/.test(scope)) {
|
|
747
758
|
diagnostics.push({ code: "stale-review", severity: "warn", file, message: `updated ${reviewAge} days ago: ${updated}` });
|
|
748
759
|
}
|
|
Binary file
|
|
Binary file
|
|
@@ -29,16 +29,16 @@
|
|
|
29
29
|
"architecture": "arm64",
|
|
30
30
|
"format": "elf",
|
|
31
31
|
"path": "dist/native/linux-arm64-musl/project-librarian-indexer",
|
|
32
|
-
"sha256": "
|
|
33
|
-
"size":
|
|
32
|
+
"sha256": "bcb48020ae87fe3e5ebd6a8d986a91f83e67e5d69c4df9357a7c3a6c78c232b0",
|
|
33
|
+
"size": 3101344,
|
|
34
34
|
"triple": "linux-arm64-musl"
|
|
35
35
|
},
|
|
36
36
|
{
|
|
37
37
|
"architecture": "x64",
|
|
38
38
|
"format": "elf",
|
|
39
39
|
"path": "dist/native/linux-x64/project-librarian-indexer",
|
|
40
|
-
"sha256": "
|
|
41
|
-
"size":
|
|
40
|
+
"sha256": "22d32884276c23c4597bd611facd5880936b29600c8331bb3d585d2ab370a206",
|
|
41
|
+
"size": 3110688,
|
|
42
42
|
"triple": "linux-x64"
|
|
43
43
|
},
|
|
44
44
|
{
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"architecture": "arm64",
|
|
54
54
|
"format": "pe",
|
|
55
55
|
"path": "dist/native/win32-arm64/project-librarian-indexer.exe",
|
|
56
|
-
"sha256": "
|
|
56
|
+
"sha256": "b49ad30a72a9a93ff524cc1643b141c94de4850655ce646d80579edc5b832f71",
|
|
57
57
|
"size": 2098688,
|
|
58
58
|
"triple": "win32-arm64"
|
|
59
59
|
},
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
"architecture": "x64",
|
|
62
62
|
"format": "pe",
|
|
63
63
|
"path": "dist/native/win32-x64/project-librarian-indexer.exe",
|
|
64
|
-
"sha256": "
|
|
64
|
+
"sha256": "36a8416e4975100f89bc57281b311b45158469fb12ea37d33dac2d5855a5e47a",
|
|
65
65
|
"size": 2375680,
|
|
66
66
|
"triple": "win32-x64"
|
|
67
67
|
}
|
|
Binary file
|
|
Binary file
|
package/dist/wiki-diagnostics.js
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.staleReviewAgeDays = void 0;
|
|
3
|
+
exports.topologyFanoutThreshold = exports.topologyHubOverloadThreshold = exports.staleReviewAgeDays = void 0;
|
|
4
4
|
exports.staleReviewAge = staleReviewAge;
|
|
5
|
+
exports.collectTopologyDiagnostics = collectTopologyDiagnostics;
|
|
6
|
+
const wiki_corpus_1 = require("./wiki-corpus");
|
|
7
|
+
const wiki_graph_1 = require("./wiki-graph");
|
|
8
|
+
const workspace_1 = require("./workspace");
|
|
5
9
|
exports.staleReviewAgeDays = 30;
|
|
10
|
+
exports.topologyHubOverloadThreshold = 60;
|
|
11
|
+
exports.topologyFanoutThreshold = 8;
|
|
6
12
|
function dateOnlyMillis(value) {
|
|
7
13
|
if (!/^\d{4}-\d{2}-\d{2}$/.test(value))
|
|
8
14
|
return null;
|
|
@@ -17,3 +23,107 @@ function staleReviewAge(updated, currentDate) {
|
|
|
17
23
|
const ageDays = Math.floor((currentMillis - updatedMillis) / 86_400_000);
|
|
18
24
|
return ageDays > exports.staleReviewAgeDays ? ageDays : null;
|
|
19
25
|
}
|
|
26
|
+
function uniqueExisting(values, fileSet) {
|
|
27
|
+
return Array.from(new Set(values.filter((value) => fileSet.has(value)))).sort();
|
|
28
|
+
}
|
|
29
|
+
function isGeneratedScopedRouter(file) {
|
|
30
|
+
return /^wiki\/indexes\/auto-[a-z0-9-]+(?:-\d+)?\.md$/.test(file);
|
|
31
|
+
}
|
|
32
|
+
function isCanonicalTruthPage(file, text) {
|
|
33
|
+
return file.startsWith("wiki/canonical/") && (0, workspace_1.metadataValue)(text, "status") === "active";
|
|
34
|
+
}
|
|
35
|
+
function hasEvidenceClaimSignal(body) {
|
|
36
|
+
return /\b(source-backed|source backed|research-backed|external research|paper-backed|evidence-backed)\b/i.test(body);
|
|
37
|
+
}
|
|
38
|
+
function hasDecisionRefSignal(text) {
|
|
39
|
+
const decisionRef = (0, workspace_1.metadataValue)(text, "decision_ref").trim().toLowerCase();
|
|
40
|
+
return decisionRef !== "" && decisionRef !== "none";
|
|
41
|
+
}
|
|
42
|
+
function hasFocusedAuthoritySignal(text, body) {
|
|
43
|
+
return hasDecisionRefSignal(text) || hasEvidenceClaimSignal(body);
|
|
44
|
+
}
|
|
45
|
+
function hasEvidenceLink(file, corpus, graph = (0, wiki_corpus_1.wikiCorpusGraph)(corpus)) {
|
|
46
|
+
const outgoingLinks = graph.outgoingLinks.get(file) ?? [];
|
|
47
|
+
const sourceLink = outgoingLinks.some((link) => link.normalizedTarget.startsWith("wiki/sources/") && corpus.fileSet.has(link.normalizedTarget));
|
|
48
|
+
const decisionRef = graph.outgoingDecisionRef.get(file);
|
|
49
|
+
return sourceLink || Boolean(decisionRef && corpus.fileSet.has(decisionRef));
|
|
50
|
+
}
|
|
51
|
+
function isBroadReviewTrigger(trigger) {
|
|
52
|
+
const normalized = trigger.toLowerCase().trim();
|
|
53
|
+
if (!normalized)
|
|
54
|
+
return false;
|
|
55
|
+
return normalized === "changes"
|
|
56
|
+
|| normalized === "project changes"
|
|
57
|
+
|| normalized === "routine review"
|
|
58
|
+
|| /^any\b.*changes$/.test(normalized)
|
|
59
|
+
|| /^general\b.*changes$/.test(normalized);
|
|
60
|
+
}
|
|
61
|
+
function isTopologyHub(file) {
|
|
62
|
+
return file === "wiki/index.md"
|
|
63
|
+
|| file.startsWith("wiki/meta/")
|
|
64
|
+
|| /^wiki\/indexes\/(?!auto-)[^/]+\.md$/.test(file);
|
|
65
|
+
}
|
|
66
|
+
function collectTopologyDiagnostics(corpus) {
|
|
67
|
+
const diagnostics = [];
|
|
68
|
+
const graph = (0, wiki_corpus_1.wikiCorpusGraph)(corpus);
|
|
69
|
+
for (const file of corpus.files) {
|
|
70
|
+
if (isGeneratedScopedRouter(file))
|
|
71
|
+
continue;
|
|
72
|
+
const outgoing = uniqueExisting((graph.outgoingLinks.get(file) ?? [])
|
|
73
|
+
.map((link) => link.normalizedTarget)
|
|
74
|
+
.filter((target) => target !== file), corpus.fileSet);
|
|
75
|
+
if (isTopologyHub(file) && outgoing.length > exports.topologyHubOverloadThreshold) {
|
|
76
|
+
diagnostics.push({
|
|
77
|
+
code: "hub-overload",
|
|
78
|
+
severity: "warn",
|
|
79
|
+
file,
|
|
80
|
+
message: `${outgoing.length} outgoing wiki links exceed the topology hub threshold ${exports.topologyHubOverloadThreshold}; split or scope the route surface`,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
for (const file of corpus.files) {
|
|
85
|
+
const text = (0, wiki_corpus_1.wikiCorpusText)(corpus, file);
|
|
86
|
+
if (isCanonicalTruthPage(file, text)) {
|
|
87
|
+
const body = (0, workspace_1.stripMetadataHeader)(text);
|
|
88
|
+
const incoming = uniqueExisting((graph.incomingLinks.get(file) ?? [])
|
|
89
|
+
.map((link) => link.file)
|
|
90
|
+
.filter((source) => source !== file), corpus.fileSet);
|
|
91
|
+
if (incoming.length > 0 && incoming.every(isGeneratedScopedRouter) && hasFocusedAuthoritySignal(text, body)) {
|
|
92
|
+
diagnostics.push({
|
|
93
|
+
code: "weak-authority-route",
|
|
94
|
+
severity: "warn",
|
|
95
|
+
file,
|
|
96
|
+
message: "active canonical page with authority signals is routed only by generated auto-index pages; add a focused route when this truth is durable",
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
if (hasEvidenceClaimSignal(body) && !hasEvidenceLink(file, corpus, graph)) {
|
|
100
|
+
diagnostics.push({
|
|
101
|
+
code: "missing-evidence-link",
|
|
102
|
+
severity: "warn",
|
|
103
|
+
file,
|
|
104
|
+
message: "canonical page makes a source-backed claim but has no source link or decision_ref evidence link",
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
for (const file of corpus.files) {
|
|
110
|
+
if (file === wiki_graph_1.wikiRouterRoot || isGeneratedScopedRouter(file))
|
|
111
|
+
continue;
|
|
112
|
+
const text = (0, wiki_corpus_1.wikiCorpusText)(corpus, file);
|
|
113
|
+
if ((0, workspace_1.metadataValue)(text, "status") !== "active")
|
|
114
|
+
continue;
|
|
115
|
+
const incoming = uniqueExisting((graph.incomingLinks.get(file) ?? [])
|
|
116
|
+
.map((link) => link.file)
|
|
117
|
+
.filter((source) => source !== file), corpus.fileSet);
|
|
118
|
+
const reviewTrigger = (0, workspace_1.metadataValue)(text, "review_trigger");
|
|
119
|
+
if (incoming.length >= exports.topologyFanoutThreshold && isBroadReviewTrigger(reviewTrigger)) {
|
|
120
|
+
diagnostics.push({
|
|
121
|
+
code: "stale-fanout",
|
|
122
|
+
severity: "warn",
|
|
123
|
+
file,
|
|
124
|
+
message: `${incoming.length} incoming links with broad review_trigger "${reviewTrigger}"; tighten review trigger before broad edits`,
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return diagnostics.sort((a, b) => a.file.localeCompare(b.file) || a.code.localeCompare(b.code));
|
|
129
|
+
}
|
package/dist/wiki-graph.js
CHANGED
|
@@ -6,6 +6,7 @@ exports.buildWikiGraph = buildWikiGraph;
|
|
|
6
6
|
exports.wikiRouterDepths = wikiRouterDepths;
|
|
7
7
|
exports.wikiQueryGraphEvidence = wikiQueryGraphEvidence;
|
|
8
8
|
exports.wikiImpactAnswer = wikiImpactAnswer;
|
|
9
|
+
exports.wikiNeighborhoodAnswer = wikiNeighborhoodAnswer;
|
|
9
10
|
const wiki_files_1 = require("./wiki-files");
|
|
10
11
|
const workspace_1 = require("./workspace");
|
|
11
12
|
// Router reachability budget. The benchmark fixture A1 assert guarantees
|
|
@@ -163,3 +164,125 @@ function wikiImpactAnswer(pages, term, graph = buildWikiGraph(pages)) {
|
|
|
163
164
|
}
|
|
164
165
|
return finalizeWikiAnswer(lines.join("\n"));
|
|
165
166
|
}
|
|
167
|
+
const neighborhoodReadCap = 5;
|
|
168
|
+
function pageClassPriority(file, scope) {
|
|
169
|
+
if (file.startsWith("wiki/decisions/") || /decision/.test(scope))
|
|
170
|
+
return 90;
|
|
171
|
+
if (file.startsWith("wiki/sources/") || /source/.test(scope))
|
|
172
|
+
return 80;
|
|
173
|
+
if (file.startsWith("wiki/canonical/") || /canonical/.test(scope))
|
|
174
|
+
return 70;
|
|
175
|
+
if (file.startsWith("wiki/plans/") || /plan/.test(scope))
|
|
176
|
+
return 50;
|
|
177
|
+
if (file.startsWith("wiki/roadmaps/") || /roadmap/.test(scope))
|
|
178
|
+
return 40;
|
|
179
|
+
if (file.startsWith("wiki/meta/") || /wiki-meta/.test(scope))
|
|
180
|
+
return 30;
|
|
181
|
+
return 10;
|
|
182
|
+
}
|
|
183
|
+
function neighborhoodReasonRank(reason) {
|
|
184
|
+
if (reason === "exact page match")
|
|
185
|
+
return 1000;
|
|
186
|
+
if (reason === "title/path match")
|
|
187
|
+
return 900;
|
|
188
|
+
if (reason === "decision_ref target")
|
|
189
|
+
return 800;
|
|
190
|
+
if (reason === "decision_ref citation")
|
|
191
|
+
return 760;
|
|
192
|
+
if (reason === "outgoing link target")
|
|
193
|
+
return 700;
|
|
194
|
+
if (reason === "incoming link source")
|
|
195
|
+
return 650;
|
|
196
|
+
return 0;
|
|
197
|
+
}
|
|
198
|
+
function isGeneratedScopedRouter(file) {
|
|
199
|
+
return /^wiki\/indexes\/auto-[a-z0-9-]+(?:-\d+)?\.md$/.test(file);
|
|
200
|
+
}
|
|
201
|
+
function isRouterOnlySurface(file) {
|
|
202
|
+
return file === exports.wikiRouterRoot || file === "wiki/index.md" || isGeneratedScopedRouter(file);
|
|
203
|
+
}
|
|
204
|
+
function addNeighborhoodCandidate(candidates, file, reason, bestFile, graph) {
|
|
205
|
+
if (!graph.files.has(file) || (file !== bestFile && isRouterOnlySurface(file)))
|
|
206
|
+
return;
|
|
207
|
+
const existing = candidates.get(file);
|
|
208
|
+
if (!existing || neighborhoodReasonRank(reason) > neighborhoodReasonRank(existing.reason)) {
|
|
209
|
+
candidates.set(file, { file, reason });
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
function wikiNeighborhoodAnswer(pages, term, graph = buildWikiGraph(pages)) {
|
|
213
|
+
const depths = wikiRouterDepths(graph);
|
|
214
|
+
const textByFile = new Map(pages.map((page) => [page.file, page.text]));
|
|
215
|
+
const lowered = term.toLowerCase();
|
|
216
|
+
const exactTarget = (0, wiki_files_1.normalizeWikiLinkTarget)("wiki/index.md", term.replace(/^\[\[|\]\]$/g, ""));
|
|
217
|
+
const matches = pages
|
|
218
|
+
.map((page) => ({ file: page.file, title: (0, wiki_files_1.wikiTitleForFile)(page.file, page.text), text: page.text }))
|
|
219
|
+
.filter((page) => page.file === exactTarget || page.file.toLowerCase().includes(lowered) || page.title.toLowerCase().includes(lowered))
|
|
220
|
+
.sort((a, b) => {
|
|
221
|
+
const exactDelta = Number(b.file === exactTarget) - Number(a.file === exactTarget);
|
|
222
|
+
if (exactDelta !== 0)
|
|
223
|
+
return exactDelta;
|
|
224
|
+
const titleDelta = Number(b.title.toLowerCase().includes(lowered)) - Number(a.title.toLowerCase().includes(lowered));
|
|
225
|
+
if (titleDelta !== 0)
|
|
226
|
+
return titleDelta;
|
|
227
|
+
const aScope = (0, workspace_1.metadataValue)(a.text, "scope");
|
|
228
|
+
const bScope = (0, workspace_1.metadataValue)(b.text, "scope");
|
|
229
|
+
return pageClassPriority(b.file, bScope) - pageClassPriority(a.file, aScope) || a.file.localeCompare(b.file);
|
|
230
|
+
});
|
|
231
|
+
if (matches.length === 0)
|
|
232
|
+
return `Wiki neighborhood "${term}": no matching wiki pages.`;
|
|
233
|
+
const best = matches[0];
|
|
234
|
+
const candidates = new Map();
|
|
235
|
+
addNeighborhoodCandidate(candidates, best.file, best.file === exactTarget ? "exact page match" : "title/path match", best.file, graph);
|
|
236
|
+
const outgoingRef = graph.outgoingDecisionRef.get(best.file);
|
|
237
|
+
if (outgoingRef)
|
|
238
|
+
addNeighborhoodCandidate(candidates, outgoingRef, "decision_ref target", best.file, graph);
|
|
239
|
+
for (const source of graph.incomingDecisionRefs.get(best.file) ?? [])
|
|
240
|
+
addNeighborhoodCandidate(candidates, source, "decision_ref citation", best.file, graph);
|
|
241
|
+
for (const link of graph.outgoingLinks.get(best.file) ?? [])
|
|
242
|
+
addNeighborhoodCandidate(candidates, link.normalizedTarget, "outgoing link target", best.file, graph);
|
|
243
|
+
for (const link of graph.incomingLinks.get(best.file) ?? [])
|
|
244
|
+
addNeighborhoodCandidate(candidates, link.file, "incoming link source", best.file, graph);
|
|
245
|
+
const sorted = Array.from(candidates.values()).sort((a, b) => {
|
|
246
|
+
const reasonDelta = neighborhoodReasonRank(b.reason) - neighborhoodReasonRank(a.reason);
|
|
247
|
+
if (reasonDelta !== 0)
|
|
248
|
+
return reasonDelta;
|
|
249
|
+
const aText = textByFile.get(a.file) ?? "";
|
|
250
|
+
const bText = textByFile.get(b.file) ?? "";
|
|
251
|
+
const classDelta = pageClassPriority(b.file, (0, workspace_1.metadataValue)(bText, "scope")) - pageClassPriority(a.file, (0, workspace_1.metadataValue)(aText, "scope"));
|
|
252
|
+
if (classDelta !== 0)
|
|
253
|
+
return classDelta;
|
|
254
|
+
const aDepth = depths.get(a.file) ?? Number.POSITIVE_INFINITY;
|
|
255
|
+
const bDepth = depths.get(b.file) ?? Number.POSITIVE_INFINITY;
|
|
256
|
+
return aDepth - bDepth || a.file.localeCompare(b.file);
|
|
257
|
+
}).slice(0, neighborhoodReadCap);
|
|
258
|
+
const lines = [
|
|
259
|
+
`Wiki neighborhood "${term}": best match ${best.file} — ${best.title}; read ${sorted.length} nearby page${sorted.length === 1 ? "" : "s"}.`,
|
|
260
|
+
"",
|
|
261
|
+
"Read order:",
|
|
262
|
+
];
|
|
263
|
+
sorted.forEach((candidate, index) => {
|
|
264
|
+
const text = textByFile.get(candidate.file) ?? "";
|
|
265
|
+
const depth = depths.get(candidate.file);
|
|
266
|
+
const status = (0, workspace_1.metadataValue)(text, "status") || "unknown";
|
|
267
|
+
const scope = (0, workspace_1.metadataValue)(text, "scope") || "unknown-scope";
|
|
268
|
+
lines.push(`${index + 1}. ${candidate.file} — ${candidate.reason}; ${depth === undefined ? "router unreachable" : `router depth ${depth}`}; ${status} ${scope}`);
|
|
269
|
+
});
|
|
270
|
+
const incoming = uniqueSorted((graph.incomingLinks.get(best.file) ?? [])
|
|
271
|
+
.map((link) => link.file)
|
|
272
|
+
.filter((source) => source !== best.file && graph.files.has(source)));
|
|
273
|
+
const outgoing = uniqueSorted((graph.outgoingLinks.get(best.file) ?? [])
|
|
274
|
+
.map((link) => link.normalizedTarget)
|
|
275
|
+
.filter((target) => target !== best.file && graph.files.has(target)));
|
|
276
|
+
const incomingRefs = uniqueSorted((graph.incomingDecisionRefs.get(best.file) ?? [])
|
|
277
|
+
.filter((source) => source !== best.file && graph.files.has(source)));
|
|
278
|
+
const reviewTrigger = (0, workspace_1.metadataValue)(best.text, "review_trigger");
|
|
279
|
+
lines.push("");
|
|
280
|
+
lines.push("Why:");
|
|
281
|
+
lines.push(`- incoming links: ${sampled(incoming, impactListCap)}`);
|
|
282
|
+
lines.push(`- outgoing links: ${sampled(outgoing, impactListCap)}`);
|
|
283
|
+
lines.push(`- decision_ref: ${outgoingRef && graph.files.has(outgoingRef) ? outgoingRef : "none"}`);
|
|
284
|
+
lines.push(`- decision_ref citations: ${sampled(incomingRefs, impactListCap)}`);
|
|
285
|
+
if (reviewTrigger)
|
|
286
|
+
lines.push(`- review_trigger: ${reviewTrigger}`);
|
|
287
|
+
return finalizeWikiAnswer(lines.join("\n"));
|
|
288
|
+
}
|
package/docs/benchmarks.md
CHANGED
|
@@ -17,17 +17,33 @@ Generated benchmark reports under `benchmarks/reports/llm/` are ignored by defau
|
|
|
17
17
|
|
|
18
18
|
## Wiki Track
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
|
25
|
-
|
|
|
26
|
-
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
20
|
+
Latest clean synthetic wiki-track release evidence: 2026-06-29, `gpt-5.5`, branch `perf/small-repo-code-evidence-safeguards` at `ae79390`, 42 scenarios, 21 with/without pairs, 3 measured runs plus 1 warmup each. The overall claim gate **passed**, and the claim ledger classified the report as release-claimable. A repair run reused 125 claimable measured runs from retained raw JSONL and remeasured the remaining failed slot.
|
|
21
|
+
|
|
22
|
+
Cost-weighted tokens, Project Librarian vs control, all scales combined:
|
|
23
|
+
|
|
24
|
+
| Task family | Delta |
|
|
25
|
+
| --- | ---: |
|
|
26
|
+
| onboarding | 65.95% less |
|
|
27
|
+
| decision_lookup | 48.46% less |
|
|
28
|
+
| code_impact | 55.61% less |
|
|
29
|
+
| release_policy | 58.22% less |
|
|
30
|
+
| change_location | 29.59% less |
|
|
31
|
+
| multi_session | 52.58% less |
|
|
32
|
+
| aggregation | 42.53% less |
|
|
33
|
+
|
|
34
|
+
Scale-specific cost-weighted token deltas:
|
|
35
|
+
|
|
36
|
+
| Task family | Small | Medium | Large |
|
|
37
|
+
| --- | ---: | ---: | ---: |
|
|
38
|
+
| onboarding | 60.88% less | 65.53% less | 69.17% less |
|
|
39
|
+
| decision_lookup | 19.18% less | 51.29% less | 60.12% less |
|
|
40
|
+
| code_impact | 26.61% less | 54.81% less | 68.43% less |
|
|
41
|
+
| release_policy | 30.23% less | 62.16% less | 67.26% less |
|
|
42
|
+
| change_location | 7.79% less | 2.86% more | 57.27% less |
|
|
43
|
+
| multi_session | 54.29% more | 35.32% less | 82.77% less |
|
|
44
|
+
| aggregation | 7.66% less | 51.32% less | 49.17% less |
|
|
45
|
+
|
|
46
|
+
The release claim is 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 `multi_session` and medium `change_location` cost more with the wiki in the cost-weighted token metric. `code_impact`, `change_location`, and `aggregation` also still have wall-time or command-count regressions in this report, even though token and output-byte metrics improved at the task-family aggregate level.
|
|
31
47
|
|
|
32
48
|
## Code-Graph Track
|
|
33
49
|
|
|
@@ -52,7 +68,11 @@ Repositories under test:
|
|
|
52
68
|
|
|
53
69
|
Question types:
|
|
54
70
|
|
|
71
|
+
- **onboarding** - summarize what the project is, current risks, and where to read next.
|
|
55
72
|
- **decision_lookup** - find the latest project decision and its date from the wiki.
|
|
73
|
+
- **code_impact** - identify likely impacted files or areas for a benchmark report schema change.
|
|
74
|
+
- **release_policy** - find the checks required before publishing or making benchmark claims.
|
|
75
|
+
- **change_location** - find where an agent should edit to implement the Codex LLM benchmark runner.
|
|
56
76
|
- **aggregation** - answer a question whose facts are scattered across several pages and must be synthesized.
|
|
57
77
|
- **multi_session** - a second session on the same project, measuring whether the durable wiki helps the next session, not just the first.
|
|
58
78
|
- **impact_trace** - trace the full set of direct and indirect importers for a changing module.
|
package/docs/cli-reference.md
CHANGED
|
@@ -19,7 +19,7 @@ node .codex/skills/project-librarian/dist/init-project-wiki.js install [--scope
|
|
|
19
19
|
| `update --agents <list>` | Refresh an existing setup and existing project-scoped skill copies; selected surfaces can be `codex`, `claude`, `cursor`, `gemini`, or `all`. |
|
|
20
20
|
| `--migrate`, `--adopt-existing` | Preserve an existing wiki as `wiki_legacy*`, create migration inboxes, and generate unit-map/split-plan/coverage review files. |
|
|
21
21
|
| `--lint` | Validate generated setup without editing files. |
|
|
22
|
-
| `--link-check` | Report broken wiki links, duplicate routes, orphan pages,
|
|
22
|
+
| `--link-check` | Report broken wiki links, duplicate routes, orphan pages, router reachability, and warning-only topology signals. |
|
|
23
23
|
| `--quality-check` | Report stale, conflicting, and low-quality wiki document signals. |
|
|
24
24
|
| `--doctor` | Run lint, link-check, and quality-check together. |
|
|
25
25
|
| `--doctor --fix` | Safely refresh generated index routing before diagnostics. `--fix` is only a modifier for `--doctor`. |
|
|
@@ -28,8 +28,7 @@ node .codex/skills/project-librarian/dist/init-project-wiki.js install [--scope
|
|
|
28
28
|
| `--migration-doctor` | Run migration-lint and migration-quality-check together. |
|
|
29
29
|
| `--query <terms>` | Search wiki paths, metadata, titles, and bodies; answer-first output with per-page TL;DR lines under a hard size cap. |
|
|
30
30
|
| `--wiki-impact <page-or-term>` | Show wiki backlinks, `decision_ref` citations, outgoing links, and router depth for matching pages. |
|
|
31
|
-
| `--wiki-
|
|
32
|
-
| `--wiki-visualize-out <path>` | With `--wiki-visualize`, write to a custom repository-relative path under `.project-wiki/`. |
|
|
31
|
+
| `--wiki-neighborhood <page-or-term>` | Show a bounded read order for nearby wiki pages using links, `decision_ref`, metadata, page class, and router depth. |
|
|
33
32
|
| `--refresh-index` | Update generated auto-discovered wiki routing. |
|
|
34
33
|
| `--capture-inbox --title <title> --content <content> --category <category>` | Append a candidate note to the wiki inbox; category defaults to `project-candidate`. |
|
|
35
34
|
| `--handoff-save --goal <goal> --state <state> --next <action>` | Save generated local session handoff state under `.project-wiki/session/`. Repeat `--next`, `--decision`, `--blocked`, `--open-question`, `--verification`, `--last-success-command`, and `--last-failure-command` as needed. |
|
|
@@ -59,3 +58,14 @@ node .codex/skills/project-librarian/dist/init-project-wiki.js install [--scope
|
|
|
59
58
|
| `--code-context-pack <term>` | Print a budgeted first-pass context pack with structural file, symbol, route, import, edge, and ownership evidence. |
|
|
60
59
|
| `--code-search-symbol <term>` | Search indexed symbols. |
|
|
61
60
|
| `--code-query <sql>` | Run conservative read-only SQL over the evidence index. |
|
|
61
|
+
|
|
62
|
+
### Topology Warnings
|
|
63
|
+
|
|
64
|
+
`--link-check` keeps topology findings warning-only so they can guide cleanup without blocking bootstrap, update, or release flows.
|
|
65
|
+
|
|
66
|
+
| Code | Meaning |
|
|
67
|
+
| --- | --- |
|
|
68
|
+
| `hub-overload` | A hand-maintained router or meta page links to too many wiki pages and should be split or scoped. |
|
|
69
|
+
| `weak-authority-route` | An active canonical page with decision or evidence authority signals is reachable only through generated auto-index routing. |
|
|
70
|
+
| `missing-evidence-link` | An active canonical page makes a source-backed claim without a source link or `decision_ref`. |
|
|
71
|
+
| `stale-fanout` | A heavily linked active page has a broad review trigger that is too weak for topology-sensitive edits. |
|