project-librarian 0.2.1 → 0.4.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 +261 -93
- package/README.md +190 -76
- package/SKILL.md +16 -10
- package/dist/args.js +25 -2
- package/dist/code-index-file-policy.js +105 -2
- package/dist/code-index.js +338 -70
- package/dist/hooks.js +78 -0
- package/dist/init-project-wiki.js +265 -167
- package/dist/install-skill.js +2 -9
- package/dist/mcp-server.js +709 -0
- package/dist/migration.js +875 -60
- package/dist/modes.js +262 -62
- package/dist/retrieval-eval.js +68 -0
- package/dist/taxonomy.js +193 -0
- package/dist/templates.js +214 -36
- package/dist/wiki-concepts.js +49 -0
- package/dist/wiki-files.js +120 -27
- package/dist/wiki-graph.js +166 -0
- package/dist/wiki-visualizer.js +558 -0
- package/package.json +5 -4
- package/README.ja.md +0 -215
- package/README.zh.md +0 -215
package/dist/hooks.js
CHANGED
|
@@ -39,8 +39,15 @@ exports.upsertHookConfig = upsertHookConfig;
|
|
|
39
39
|
exports.upsertClaudeHookConfig = upsertClaudeHookConfig;
|
|
40
40
|
exports.upsertGeminiHookConfig = upsertGeminiHookConfig;
|
|
41
41
|
exports.upsertCursorHookConfig = upsertCursorHookConfig;
|
|
42
|
+
exports.codeEvidenceIndexExists = codeEvidenceIndexExists;
|
|
43
|
+
exports.mcpRegistrationGate = mcpRegistrationGate;
|
|
44
|
+
exports.upsertClaudeMcpConfig = upsertClaudeMcpConfig;
|
|
45
|
+
exports.upsertCursorMcpConfig = upsertCursorMcpConfig;
|
|
46
|
+
exports.upsertGeminiMcpConfig = upsertGeminiMcpConfig;
|
|
42
47
|
const childProcess = __importStar(require("node:child_process"));
|
|
48
|
+
const fs = __importStar(require("node:fs"));
|
|
43
49
|
const args_1 = require("./args");
|
|
50
|
+
const code_index_file_policy_1 = require("./code-index-file-policy");
|
|
44
51
|
const workspace_1 = require("./workspace");
|
|
45
52
|
function upsertGitHooksPath() {
|
|
46
53
|
if (args_1.noGitConfigMode)
|
|
@@ -136,6 +143,75 @@ function upsertCursorHookConfig() {
|
|
|
136
143
|
(0, workspace_1.write)(relativePath, next);
|
|
137
144
|
return previous === next ? "exists" : previous ? "updated" : "created";
|
|
138
145
|
}
|
|
146
|
+
const mcpServerName = "project-librarian";
|
|
147
|
+
// Candidate local-runner paths, project root relative, in the recorded
|
|
148
|
+
// local-runner-first order (mirrors validationTrailers()). The first existing
|
|
149
|
+
// runner wins; absent any local install we register the published binary.
|
|
150
|
+
const localRunnerCandidates = [
|
|
151
|
+
"tools/project-librarian/dist/init-project-wiki.js",
|
|
152
|
+
".codex/skills/project-librarian/dist/init-project-wiki.js",
|
|
153
|
+
".claude/skills/project-librarian/dist/init-project-wiki.js",
|
|
154
|
+
".cursor/skills/project-librarian/dist/init-project-wiki.js",
|
|
155
|
+
".gemini/skills/project-librarian/dist/init-project-wiki.js",
|
|
156
|
+
];
|
|
157
|
+
// Deterministic command policy for the registered MCP server: if the repo
|
|
158
|
+
// contains a local runner, register `node <runner> mcp`; otherwise register the
|
|
159
|
+
// installed binary `project-librarian mcp`. This mirrors the local-runner-first
|
|
160
|
+
// skill policy (run the installed local copy with node, not npx) so registration
|
|
161
|
+
// does not depend on network/registry access. The runner path is stored project
|
|
162
|
+
// relative so the registration stays portable across clones.
|
|
163
|
+
function mcpServerEntry() {
|
|
164
|
+
const runner = localRunnerCandidates.find((candidate) => fs.existsSync((0, workspace_1.abs)(candidate)));
|
|
165
|
+
if (runner)
|
|
166
|
+
return { command: "node", args: [runner, "mcp"] };
|
|
167
|
+
return { command: mcpServerName, args: ["mcp"] };
|
|
168
|
+
}
|
|
169
|
+
// Preservation-first, idempotent merge of the project-librarian MCP server into a
|
|
170
|
+
// JSON config file's `mcpServers` map. Unknown keys and other servers are never
|
|
171
|
+
// clobbered; only `mcpServers["project-librarian"]` is set. A second run with an
|
|
172
|
+
// unchanged entry returns "exists". Used for Claude `.mcp.json`, Cursor
|
|
173
|
+
// `.cursor/mcp.json`, and (via the same map) Gemini `.gemini/settings.json`.
|
|
174
|
+
//
|
|
175
|
+
// Codex boundary: `codex mcp` only manages USER-level config (~/.codex/config.toml
|
|
176
|
+
// via `codex mcp add`); there is no documented project-level MCP config file under
|
|
177
|
+
// `.codex/`. Per the no-user-level-writes rule we do not register Codex here; the
|
|
178
|
+
// README documents running `codex mcp add project-librarian -- node <runner> mcp`.
|
|
179
|
+
function upsertMcpServersFile(relativePath) {
|
|
180
|
+
const config = (0, workspace_1.parseJson)(relativePath, {});
|
|
181
|
+
if (!config.mcpServers || typeof config.mcpServers !== "object" || Array.isArray(config.mcpServers)) {
|
|
182
|
+
config.mcpServers = {};
|
|
183
|
+
}
|
|
184
|
+
config.mcpServers[mcpServerName] = mcpServerEntry();
|
|
185
|
+
const next = `${JSON.stringify(config, null, 2)}\n`;
|
|
186
|
+
const previous = (0, workspace_1.exists)(relativePath) ? (0, workspace_1.read)(relativePath) : "";
|
|
187
|
+
if (previous === next)
|
|
188
|
+
return "exists";
|
|
189
|
+
(0, workspace_1.write)(relativePath, next);
|
|
190
|
+
return previous ? "updated" : "created";
|
|
191
|
+
}
|
|
192
|
+
function codeEvidenceIndexExists() {
|
|
193
|
+
return (0, workspace_1.walkFilesUnder)(code_index_file_policy_1.codeEvidenceDirectory, (file) => file.endsWith(".sqlite")).length > 0;
|
|
194
|
+
}
|
|
195
|
+
function mcpRegistrationGate() {
|
|
196
|
+
if (codeEvidenceIndexExists())
|
|
197
|
+
return { register: true };
|
|
198
|
+
const indexableFileCount = (0, code_index_file_policy_1.discoverCodeFiles)(["."]).length;
|
|
199
|
+
if (indexableFileCount >= code_index_file_policy_1.SMALL_REPO_FILE_THRESHOLD)
|
|
200
|
+
return { register: true };
|
|
201
|
+
return {
|
|
202
|
+
register: false,
|
|
203
|
+
reason: `skipped-small-repo ${indexableFileCount} indexable files < ${code_index_file_policy_1.SMALL_REPO_FILE_THRESHOLD} (code-evidence tools measured costlier than direct reads at this scale: stageR1; opt in via --code-index --acknowledge-small-repo, then re-run bootstrap)`,
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
function upsertClaudeMcpConfig() {
|
|
207
|
+
return upsertMcpServersFile(".mcp.json");
|
|
208
|
+
}
|
|
209
|
+
function upsertCursorMcpConfig() {
|
|
210
|
+
return upsertMcpServersFile(".cursor/mcp.json");
|
|
211
|
+
}
|
|
212
|
+
function upsertGeminiMcpConfig() {
|
|
213
|
+
return upsertMcpServersFile(".gemini/settings.json");
|
|
214
|
+
}
|
|
139
215
|
function buildStartupHookScript(output) {
|
|
140
216
|
return `#!/usr/bin/env node
|
|
141
217
|
|
|
@@ -183,6 +259,8 @@ const sections = files
|
|
|
183
259
|
|
|
184
260
|
const additionalContext = [
|
|
185
261
|
"[Project wiki startup review]",
|
|
262
|
+
"Injected context: wiki/startup.md and wiki/index.md are ALREADY included below this line.",
|
|
263
|
+
"Do not re-read these two files this session; route any further reads through the index.",
|
|
186
264
|
"Use ./wiki as the project-planning source of truth only. Start with compact routing context; read detailed project canonical, decision, or meta files on demand.",
|
|
187
265
|
"Project canonical content language is selected from user/project context; do not assume a fixed default language.",
|
|
188
266
|
"When project planning content is added, changed, or removed, update ./wiki in the same turn.",
|
|
@@ -7,18 +7,19 @@ const install_skill_1 = require("./install-skill");
|
|
|
7
7
|
const modes_1 = require("./modes");
|
|
8
8
|
const migration_1 = require("./migration");
|
|
9
9
|
const templates_1 = require("./templates");
|
|
10
|
+
const wiki_visualizer_1 = require("./wiki-visualizer");
|
|
10
11
|
const workspace_1 = require("./workspace");
|
|
11
|
-
const wiki_files_1 = require("./wiki-files");
|
|
12
12
|
function codeIndex() {
|
|
13
13
|
return require("./code-index");
|
|
14
14
|
}
|
|
15
15
|
function printUsage() {
|
|
16
16
|
console.log(`Usage:
|
|
17
|
-
project-librarian [init] [options]
|
|
18
|
-
project-librarian install-skill [--scope user|project] [--agents codex|claude|cursor|gemini|all
|
|
17
|
+
project-librarian [init|update] [options]
|
|
18
|
+
project-librarian install-skill [--scope user|project] [--agents codex|claude|cursor|gemini|all]
|
|
19
|
+
project-librarian mcp
|
|
19
20
|
|
|
20
21
|
Options:
|
|
21
|
-
--migrate, --adopt-existing Preserve an existing wiki as wiki_legacy and create migration inboxes.
|
|
22
|
+
--migrate, --adopt-existing Preserve an existing wiki as wiki_legacy and create unit-level migration map, split plan, coverage ledger, review files, and inboxes.
|
|
22
23
|
--lint Validate the generated project wiki setup without editing files.
|
|
23
24
|
--link-check Report broken wiki links, duplicate routes, and orphan pages.
|
|
24
25
|
--quality-check Report stale, conflicting, and low-quality wiki document signals.
|
|
@@ -31,14 +32,18 @@ Options:
|
|
|
31
32
|
--issue-draft Print a problem/side-effect GitHub issue body draft.
|
|
32
33
|
--issue-body-file <path> With --issue-create, use an existing Markdown body file.
|
|
33
34
|
--issue-title <title> Override the generated issue draft title.
|
|
34
|
-
--query <terms> Search wiki paths, metadata, titles, and bodies.
|
|
35
|
+
--query <terms> Search wiki paths, metadata, titles, and bodies (answer-shaped, capped output).
|
|
36
|
+
--wiki-impact <page-or-term> Show wiki backlinks, decision_ref citations, and router depth for matching pages.
|
|
37
|
+
--wiki-visualize Write a static wiki graph visualizer to .project-wiki/wiki-graph.html.
|
|
38
|
+
--wiki-visualize-out <path> With --wiki-visualize, write under a custom .project-wiki/ path.
|
|
35
39
|
--refresh-index Update the managed auto-discovered wiki index block.
|
|
36
40
|
--capture-inbox Append a candidate note with --title, --content, and optional --category.
|
|
37
41
|
--glossary-init Create and route the optional glossary page.
|
|
38
42
|
--prune-check Report active pages with stale or unresolved signals.
|
|
39
|
-
--review-migration Sync
|
|
43
|
+
--review-migration Sync unit coverage and compatible inbox statuses into migration review files.
|
|
40
44
|
--no-git-config Install hook files without changing git core.hooksPath.
|
|
41
45
|
--code-index Build the disposable .project-wiki code evidence index.
|
|
46
|
+
--acknowledge-small-repo With --code-index, proceed below the small-repo scale gate after its cost warning.
|
|
42
47
|
--incremental With --code-index, require an existing compatible index and update only changes.
|
|
43
48
|
--code-index-full With --code-index, force a full rebuild even when incremental update is possible.
|
|
44
49
|
--code-parser <mode> With --code-index, use parser mode default or tree-sitter.
|
|
@@ -47,9 +52,22 @@ Options:
|
|
|
47
52
|
--code-report Print architecture and ownership summaries from the code evidence index.
|
|
48
53
|
--code-report-section <section> With --code-report, print one section: coverage, ownership, languages, parsers, workspaces, workspace-graph, routes, hotspots, configs, or edges.
|
|
49
54
|
--code-impact <term> Show file, symbol, route, import, and edge impact evidence for a term.
|
|
55
|
+
--code-context-pack <term> Print a budgeted first-pass code context pack for a path, symbol, route, or module term.
|
|
50
56
|
--code-search-symbol <term> Search indexed symbols.
|
|
57
|
+
|
|
58
|
+
Commands:
|
|
59
|
+
update Run the idempotent wiki/setup update path; rejects migration flags.
|
|
60
|
+
mcp Run the stdio MCP server exposing answer-shaped code-evidence tools (code_context_pack, code_impact, code_ownership, code_workspace_graph, code_search, code_status) over the existing .project-wiki index.
|
|
61
|
+
|
|
51
62
|
--help Show this help.`);
|
|
52
63
|
}
|
|
64
|
+
// console.log queues asynchronously on pipes; an immediate process.exit() discards
|
|
65
|
+
// anything past the first ~64KB pipe chunk (observed truncating a large
|
|
66
|
+
// --code-report on an 11k-file repo). Exiting from a zero-length write callback
|
|
67
|
+
// guarantees everything queued before it has drained.
|
|
68
|
+
function exitAfterStdoutDrain(code) {
|
|
69
|
+
process.stdout.write("", () => process.exit(code));
|
|
70
|
+
}
|
|
53
71
|
if (args_1.helpMode) {
|
|
54
72
|
printUsage();
|
|
55
73
|
process.exit(0);
|
|
@@ -74,6 +92,10 @@ if (args_1.missingValueOptions.length > 0) {
|
|
|
74
92
|
printUsage();
|
|
75
93
|
process.exit(1);
|
|
76
94
|
}
|
|
95
|
+
if (args_1.command === "update" && args_1.migrateMode) {
|
|
96
|
+
console.error("update cannot be combined with --migrate or --adopt-existing; use project-librarian --migrate for migration.");
|
|
97
|
+
process.exit(1);
|
|
98
|
+
}
|
|
77
99
|
if (args_1.fixMode && !args_1.doctorMode) {
|
|
78
100
|
console.error("--fix is only supported with --doctor.");
|
|
79
101
|
process.exit(1);
|
|
@@ -86,10 +108,18 @@ if (args_1.codeReportSection && !args_1.codeReportMode) {
|
|
|
86
108
|
console.error("--code-report-section is only supported with --code-report.");
|
|
87
109
|
process.exit(1);
|
|
88
110
|
}
|
|
111
|
+
if (args_1.wikiVisualizeOutput && !args_1.wikiVisualizeMode) {
|
|
112
|
+
console.error("--wiki-visualize-out is only supported with --wiki-visualize.");
|
|
113
|
+
process.exit(1);
|
|
114
|
+
}
|
|
89
115
|
if (args_1.codeIndexIncrementalMode && !args_1.codeIndexMode) {
|
|
90
116
|
console.error("--incremental is only supported with --code-index.");
|
|
91
117
|
process.exit(1);
|
|
92
118
|
}
|
|
119
|
+
if (args_1.acknowledgeSmallRepoMode && !args_1.codeIndexMode) {
|
|
120
|
+
console.error("--acknowledge-small-repo is only supported with --code-index.");
|
|
121
|
+
process.exit(1);
|
|
122
|
+
}
|
|
93
123
|
if (args_1.codeIndexFullMode && !args_1.codeIndexMode) {
|
|
94
124
|
console.error("--code-index-full is only supported with --code-index.");
|
|
95
125
|
process.exit(1);
|
|
@@ -106,165 +136,233 @@ if (args_1.command === "install-skill") {
|
|
|
106
136
|
(0, install_skill_1.runInstallSkillMode)();
|
|
107
137
|
process.exit(0);
|
|
108
138
|
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
process.
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
}
|
|
130
|
-
if (args_1.
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
}
|
|
150
|
-
if (args_1.
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
(
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
}
|
|
186
|
-
if (args_1.
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
(
|
|
196
|
-
(0,
|
|
197
|
-
(0
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
(0,
|
|
201
|
-
(0
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
(0,
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
(0
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
results
|
|
233
|
-
|
|
234
|
-
results.push(["
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
results.push(["
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
139
|
+
if (args_1.command === "mcp") {
|
|
140
|
+
// Hand-rolled stdio MCP server over the existing code-evidence index. Lazy
|
|
141
|
+
// require keeps the server (and its node:sqlite dependency) out of the normal
|
|
142
|
+
// bootstrap path. The server roots at process.cwd() and runs until stdin ends,
|
|
143
|
+
// exiting from inside runMcpServerMode; the init flow below must not run.
|
|
144
|
+
require("./mcp-server").runMcpServerMode();
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
runInitCommand();
|
|
148
|
+
}
|
|
149
|
+
function runInitCommand() {
|
|
150
|
+
const activeCodeModes = [args_1.codeQueryMode, args_1.codeReportMode, args_1.codeStatusMode, args_1.codeFilesMode, args_1.codeImpactMode, args_1.codeContextPackMode, args_1.codeSearchSymbolMode, args_1.codeIndexMode].filter(Boolean).length;
|
|
151
|
+
if (activeCodeModes > 1) {
|
|
152
|
+
console.error("Use one code evidence mode at a time: --code-index, --code-query, --code-report, --code-status, --code-files, --code-impact, --code-context-pack, or --code-search-symbol.");
|
|
153
|
+
process.exit(1);
|
|
154
|
+
}
|
|
155
|
+
if (args_1.codeQueryMode) {
|
|
156
|
+
codeIndex().runCodeQueryMode();
|
|
157
|
+
exitAfterStdoutDrain(0);
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
if (args_1.codeReportMode) {
|
|
161
|
+
codeIndex().runCodeReportMode();
|
|
162
|
+
exitAfterStdoutDrain(0);
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
if (args_1.codeStatusMode) {
|
|
166
|
+
codeIndex().runCodeStatusMode();
|
|
167
|
+
exitAfterStdoutDrain(0);
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
if (args_1.codeFilesMode) {
|
|
171
|
+
codeIndex().runCodeFilesMode();
|
|
172
|
+
exitAfterStdoutDrain(0);
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
if (args_1.codeImpactMode) {
|
|
176
|
+
codeIndex().runCodeImpactMode();
|
|
177
|
+
exitAfterStdoutDrain(0);
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
if (args_1.codeContextPackMode) {
|
|
181
|
+
codeIndex().runCodeContextPackMode();
|
|
182
|
+
exitAfterStdoutDrain(0);
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
if (args_1.codeSearchSymbolMode) {
|
|
186
|
+
codeIndex().runCodeSearchSymbolMode();
|
|
187
|
+
exitAfterStdoutDrain(0);
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
if (args_1.codeIndexMode) {
|
|
191
|
+
codeIndex().runCodeIndexMode();
|
|
192
|
+
process.exit(0);
|
|
193
|
+
}
|
|
194
|
+
if (args_1.wikiImpactMode) {
|
|
195
|
+
(0, modes_1.runWikiImpactMode)();
|
|
196
|
+
exitAfterStdoutDrain(0);
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
if (args_1.wikiVisualizeMode) {
|
|
200
|
+
try {
|
|
201
|
+
const output = (0, wiki_visualizer_1.writeWikiVisualizer)(args_1.wikiVisualizeOutput);
|
|
202
|
+
console.log(`Project wiki visualizer written: ${output}`);
|
|
203
|
+
exitAfterStdoutDrain(0);
|
|
204
|
+
}
|
|
205
|
+
catch (error) {
|
|
206
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
207
|
+
process.exit(1);
|
|
208
|
+
}
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
if (args_1.queryTerm) {
|
|
212
|
+
(0, modes_1.runQueryMode)();
|
|
213
|
+
exitAfterStdoutDrain(0);
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
if (args_1.issueCreateMode) {
|
|
217
|
+
(0, modes_1.runIssueCreateMode)();
|
|
218
|
+
process.exit(0);
|
|
219
|
+
}
|
|
220
|
+
if (args_1.issueDraftMode) {
|
|
221
|
+
(0, modes_1.runIssueDraftMode)();
|
|
222
|
+
exitAfterStdoutDrain(0);
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
if (args_1.pruneCheckMode) {
|
|
226
|
+
(0, modes_1.runPruneCheckMode)();
|
|
227
|
+
process.exit(0);
|
|
228
|
+
}
|
|
229
|
+
if (args_1.reviewMigrationMode) {
|
|
230
|
+
(0, migration_1.runReviewMigrationMode)();
|
|
231
|
+
process.exit(0);
|
|
232
|
+
}
|
|
233
|
+
if (args_1.migrationDoctorMode) {
|
|
234
|
+
(0, modes_1.runMigrationDoctorMode)();
|
|
235
|
+
process.exit(0);
|
|
236
|
+
}
|
|
237
|
+
if (args_1.migrationQualityCheckMode) {
|
|
238
|
+
(0, modes_1.runMigrationQualityCheckMode)();
|
|
239
|
+
process.exit(0);
|
|
240
|
+
}
|
|
241
|
+
if (args_1.migrationLintMode) {
|
|
242
|
+
(0, modes_1.runMigrationLintMode)();
|
|
243
|
+
process.exit(0);
|
|
244
|
+
}
|
|
245
|
+
if (args_1.doctorMode) {
|
|
246
|
+
(0, modes_1.runDoctorMode)(args_1.fixMode);
|
|
247
|
+
process.exit(0);
|
|
248
|
+
}
|
|
249
|
+
if (args_1.linkCheckMode) {
|
|
250
|
+
(0, modes_1.runLinkCheckMode)();
|
|
251
|
+
process.exit(0);
|
|
252
|
+
}
|
|
253
|
+
if (args_1.qualityCheckMode) {
|
|
254
|
+
(0, modes_1.runQualityCheckMode)();
|
|
255
|
+
process.exit(0);
|
|
256
|
+
}
|
|
257
|
+
if (args_1.lintMode) {
|
|
258
|
+
(0, modes_1.runLintMode)();
|
|
259
|
+
process.exit(0);
|
|
260
|
+
}
|
|
261
|
+
const migrationState = args_1.migrateMode ? (0, migration_1.prepareMigrationMode)() : null;
|
|
262
|
+
const results = [];
|
|
263
|
+
if (migrationState)
|
|
264
|
+
results.push(["migration prepare", migrationState.note]);
|
|
265
|
+
(0, workspace_1.mkdirp)("wiki/canonical");
|
|
266
|
+
(0, workspace_1.mkdirp)("wiki/decisions");
|
|
267
|
+
(0, workspace_1.mkdirp)("wiki/inbox");
|
|
268
|
+
(0, workspace_1.mkdirp)("wiki/meta");
|
|
269
|
+
(0, workspace_1.mkdirp)("wiki/sources");
|
|
270
|
+
(0, workspace_1.mkdirp)(".codex/hooks");
|
|
271
|
+
(0, workspace_1.mkdirp)(".claude/hooks");
|
|
272
|
+
(0, workspace_1.mkdirp)(".cursor/hooks");
|
|
273
|
+
(0, workspace_1.mkdirp)(".cursor/rules");
|
|
274
|
+
(0, workspace_1.mkdirp)(".gemini/hooks");
|
|
275
|
+
(0, workspace_1.mkdirp)(".githooks");
|
|
276
|
+
// B1 fallback: sync the CURRENT startup.md TL;DR into the managed AGENTS.md block
|
|
277
|
+
// so non-interactive `codex exec` (which does not run SessionStart hooks) still
|
|
278
|
+
// gets compact startup context. Routers are starter files written later in this
|
|
279
|
+
// flow, so on a fresh bootstrap startup.md does not exist yet; fall back to the
|
|
280
|
+
// template TL;DR that bootstrap is about to write. A missing "## TL;DR" section in
|
|
281
|
+
// an existing startup.md fails loudly inside extractStartupTldr (no fallback).
|
|
282
|
+
const startupForSync = (0, workspace_1.exists)("wiki/startup.md") ? (0, workspace_1.read)("wiki/startup.md") : templates_1.startup;
|
|
283
|
+
const startupTldrForAgents = (0, templates_1.extractStartupTldr)(startupForSync);
|
|
284
|
+
results.push(["AGENTS.md", (0, workspace_1.upsertMarkedSection)("AGENTS.md", "<!-- PROJECT-WIKI-FIRST:START -->", "<!-- PROJECT-WIKI-FIRST:END -->", (0, templates_1.agentsSection)(startupTldrForAgents))]);
|
|
285
|
+
results.push(["CLAUDE.md", (0, workspace_1.upsertMarkedSection)("CLAUDE.md", "<!-- PROJECT-WIKI-CLAUDE:START -->", "<!-- PROJECT-WIKI-CLAUDE:END -->", templates_1.claudeSection)]);
|
|
286
|
+
results.push(["GEMINI.md", (0, workspace_1.upsertMarkedSection)("GEMINI.md", "<!-- PROJECT-WIKI-GEMINI:START -->", "<!-- PROJECT-WIKI-GEMINI:END -->", templates_1.geminiSection)]);
|
|
287
|
+
results.push([".cursor/rules/project-librarian.mdc", (0, workspace_1.writeManaged)(".cursor/rules/project-librarian.mdc", templates_1.cursorRule)]);
|
|
288
|
+
results.push(["wiki/AGENTS.md", (0, workspace_1.upsertMarkedSection)("wiki/AGENTS.md", "<!-- PROJECT-WIKI-INTERNAL:START -->", "<!-- PROJECT-WIKI-INTERNAL:END -->", templates_1.wikiAgentsSection)]);
|
|
289
|
+
results.push([".githooks/prepare-commit-msg", (0, workspace_1.writeManaged)(".githooks/prepare-commit-msg", hooks_1.gitPrepareCommitMsgHook)]);
|
|
290
|
+
(0, workspace_1.makeExecutable)(".githooks/prepare-commit-msg");
|
|
291
|
+
results.push([".githooks/wiki-commit-trailers.js", (0, workspace_1.writeManaged)(".githooks/wiki-commit-trailers.js", hooks_1.gitWikiCommitTrailersScript)]);
|
|
292
|
+
(0, workspace_1.makeExecutable)(".githooks/wiki-commit-trailers.js");
|
|
293
|
+
results.push(["git core.hooksPath", (0, hooks_1.upsertGitHooksPath)()]);
|
|
294
|
+
results.push([".codex/hooks.json", (0, hooks_1.upsertHookConfig)()]);
|
|
295
|
+
results.push([".codex/hooks/wiki-session-start.js", (0, workspace_1.writeManaged)(".codex/hooks/wiki-session-start.js", hooks_1.hookScript)]);
|
|
296
|
+
results.push([".claude/settings.json", (0, hooks_1.upsertClaudeHookConfig)()]);
|
|
297
|
+
results.push([".claude/hooks/wiki-session-start.js", (0, workspace_1.writeManaged)(".claude/hooks/wiki-session-start.js", hooks_1.hookScript)]);
|
|
298
|
+
results.push([".cursor/hooks.json", (0, hooks_1.upsertCursorHookConfig)()]);
|
|
299
|
+
results.push([".cursor/hooks/wiki-session-start.js", (0, workspace_1.writeManaged)(".cursor/hooks/wiki-session-start.js", hooks_1.cursorHookScript)]);
|
|
300
|
+
results.push([".gemini/settings.json", (0, hooks_1.upsertGeminiHookConfig)()]);
|
|
301
|
+
results.push([".gemini/hooks/wiki-session-start.js", (0, workspace_1.writeManaged)(".gemini/hooks/wiki-session-start.js", hooks_1.hookScript)]);
|
|
302
|
+
// Bootstrap-managed MCP registration (preservation-first, idempotent). Claude
|
|
303
|
+
// Code reads `.mcp.json`, Cursor reads `.cursor/mcp.json`, and Gemini reads
|
|
304
|
+
// `mcpServers` inside `.gemini/settings.json`. Codex only supports user-level MCP
|
|
305
|
+
// config (`codex mcp add` -> ~/.codex/config.toml), so it is intentionally not
|
|
306
|
+
// registered at project level; the README documents the manual user-level step.
|
|
307
|
+
// Registration is scale-gated (2026-06-12 decision): below the measured
|
|
308
|
+
// file-count threshold with no existing .project-wiki index, the rows report the
|
|
309
|
+
// skip reason instead of writing config; an existing index registers regardless.
|
|
310
|
+
const mcpGate = (0, hooks_1.mcpRegistrationGate)();
|
|
311
|
+
if (mcpGate.register) {
|
|
312
|
+
results.push([".mcp.json", (0, hooks_1.upsertClaudeMcpConfig)()]);
|
|
313
|
+
results.push([".cursor/mcp.json", (0, hooks_1.upsertCursorMcpConfig)()]);
|
|
314
|
+
results.push([".gemini/settings.json mcpServers", (0, hooks_1.upsertGeminiMcpConfig)()]);
|
|
315
|
+
}
|
|
316
|
+
else {
|
|
317
|
+
results.push([".mcp.json", mcpGate.reason]);
|
|
318
|
+
results.push([".cursor/mcp.json", mcpGate.reason]);
|
|
319
|
+
results.push([".gemini/settings.json mcpServers", mcpGate.reason]);
|
|
320
|
+
}
|
|
321
|
+
// Routers accumulate user-maintained project state after bootstrap, so they are
|
|
322
|
+
// starter files: templates are written only when the file is absent, never rebuilt.
|
|
323
|
+
results.push(["wiki/startup.md", (0, workspace_1.writeStarter)("wiki/startup.md", templates_1.startup)]);
|
|
324
|
+
results.push(["wiki/index.md", (0, workspace_1.writeStarter)("wiki/index.md", templates_1.index)]);
|
|
325
|
+
results.push(["wiki/meta/operating-model.md", (0, workspace_1.writeManaged)("wiki/meta/operating-model.md", templates_1.wikiOperatingModel)]);
|
|
326
|
+
results.push(["wiki/meta/decision-policy.md", (0, workspace_1.writeManaged)("wiki/meta/decision-policy.md", templates_1.decisionPolicy)]);
|
|
327
|
+
results.push(["wiki/canonical/wiki-operating-model.md", (0, workspace_1.deleteIfGenerated)("wiki/canonical/wiki-operating-model.md", ["# Wiki Operating Model"])]);
|
|
328
|
+
results.push(["wiki/canonical/decision-policy.md", (0, workspace_1.deleteIfGenerated)("wiki/canonical/decision-policy.md", ["# Decision Policy"])]);
|
|
329
|
+
results.push(["wiki/decisions/wiki-v1-decisions.md", (0, workspace_1.deleteIfGenerated)("wiki/decisions/wiki-v1-decisions.md", ["# Wiki v1 Decisions", "# Wiki Operations v1 Decisions"])]);
|
|
330
|
+
for (const [relativePath, content] of Object.entries(templates_1.starterFiles)) {
|
|
331
|
+
if (!templates_1.defaultStarterFilePaths.has(relativePath))
|
|
332
|
+
continue;
|
|
333
|
+
results.push([relativePath, (0, workspace_1.writeStarter)(relativePath, content)]);
|
|
334
|
+
}
|
|
335
|
+
results.push(["wiki/meta/wiki-ops-v1-decisions.md", (0, workspace_1.writeManaged)("wiki/meta/wiki-ops-v1-decisions.md", templates_1.starterFiles["wiki/meta/wiki-ops-v1-decisions.md"])]);
|
|
336
|
+
if (args_1.glossaryMode) {
|
|
337
|
+
results.push(["wiki/canonical/glossary.md", (0, workspace_1.writeStarter)("wiki/canonical/glossary.md", templates_1.glossary)]);
|
|
338
|
+
results.push(["wiki/index.md glossary router", (0, workspace_1.upsertMarkedSection)("wiki/index.md", "<!-- PROJECT-WIKI-GLOSSARY:START -->", "<!-- PROJECT-WIKI-GLOSSARY:END -->", templates_1.glossaryIndexBlock)]);
|
|
339
|
+
}
|
|
340
|
+
if (args_1.captureInboxMode) {
|
|
341
|
+
results.push(["wiki/inbox/project-candidates.md", (0, modes_1.appendCaptureInbox)()]);
|
|
342
|
+
results.push(["wiki/index.md inbox router", (0, workspace_1.upsertMarkedSection)("wiki/index.md", "<!-- PROJECT-WIKI-INBOX:START -->", "<!-- PROJECT-WIKI-INBOX:END -->", templates_1.inboxIndexBlock)]);
|
|
343
|
+
}
|
|
344
|
+
if (args_1.refreshIndexMode) {
|
|
345
|
+
results.push(["wiki/index.md auto-discovered pages", (0, workspace_1.upsertMarkedSection)("wiki/index.md", "<!-- PROJECT-WIKI-AUTO-INDEX:START -->", "<!-- PROJECT-WIKI-AUTO-INDEX:END -->", (0, modes_1.buildRefreshIndexBlock)())]);
|
|
346
|
+
}
|
|
347
|
+
if (args_1.migrateMode && migrationState) {
|
|
348
|
+
const migration = (0, migration_1.runMigrationMode)(migrationState);
|
|
349
|
+
for (const result of migration.results)
|
|
350
|
+
results.push(result);
|
|
351
|
+
results.push(["migration summary", `${migration.total} files from ${migration.legacyPath || "no legacy"}`]);
|
|
352
|
+
}
|
|
353
|
+
const modes = [];
|
|
354
|
+
if (args_1.migrateMode)
|
|
355
|
+
modes.push("migration");
|
|
356
|
+
if (args_1.glossaryMode)
|
|
357
|
+
modes.push("glossary");
|
|
358
|
+
if (args_1.captureInboxMode)
|
|
359
|
+
modes.push("capture-inbox");
|
|
360
|
+
if (args_1.refreshIndexMode)
|
|
361
|
+
modes.push("refresh-index");
|
|
362
|
+
if (args_1.noGitConfigMode)
|
|
363
|
+
modes.push("no-git-config");
|
|
364
|
+
console.log(modes.length > 0 ? `Project Librarian + ${modes.join(" + ")} complete.` : "Project Librarian complete.");
|
|
365
|
+
for (const [relativePath, status] of results) {
|
|
366
|
+
console.log(`${String(status).padEnd(7)} ${relativePath}`);
|
|
367
|
+
}
|
|
270
368
|
}
|