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/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 normalizeMigrationCopyText(text) {
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
- if (file.endsWith("/migration-inbox.md"))
541
- return false;
542
- const starter = templates_1.starterFiles[file];
543
- return !starter || normalizeMigrationCopyText(starter) !== normalizeMigrationCopyText(text);
544
- }
545
- function migrationCopyDiagnostics(files) {
546
- const roots = legacyWikiRoots();
547
- if (roots.length === 0)
548
- return [];
549
- const guardedFiles = files.filter((file) => shouldGuardAgainstMigrationCopy(file, (0, workspace_1.read)(file)));
550
- if (guardedFiles.length === 0)
551
- return [];
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\` imports \`AGENTS.md\` for Claude Code compatibility. This file owns detailed wiki editing rules.
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\`, \`CLAUDE.md\`, hooks, or skills instead.
59
- - During migration review, do not copy legacy markdown files directly into \`canonical/\`, \`decisions/\`, or \`sources/\`. Rewrite only the useful project meaning, cite current-project evidence when possible, and keep unresolved or ambiguous material in migration inboxes.
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
- - The Codex and Claude Code session-start hooks inject only this file and \`wiki/index.md\`.
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
- - The Codex and Claude Code session-start hooks inject only \`wiki/startup.md\` and \`wiki/index.md\`.
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 and Claude Code 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]] |
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]] |
@@ -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.0",
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": "node benchmarks/project-metrics.js",
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 build && node benchmarks/project-metrics.js --quick --runs 2 --warmup-runs 1 --sample-repo benchmarks/samples/web-service --sample-repo benchmarks/samples/python-cli --sample-repo benchmarks/samples/mixed-monorepo --out benchmarks/reports/ci-current.json --markdown benchmarks/reports/ci-current.md",
44
- "benchmark:release": "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 --fail-on-regression --require-clean --markdown",
45
- "benchmark:trend": "node benchmarks/project-metrics.js --trend-markdown",
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",