project-librarian 0.2.0 → 0.2.1
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.ja.md +34 -46
- package/README.ko.md +35 -47
- package/README.md +38 -47
- package/README.zh.md +34 -46
- package/SKILL.md +27 -12
- package/dist/args.js +10 -1
- package/dist/code-index-file-policy.js +2 -0
- package/dist/hooks.js +65 -7
- package/dist/init-project-wiki.js +25 -1
- package/dist/install-skill.js +28 -9
- package/dist/migration.js +203 -4
- package/dist/modes.js +129 -100
- package/dist/templates.js +28 -7
- package/dist/wiki-files.js +9 -1
- package/package.json +9 -6
package/dist/modes.js
CHANGED
|
@@ -42,8 +42,13 @@ exports.runIssueCreateMode = runIssueCreateMode;
|
|
|
42
42
|
exports.runPruneCheckMode = runPruneCheckMode;
|
|
43
43
|
exports.collectLinkDiagnostics = collectLinkDiagnostics;
|
|
44
44
|
exports.collectQualityDiagnostics = collectQualityDiagnostics;
|
|
45
|
+
exports.collectMigrationQualityDiagnostics = collectMigrationQualityDiagnostics;
|
|
46
|
+
exports.collectMigrationLintDiagnostics = collectMigrationLintDiagnostics;
|
|
45
47
|
exports.runLinkCheckMode = runLinkCheckMode;
|
|
46
48
|
exports.runQualityCheckMode = runQualityCheckMode;
|
|
49
|
+
exports.runMigrationQualityCheckMode = runMigrationQualityCheckMode;
|
|
50
|
+
exports.runMigrationLintMode = runMigrationLintMode;
|
|
51
|
+
exports.runMigrationDoctorMode = runMigrationDoctorMode;
|
|
47
52
|
exports.runDoctorMode = runDoctorMode;
|
|
48
53
|
exports.runLintMode = runLintMode;
|
|
49
54
|
const fs = __importStar(require("node:fs"));
|
|
@@ -53,6 +58,7 @@ const path = __importStar(require("node:path"));
|
|
|
53
58
|
const args_1 = require("./args");
|
|
54
59
|
const workspace_1 = require("./workspace");
|
|
55
60
|
const templates_1 = require("./templates");
|
|
61
|
+
const migration_1 = require("./migration");
|
|
56
62
|
const wiki_files_1 = require("./wiki-files");
|
|
57
63
|
const scopedAutoIndexThreshold = 40;
|
|
58
64
|
const scopedAutoIndexMarker = "<!-- PROJECT-WIKI-SCOPED-AUTO-INDEX -->";
|
|
@@ -262,6 +268,7 @@ function issueDraftMarkdown() {
|
|
|
262
268
|
const generatedFiles = existingFileList([
|
|
263
269
|
"AGENTS.md",
|
|
264
270
|
"CLAUDE.md",
|
|
271
|
+
"GEMINI.md",
|
|
265
272
|
"wiki/AGENTS.md",
|
|
266
273
|
"wiki/startup.md",
|
|
267
274
|
"wiki/index.md",
|
|
@@ -269,6 +276,11 @@ function issueDraftMarkdown() {
|
|
|
269
276
|
".codex/hooks/wiki-session-start.js",
|
|
270
277
|
".claude/settings.json",
|
|
271
278
|
".claude/hooks/wiki-session-start.js",
|
|
279
|
+
".cursor/rules/project-librarian.mdc",
|
|
280
|
+
".cursor/hooks.json",
|
|
281
|
+
".cursor/hooks/wiki-session-start.js",
|
|
282
|
+
".gemini/settings.json",
|
|
283
|
+
".gemini/hooks/wiki-session-start.js",
|
|
272
284
|
".githooks/prepare-commit-msg",
|
|
273
285
|
".githooks/wiki-commit-trailers.js",
|
|
274
286
|
]);
|
|
@@ -504,107 +516,21 @@ function legacyWikiRoots() {
|
|
|
504
516
|
.map((entry) => entry.name)
|
|
505
517
|
.sort();
|
|
506
518
|
}
|
|
507
|
-
function
|
|
508
|
-
return (0, workspace_1.stripMetadataHeader)(text)
|
|
509
|
-
.replace(/<!--[\s\S]*?-->/g, " ")
|
|
510
|
-
.replace(/\r?\n/g, " ")
|
|
511
|
-
.replace(/\s+/g, " ")
|
|
512
|
-
.trim()
|
|
513
|
-
.toLowerCase();
|
|
514
|
-
}
|
|
515
|
-
function migrationCopyTokens(text) {
|
|
516
|
-
return normalizeMigrationCopyText(text).match(/[\p{L}\p{N}_./-]+/gu) ?? [];
|
|
517
|
-
}
|
|
518
|
-
function tokenOverlapScore(left, right) {
|
|
519
|
-
if (left.length === 0 || right.length === 0)
|
|
520
|
-
return 0;
|
|
521
|
-
const counts = new Map();
|
|
522
|
-
for (const token of right)
|
|
523
|
-
counts.set(token, (counts.get(token) ?? 0) + 1);
|
|
524
|
-
let overlap = 0;
|
|
525
|
-
for (const token of left) {
|
|
526
|
-
const count = counts.get(token) ?? 0;
|
|
527
|
-
if (count <= 0)
|
|
528
|
-
continue;
|
|
529
|
-
overlap += 1;
|
|
530
|
-
if (count === 1)
|
|
531
|
-
counts.delete(token);
|
|
532
|
-
else
|
|
533
|
-
counts.set(token, count - 1);
|
|
534
|
-
}
|
|
535
|
-
return overlap / Math.max(left.length, right.length);
|
|
536
|
-
}
|
|
537
|
-
function shouldGuardAgainstMigrationCopy(file, text) {
|
|
519
|
+
function shouldGuardAgainstLegacyReference(file) {
|
|
538
520
|
if (!/^wiki\/(?:canonical|decisions|sources)\//.test(file))
|
|
539
521
|
return false;
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
return
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
const legacyEntries = roots
|
|
553
|
-
.flatMap((legacyRoot) => (0, wiki_files_1.walkMarkdownFiles)((0, workspace_1.abs)(legacyRoot), [], (0, workspace_1.abs)(legacyRoot)))
|
|
554
|
-
.map((legacyFile) => {
|
|
555
|
-
const text = (0, workspace_1.read)(legacyFile.path);
|
|
556
|
-
return {
|
|
557
|
-
file: legacyFile.path,
|
|
558
|
-
basePath: legacyFile.basePath,
|
|
559
|
-
basename: path.basename(legacyFile.basePath).toLowerCase(),
|
|
560
|
-
normalized: normalizeMigrationCopyText(text),
|
|
561
|
-
tokens: migrationCopyTokens(text),
|
|
562
|
-
};
|
|
563
|
-
})
|
|
564
|
-
.filter((entry) => entry.normalized.length >= 200);
|
|
565
|
-
const diagnostics = [];
|
|
566
|
-
for (const file of guardedFiles) {
|
|
567
|
-
const text = (0, workspace_1.read)(file);
|
|
568
|
-
const normalized = normalizeMigrationCopyText(text);
|
|
569
|
-
if (normalized.length < 200)
|
|
570
|
-
continue;
|
|
571
|
-
const tokens = migrationCopyTokens(text);
|
|
572
|
-
const basename = path.basename(file).toLowerCase();
|
|
573
|
-
const relativeWithinWiki = file.replace(/^wiki\//, "");
|
|
574
|
-
for (const legacy of legacyEntries) {
|
|
575
|
-
if (normalized === legacy.normalized) {
|
|
576
|
-
diagnostics.push({
|
|
577
|
-
code: "migration-copy-risk",
|
|
578
|
-
severity: "error",
|
|
579
|
-
file,
|
|
580
|
-
message: `body matches legacy document ${legacy.file}; rewrite project truth instead of copying legacy files`,
|
|
581
|
-
});
|
|
582
|
-
break;
|
|
583
|
-
}
|
|
584
|
-
if (tokens.length >= 80 && legacy.tokens.length >= 80) {
|
|
585
|
-
const score = tokenOverlapScore(tokens, legacy.tokens);
|
|
586
|
-
if (score >= 0.92) {
|
|
587
|
-
diagnostics.push({
|
|
588
|
-
code: "migration-copy-risk",
|
|
589
|
-
severity: "error",
|
|
590
|
-
file,
|
|
591
|
-
message: `body is ${Math.round(score * 100)}% token-similar to legacy document ${legacy.file}; rewrite and cite current-project evidence`,
|
|
592
|
-
});
|
|
593
|
-
break;
|
|
594
|
-
}
|
|
595
|
-
}
|
|
596
|
-
if (relativeWithinWiki === legacy.basePath || basename === legacy.basename) {
|
|
597
|
-
diagnostics.push({
|
|
598
|
-
code: "migration-filename-reuse",
|
|
599
|
-
severity: "warn",
|
|
600
|
-
file,
|
|
601
|
-
message: `filename also exists in legacy document ${legacy.file}; verify this is a rewrite, not a file copy`,
|
|
602
|
-
});
|
|
603
|
-
break;
|
|
604
|
-
}
|
|
605
|
-
}
|
|
606
|
-
}
|
|
607
|
-
return diagnostics;
|
|
522
|
+
return !file.endsWith("/migration-inbox.md");
|
|
523
|
+
}
|
|
524
|
+
function migrationLegacyReferenceDiagnostics(files) {
|
|
525
|
+
return files
|
|
526
|
+
.filter(shouldGuardAgainstLegacyReference)
|
|
527
|
+
.filter((file) => /\bwiki_legacy(?:_|\b|\/)/.test((0, workspace_1.stripMetadataHeader)((0, workspace_1.read)(file))))
|
|
528
|
+
.map((file) => ({
|
|
529
|
+
code: "migration-legacy-reference",
|
|
530
|
+
severity: "error",
|
|
531
|
+
file,
|
|
532
|
+
message: "new project truth must not link to or cite wiki_legacy*; migrate the meaning or keep unresolved material in migration inboxes",
|
|
533
|
+
}));
|
|
608
534
|
}
|
|
609
535
|
function collectQualityDiagnostics() {
|
|
610
536
|
const diagnostics = [];
|
|
@@ -650,9 +576,35 @@ function collectQualityDiagnostics() {
|
|
|
650
576
|
}
|
|
651
577
|
}
|
|
652
578
|
}
|
|
653
|
-
diagnostics.push(...migrationCopyDiagnostics(files));
|
|
654
579
|
return diagnostics.sort((a, b) => a.file.localeCompare(b.file) || a.code.localeCompare(b.code));
|
|
655
580
|
}
|
|
581
|
+
function collectMigrationQualityDiagnostics() {
|
|
582
|
+
const files = (0, wiki_files_1.wikiMarkdownFiles)();
|
|
583
|
+
return migrationLegacyReferenceDiagnostics(files).sort((a, b) => a.file.localeCompare(b.file) || a.code.localeCompare(b.code));
|
|
584
|
+
}
|
|
585
|
+
function collectMigrationLintDiagnostics() {
|
|
586
|
+
if (legacyWikiRoots().length === 0)
|
|
587
|
+
return [];
|
|
588
|
+
const requiredFiles = [
|
|
589
|
+
"wiki/migration/inventory.md",
|
|
590
|
+
"wiki/migration/coverage.md",
|
|
591
|
+
"wiki/migration/plan.md",
|
|
592
|
+
"wiki/migration/verification.md",
|
|
593
|
+
"wiki/canonical/migration-inbox.md",
|
|
594
|
+
"wiki/decisions/migration-inbox.md",
|
|
595
|
+
"wiki/sources/migration-inbox.md",
|
|
596
|
+
];
|
|
597
|
+
const diagnostics = requiredFiles
|
|
598
|
+
.filter((file) => !(0, workspace_1.exists)(file))
|
|
599
|
+
.map((file) => ({
|
|
600
|
+
code: "migration-missing-file",
|
|
601
|
+
severity: "error",
|
|
602
|
+
file,
|
|
603
|
+
message: "migration review files are missing; run --migrate or keep migration diagnostics out of normal doctor",
|
|
604
|
+
}));
|
|
605
|
+
diagnostics.push(...(0, migration_1.collectMigrationCoverageDiagnostics)());
|
|
606
|
+
return diagnostics.sort((a, b) => a.file.localeCompare(b.file) || a.code.localeCompare(b.code) || a.message.localeCompare(b.message));
|
|
607
|
+
}
|
|
656
608
|
function runLinkCheckMode() {
|
|
657
609
|
const ok = printDiagnostics("Project wiki link-check", collectLinkDiagnostics(), (0, wiki_files_1.wikiMarkdownFiles)().length);
|
|
658
610
|
if (!ok)
|
|
@@ -663,6 +615,22 @@ function runQualityCheckMode() {
|
|
|
663
615
|
if (!ok)
|
|
664
616
|
process.exit(1);
|
|
665
617
|
}
|
|
618
|
+
function runMigrationQualityCheckMode() {
|
|
619
|
+
const ok = printDiagnostics("Project wiki migration quality-check", collectMigrationQualityDiagnostics(), (0, wiki_files_1.wikiMarkdownFiles)().length);
|
|
620
|
+
if (!ok)
|
|
621
|
+
process.exit(1);
|
|
622
|
+
}
|
|
623
|
+
function runMigrationLintMode() {
|
|
624
|
+
const ok = printDiagnostics("Project wiki migration lint", collectMigrationLintDiagnostics(), (0, wiki_files_1.wikiMarkdownFiles)().length);
|
|
625
|
+
if (!ok)
|
|
626
|
+
process.exit(1);
|
|
627
|
+
}
|
|
628
|
+
function runMigrationDoctorMode() {
|
|
629
|
+
const lintOk = printDiagnostics("Project wiki migration lint", collectMigrationLintDiagnostics(), (0, wiki_files_1.wikiMarkdownFiles)().length);
|
|
630
|
+
const qualityOk = printDiagnostics("Project wiki migration quality-check", collectMigrationQualityDiagnostics(), (0, wiki_files_1.wikiMarkdownFiles)().length);
|
|
631
|
+
if (!lintOk || !qualityOk)
|
|
632
|
+
process.exit(1);
|
|
633
|
+
}
|
|
666
634
|
function runDoctorMode(fix) {
|
|
667
635
|
if (fix) {
|
|
668
636
|
console.log("Project wiki doctor --fix");
|
|
@@ -687,6 +655,7 @@ function runLintMode() {
|
|
|
687
655
|
const requiredFiles = [
|
|
688
656
|
"AGENTS.md",
|
|
689
657
|
"CLAUDE.md",
|
|
658
|
+
"GEMINI.md",
|
|
690
659
|
"wiki/AGENTS.md",
|
|
691
660
|
"wiki/startup.md",
|
|
692
661
|
"wiki/index.md",
|
|
@@ -705,6 +674,11 @@ function runLintMode() {
|
|
|
705
674
|
".codex/hooks.json",
|
|
706
675
|
".claude/hooks/wiki-session-start.js",
|
|
707
676
|
".claude/settings.json",
|
|
677
|
+
".cursor/rules/project-librarian.mdc",
|
|
678
|
+
".cursor/hooks/wiki-session-start.js",
|
|
679
|
+
".cursor/hooks.json",
|
|
680
|
+
".gemini/hooks/wiki-session-start.js",
|
|
681
|
+
".gemini/settings.json",
|
|
708
682
|
];
|
|
709
683
|
for (const file of requiredFiles) {
|
|
710
684
|
if (!(0, workspace_1.exists)(file))
|
|
@@ -735,6 +709,13 @@ function runLintMode() {
|
|
|
735
709
|
warnings.push("root AGENTS.md should point detailed wiki editing rules to wiki/AGENTS.md");
|
|
736
710
|
if ((0, workspace_1.exists)("CLAUDE.md") && !(0, workspace_1.read)("CLAUDE.md").includes("@AGENTS.md"))
|
|
737
711
|
errors.push("CLAUDE.md should import AGENTS.md for Claude Code compatibility");
|
|
712
|
+
if ((0, workspace_1.exists)("GEMINI.md") && !(0, workspace_1.read)("GEMINI.md").includes("@AGENTS.md"))
|
|
713
|
+
errors.push("GEMINI.md should import AGENTS.md for Gemini CLI compatibility");
|
|
714
|
+
if ((0, workspace_1.exists)(".cursor/rules/project-librarian.mdc")) {
|
|
715
|
+
const cursorRule = (0, workspace_1.read)(".cursor/rules/project-librarian.mdc");
|
|
716
|
+
if (!cursorRule.includes("alwaysApply: true") || !cursorRule.includes("@AGENTS.md"))
|
|
717
|
+
errors.push("Cursor project rule should always apply and reference AGENTS.md");
|
|
718
|
+
}
|
|
738
719
|
if ((0, workspace_1.exists)("wiki/AGENTS.md") && !(0, workspace_1.read)("wiki/AGENTS.md").includes("Language policy"))
|
|
739
720
|
warnings.push("wiki/AGENTS.md is missing language policy");
|
|
740
721
|
for (const legacyFile of ["wiki/canonical/wiki-operating-model.md", "wiki/canonical/decision-policy.md", "wiki/decisions/wiki-v1-decisions.md"]) {
|
|
@@ -751,6 +732,16 @@ function runLintMode() {
|
|
|
751
732
|
if (!hook.includes('["wiki/startup.md", 3500]') || !hook.includes('["wiki/index.md", 4500]'))
|
|
752
733
|
errors.push("Claude startup hook does not clearly inject only startup/index with expected budgets");
|
|
753
734
|
}
|
|
735
|
+
if ((0, workspace_1.exists)(".cursor/hooks/wiki-session-start.js")) {
|
|
736
|
+
const hook = (0, workspace_1.read)(".cursor/hooks/wiki-session-start.js");
|
|
737
|
+
if (!hook.includes('["wiki/startup.md", 3500]') || !hook.includes('["wiki/index.md", 4500]') || !hook.includes("additional_context"))
|
|
738
|
+
errors.push("Cursor startup hook does not clearly inject startup/index through additional_context");
|
|
739
|
+
}
|
|
740
|
+
if ((0, workspace_1.exists)(".gemini/hooks/wiki-session-start.js")) {
|
|
741
|
+
const hook = (0, workspace_1.read)(".gemini/hooks/wiki-session-start.js");
|
|
742
|
+
if (!hook.includes('["wiki/startup.md", 3500]') || !hook.includes('["wiki/index.md", 4500]') || !hook.includes("hookSpecificOutput"))
|
|
743
|
+
errors.push("Gemini startup hook does not clearly inject startup/index through hookSpecificOutput");
|
|
744
|
+
}
|
|
754
745
|
if ((0, workspace_1.exists)(".claude/settings.json")) {
|
|
755
746
|
const command = "node .claude/hooks/wiki-session-start.js";
|
|
756
747
|
try {
|
|
@@ -772,6 +763,44 @@ function runLintMode() {
|
|
|
772
763
|
errors.push(message);
|
|
773
764
|
}
|
|
774
765
|
}
|
|
766
|
+
if ((0, workspace_1.exists)(".cursor/hooks.json")) {
|
|
767
|
+
const command = "node .cursor/hooks/wiki-session-start.js";
|
|
768
|
+
try {
|
|
769
|
+
const settings = (0, workspace_1.parseJson)(".cursor/hooks.json", { version: 1, hooks: {} });
|
|
770
|
+
if (!settings.hooks || typeof settings.hooks !== "object" || Array.isArray(settings.hooks)) {
|
|
771
|
+
throw new Error(".cursor/hooks.json has invalid hooks object");
|
|
772
|
+
}
|
|
773
|
+
const sessionStart = settings.hooks.sessionStart ?? [];
|
|
774
|
+
if (!Array.isArray(sessionStart) || !sessionStart.some((hook) => hook?.command === command)) {
|
|
775
|
+
errors.push(".cursor/hooks.json is missing the project wiki sessionStart hook");
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
catch (error) {
|
|
779
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
780
|
+
errors.push(message);
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
if ((0, workspace_1.exists)(".gemini/settings.json")) {
|
|
784
|
+
const command = 'node "$GEMINI_PROJECT_DIR/.gemini/hooks/wiki-session-start.js"';
|
|
785
|
+
try {
|
|
786
|
+
const settings = (0, workspace_1.parseJson)(".gemini/settings.json", { hooks: {} });
|
|
787
|
+
if (!settings.hooks || typeof settings.hooks !== "object" || Array.isArray(settings.hooks)) {
|
|
788
|
+
throw new Error(".gemini/settings.json has invalid hooks object");
|
|
789
|
+
}
|
|
790
|
+
const sessionStart = settings.hooks.SessionStart ?? [];
|
|
791
|
+
const configuredMatchers = new Set(sessionStart
|
|
792
|
+
.filter((entry) => Array.isArray(entry.hooks) && entry.hooks.some((hook) => hook.command === command))
|
|
793
|
+
.map((entry) => entry.matcher));
|
|
794
|
+
for (const matcher of ["startup", "resume", "clear"]) {
|
|
795
|
+
if (!configuredMatchers.has(matcher))
|
|
796
|
+
errors.push(`.gemini/settings.json is missing the project wiki SessionStart hook for ${matcher}`);
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
catch (error) {
|
|
800
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
801
|
+
errors.push(message);
|
|
802
|
+
}
|
|
803
|
+
}
|
|
775
804
|
for (const file of [".githooks/prepare-commit-msg", ".githooks/wiki-commit-trailers.js"]) {
|
|
776
805
|
if ((0, workspace_1.exists)(file) && (fs.statSync((0, workspace_1.abs)(file)).mode & 0o111) === 0)
|
|
777
806
|
errors.push(`${file} is not executable`);
|
package/dist/templates.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.starterFiles = exports.decisionPolicy = exports.wikiOperatingModel = exports.inboxIndexBlock = exports.glossaryIndexBlock = exports.glossary = exports.index = exports.startup = exports.metadata = exports.wikiAgentsSection = exports.claudeSection = exports.agentsSection = void 0;
|
|
3
|
+
exports.starterFiles = exports.decisionPolicy = exports.wikiOperatingModel = exports.inboxIndexBlock = exports.glossaryIndexBlock = exports.glossary = exports.index = exports.startup = exports.metadata = exports.wikiAgentsSection = exports.cursorRule = exports.geminiSection = exports.claudeSection = exports.agentsSection = void 0;
|
|
4
4
|
const workspace_1 = require("./workspace");
|
|
5
5
|
exports.agentsSection = `<!-- PROJECT-WIKI-FIRST:START -->
|
|
6
6
|
## Wiki-First Planning
|
|
@@ -31,10 +31,31 @@ Claude Code reads \`CLAUDE.md\`, not \`AGENTS.md\`, so this file imports \`AGENT
|
|
|
31
31
|
|
|
32
32
|
At session start, follow the imported instructions: review \`wiki/startup.md\` and \`wiki/index.md\` first, then read detailed wiki pages on demand only when the current task needs them.
|
|
33
33
|
<!-- PROJECT-WIKI-CLAUDE:END -->`;
|
|
34
|
+
exports.geminiSection = `<!-- PROJECT-WIKI-GEMINI:START -->
|
|
35
|
+
# Gemini CLI Project Instructions
|
|
36
|
+
|
|
37
|
+
@AGENTS.md
|
|
38
|
+
|
|
39
|
+
## Gemini CLI Notes
|
|
40
|
+
|
|
41
|
+
Gemini CLI reads \`GEMINI.md\` by default, so this file imports \`AGENTS.md\` to share the same wiki-first planning contract with Codex, Claude Code, Cursor, and other agents.
|
|
42
|
+
|
|
43
|
+
At session start, follow the imported instructions: review \`wiki/startup.md\` and \`wiki/index.md\` first, then read detailed wiki pages on demand only when the current task needs them.
|
|
44
|
+
<!-- PROJECT-WIKI-GEMINI:END -->`;
|
|
45
|
+
exports.cursorRule = `---
|
|
46
|
+
alwaysApply: true
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
# Project Librarian Wiki-First Planning
|
|
50
|
+
|
|
51
|
+
Use the repository root \`AGENTS.md\` as the project-wide instruction source.
|
|
52
|
+
|
|
53
|
+
@AGENTS.md
|
|
54
|
+
`;
|
|
34
55
|
exports.wikiAgentsSection = `<!-- PROJECT-WIKI-INTERNAL:START -->
|
|
35
56
|
## Wiki Internal Rules
|
|
36
57
|
|
|
37
|
-
This file applies to \`./wiki\` and its children. Root \`AGENTS.md\` owns the project-wide wiki-first contract. Root \`CLAUDE.md\`
|
|
58
|
+
This file applies to \`./wiki\` and its children. Root \`AGENTS.md\` owns the project-wide wiki-first contract. Root \`CLAUDE.md\` and \`GEMINI.md\` import \`AGENTS.md\` for agent compatibility. This file owns detailed wiki editing rules.
|
|
38
59
|
|
|
39
60
|
Language policy:
|
|
40
61
|
|
|
@@ -55,8 +76,8 @@ Storage boundaries:
|
|
|
55
76
|
- \`meta/\` contains wiki operating rules, decision policy, bootstrap, lint, hook, and migration decisions.
|
|
56
77
|
- \`sources/\` contains external reference summaries and source notes.
|
|
57
78
|
- \`inbox/\` and migration inbox files contain candidates, not canonical truth.
|
|
58
|
-
- Do not store non-project LLM memory, assistant preferences, collaboration reminders, or workflow instructions in \`canonical/\` or \`decisions/\`; use root \`AGENTS.md\`,
|
|
59
|
-
- During migration review,
|
|
79
|
+
- Do not store non-project LLM memory, assistant preferences, collaboration reminders, or workflow instructions in \`canonical/\` or \`decisions/\`; use root \`AGENTS.md\`, compatibility instruction files, hooks, rules, or skills instead.
|
|
80
|
+
- During migration review, preserve useful meaning while converting it to the current wiki structure. Legacy files, sections, blocks, and wording may be retained when review confirms they belong in the new topic shape and remain current project truth. Do not link to or cite \`wiki_legacy*\` from the new wiki; cite current-project evidence when possible and keep unresolved or ambiguous material in migration inboxes.
|
|
60
81
|
|
|
61
82
|
Update rules:
|
|
62
83
|
|
|
@@ -126,7 +147,7 @@ exports.startup = `${(0, exports.metadata)("startup-router", "short", "wiki/meta
|
|
|
126
147
|
|
|
127
148
|
## Token Discipline
|
|
128
149
|
|
|
129
|
-
-
|
|
150
|
+
- Codex, Claude Code, Cursor, and Gemini CLI session-start hooks inject only this file and \`wiki/index.md\`.
|
|
130
151
|
- Detailed files are selected by the "read when" rules in \`wiki/index.md\`.
|
|
131
152
|
- Long decision history is not injected wholesale; read only relevant Decision Packs or ADRs.
|
|
132
153
|
`;
|
|
@@ -254,7 +275,7 @@ exports.wikiOperatingModel = `${(0, exports.metadata)("wiki-meta", "medium", "wi
|
|
|
254
275
|
## TL;DR
|
|
255
276
|
|
|
256
277
|
- This wiki keeps project-planning knowledge as durable markdown.
|
|
257
|
-
-
|
|
278
|
+
- Codex, Claude Code, Cursor, and Gemini CLI session-start hooks inject only \`wiki/startup.md\` and \`wiki/index.md\`.
|
|
258
279
|
- Detailed canonical and decision files are read on demand.
|
|
259
280
|
- Root \`AGENTS.md\` keeps the project-wide wiki-first contract; \`wiki/AGENTS.md\` keeps detailed wiki editing rules.
|
|
260
281
|
- Operating documents generated by bootstrap are English by default.
|
|
@@ -492,7 +513,7 @@ Canonical: [[meta/operating-model]], [[meta/decision-policy]]
|
|
|
492
513
|
| --- | --- | --- | --- | --- | --- |
|
|
493
514
|
| ${workspace_1.today} | Keep the wiki root at \`./wiki\`. | Planning docs live with the project. | External docs only. | Another tool cannot read \`./wiki\` or the team needs another path. | [[meta/operating-model]] |
|
|
494
515
|
| ${workspace_1.today} | Split \`canonical/\` and \`decisions/\`. | Current truth and decision history are easier to scan when separated. | A single mixed docs directory. | The structure proves too heavy for small projects. | [[meta/decision-policy]] |
|
|
495
|
-
| ${workspace_1.today} | Inject only \`startup.md\` and \`index.md\` through Codex
|
|
516
|
+
| ${workspace_1.today} | Inject only \`startup.md\` and \`index.md\` through Codex, Claude Code, Cursor, and Gemini CLI startup hooks; route detailed files Read On Demand. | Full canonical and decision bodies waste startup tokens. | Always read detailed canonical and decision files first. | Important context is repeatedly missed at startup. | [[startup]], [[index]] |
|
|
496
517
|
| ${workspace_1.today} | Use metadata headers on wiki knowledge pages. | Agents and humans can quickly judge status, scope, budget, and review triggers. | Body-only conventions. | Header maintenance costs more than it saves. | [[meta/operating-model]] |
|
|
497
518
|
| ${workspace_1.today} | Keep wiki operating docs in \`wiki/meta/\`. | Project truth stays focused on product/project content. | Store operating docs in \`canonical/\` or \`decisions/\`. | Meta docs become hard to discover. | [[meta/operating-model]] |
|
|
498
519
|
| ${workspace_1.today} | Bootstrap-generated operating documents are English by default. | Repository entry points and operating contracts are easier for public users to inspect. | Generate operating docs in a fixed non-English language. | The project intentionally targets a single-language local audience. | [[meta/operating-model]] |
|
package/dist/wiki-files.js
CHANGED
|
@@ -56,6 +56,8 @@ const path = __importStar(require("node:path"));
|
|
|
56
56
|
const workspace_1 = require("./workspace");
|
|
57
57
|
exports.standardWikiFiles = new Set([
|
|
58
58
|
"AGENTS.md",
|
|
59
|
+
"CLAUDE.md",
|
|
60
|
+
"GEMINI.md",
|
|
59
61
|
"wiki/AGENTS.md",
|
|
60
62
|
".githooks/prepare-commit-msg",
|
|
61
63
|
".githooks/wiki-commit-trailers.js",
|
|
@@ -63,11 +65,17 @@ exports.standardWikiFiles = new Set([
|
|
|
63
65
|
".codex/hooks/wiki-session-start.js",
|
|
64
66
|
".claude/settings.json",
|
|
65
67
|
".claude/hooks/wiki-session-start.js",
|
|
68
|
+
".cursor/rules/project-librarian.mdc",
|
|
69
|
+
".cursor/hooks.json",
|
|
70
|
+
".cursor/hooks/wiki-session-start.js",
|
|
71
|
+
".gemini/settings.json",
|
|
72
|
+
".gemini/hooks/wiki-session-start.js",
|
|
66
73
|
"wiki/README.md",
|
|
67
74
|
"wiki/startup.md",
|
|
68
75
|
"wiki/index.md",
|
|
69
76
|
"wiki/inbox/project-candidates.md",
|
|
70
77
|
"wiki/migration/inventory.md",
|
|
78
|
+
"wiki/migration/coverage.md",
|
|
71
79
|
"wiki/migration/plan.md",
|
|
72
80
|
"wiki/migration/review.md",
|
|
73
81
|
"wiki/migration/verification.md",
|
|
@@ -92,7 +100,7 @@ exports.standardWikiFiles = new Set([
|
|
|
92
100
|
"tools/project-librarian/agents/openai.yaml",
|
|
93
101
|
"tools/project-librarian/dist/init-project-wiki.js",
|
|
94
102
|
]);
|
|
95
|
-
exports.ignoredDirs = new Set([".git", ".codex", ".claude", "node_modules", ".next", "dist", "build", "coverage", "vendor", "tmp", "temp"]);
|
|
103
|
+
exports.ignoredDirs = new Set([".git", ".codex", ".claude", ".cursor", ".gemini", "node_modules", ".next", "dist", "build", "coverage", "vendor", "tmp", "temp"]);
|
|
96
104
|
function walkMarkdownFiles(dir = workspace_1.root, acc = [], baseDir = workspace_1.root) {
|
|
97
105
|
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
98
106
|
const fullPath = path.join(dir, entry.name);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "project-librarian",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Create and maintain compact project context for humans and LLM coding agents.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "commonjs",
|
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
"agents",
|
|
9
9
|
"claude-code",
|
|
10
10
|
"codex",
|
|
11
|
+
"cursor",
|
|
12
|
+
"gemini",
|
|
11
13
|
"llm",
|
|
12
14
|
"project-planning",
|
|
13
15
|
"wiki"
|
|
@@ -37,12 +39,13 @@
|
|
|
37
39
|
"node": ">=22.13"
|
|
38
40
|
},
|
|
39
41
|
"scripts": {
|
|
40
|
-
"benchmark": "
|
|
41
|
-
"benchmark:baseline": "npm run build && node benchmarks/project-metrics.js --sample-repo benchmarks/samples/web-service --sample-repo benchmarks/samples/python-cli --sample-repo benchmarks/samples/mixed-monorepo --require-clean --save-baseline --markdown",
|
|
42
|
+
"benchmark": "npm run benchmark:llm:dry-run",
|
|
42
43
|
"benchmark:ci": "npm run benchmark:ci-smoke",
|
|
43
|
-
"benchmark:ci-smoke": "npm run
|
|
44
|
-
"benchmark:
|
|
45
|
-
"benchmark:
|
|
44
|
+
"benchmark:ci-smoke": "npm run benchmark:llm:parse-smoke && npm run benchmark:llm:dry-run",
|
|
45
|
+
"benchmark:llm": "npm run build && node benchmarks/codex-llm-metrics.js",
|
|
46
|
+
"benchmark:llm:dry-run": "npm run build && node benchmarks/codex-llm-metrics.js --dry-run",
|
|
47
|
+
"benchmark:llm:parse-smoke": "node tests/validators/codex-llm-benchmark-smoke.js",
|
|
48
|
+
"benchmark:release": "npm run benchmark:llm -- --full-matrix --runs 3 --warmup-runs 1 --min-runs-for-claim 3 --require-clean --require-claimable --out benchmarks/reports/llm/current.json --markdown benchmarks/reports/llm/current.md",
|
|
46
49
|
"build": "tsc && chmod +x dist/init-project-wiki.js",
|
|
47
50
|
"typecheck": "tsc --noEmit",
|
|
48
51
|
"unit": "node --test tests/unit/*.test.js",
|