@jcode.labs/ragmir 2.10.0 → 2.12.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.
Files changed (64) hide show
  1. package/README.md +12 -1
  2. package/dist/chunk-stats.d.ts +8 -0
  3. package/dist/chunk-stats.d.ts.map +1 -0
  4. package/dist/chunk-stats.js +31 -0
  5. package/dist/chunk-stats.js.map +1 -0
  6. package/dist/cli.js +125 -8
  7. package/dist/cli.js.map +1 -1
  8. package/dist/context-resources.d.ts +4 -0
  9. package/dist/context-resources.d.ts.map +1 -0
  10. package/dist/context-resources.js +87 -0
  11. package/dist/context-resources.js.map +1 -0
  12. package/dist/doctor.d.ts.map +1 -1
  13. package/dist/doctor.js +15 -4
  14. package/dist/doctor.js.map +1 -1
  15. package/dist/evaluate.d.ts.map +1 -1
  16. package/dist/evaluate.js +8 -0
  17. package/dist/evaluate.js.map +1 -1
  18. package/dist/gitignore.d.ts +1 -1
  19. package/dist/gitignore.d.ts.map +1 -1
  20. package/dist/gitignore.js +3 -2
  21. package/dist/gitignore.js.map +1 -1
  22. package/dist/index.d.ts +6 -3
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +4 -1
  25. package/dist/index.js.map +1 -1
  26. package/dist/ingest.d.ts.map +1 -1
  27. package/dist/ingest.js +7 -1
  28. package/dist/ingest.js.map +1 -1
  29. package/dist/knowledge-bases.d.ts +4 -0
  30. package/dist/knowledge-bases.d.ts.map +1 -0
  31. package/dist/knowledge-bases.js +113 -0
  32. package/dist/knowledge-bases.js.map +1 -0
  33. package/dist/mcp.d.ts +3 -1
  34. package/dist/mcp.d.ts.map +1 -1
  35. package/dist/mcp.js +38 -7
  36. package/dist/mcp.js.map +1 -1
  37. package/dist/package-manager.d.ts +2 -0
  38. package/dist/package-manager.d.ts.map +1 -1
  39. package/dist/package-manager.js +18 -1
  40. package/dist/package-manager.js.map +1 -1
  41. package/dist/preview.d.ts +3 -0
  42. package/dist/preview.d.ts.map +1 -0
  43. package/dist/preview.js +115 -0
  44. package/dist/preview.js.map +1 -0
  45. package/dist/query.d.ts.map +1 -1
  46. package/dist/query.js +70 -21
  47. package/dist/query.js.map +1 -1
  48. package/dist/research.d.ts.map +1 -1
  49. package/dist/research.js +2 -0
  50. package/dist/research.js.map +1 -1
  51. package/dist/setup.d.ts +3 -1
  52. package/dist/setup.d.ts.map +1 -1
  53. package/dist/setup.js +10 -5
  54. package/dist/setup.js.map +1 -1
  55. package/dist/skill.d.ts +16 -3
  56. package/dist/skill.d.ts.map +1 -1
  57. package/dist/skill.js +319 -20
  58. package/dist/skill.js.map +1 -1
  59. package/dist/types.d.ts +142 -0
  60. package/dist/types.d.ts.map +1 -1
  61. package/dist/version.d.ts +1 -1
  62. package/dist/version.js +1 -1
  63. package/package.json +3 -3
  64. package/skills/ragmir/SKILL.md +28 -0
package/README.md CHANGED
@@ -55,7 +55,10 @@ npx rgr audit --unsupported
55
55
  npx rgr security-audit
56
56
 
57
57
  # Retrieval
58
+ npx rgr preview --path docs --max-chunks 3
58
59
  npx rgr search "deployment decision"
60
+ npx rgr search "deployment decision" --explain
61
+ npx rgr search "deployment decision" --context-path "Operations > Deployment"
59
62
  npx rgr ask "What evidence supports the deployment decision?"
60
63
  npx rgr research "deployment obligations" --compact
61
64
 
@@ -88,7 +91,10 @@ Frequently used exports:
88
91
  | Export | Purpose |
89
92
  | --- | --- |
90
93
  | `setupProject`, `addSourceEntries` | Initialize project state and select files |
94
+ | `discoverKnowledgeBases`, `knowledgeBaseIdentity` | Route root and nested monorepo bases |
95
+ | `getKnowledgeBaseContext`, `getKnowledgeBaseSourceCatalog` | Give agents bounded readiness and source context |
91
96
  | `ingest`, `audit` | Build the index and compare it with files on disk |
97
+ | `previewChunks` | Inspect redacted chunks and distributions without writing storage |
92
98
  | `search`, `ask`, `research`, `expandCitation` | Retrieve or expand cited passages |
93
99
  | `doctor`, `securityAudit` | Inspect readiness and local privacy posture |
94
100
  | `serveMcp` | Start the read-focused local MCP server |
@@ -104,11 +110,16 @@ npx rgr setup --agents claude,codex,kimi,opencode,cline
104
110
  npx rgr doctor
105
111
  ```
106
112
 
107
- Ragmir writes helper files for the selected clients and points them at the current project. MCP
113
+ Ragmir links the canonical skills into each selected client's native project folder, writes a local
114
+ runner plus MCP helpers, and points them at the current project. MCP
108
115
  exposes status, search, ask, research, exact citation expansion, audit, evaluation, usage, and
109
116
  security tools. Retrieval responses have a global byte ceiling and expose metadata-only output
110
117
  metrics. The server does not expose index deletion.
111
118
 
119
+ For a monorepo with root and app-specific bases, run `rgr bases --json` before retrieval when scope
120
+ is unclear. The nearest configured base is active, nested MCP helpers get unique names, and every
121
+ helper pins `RAGMIR_PROJECT_ROOT` so clients cannot drift to a sibling index.
122
+
112
123
  ## Retrieval modes
113
124
 
114
125
  | Mode | Network requirement | Best for |
@@ -0,0 +1,8 @@
1
+ import type { ChunkStats } from "./types.js";
2
+ interface ChunkShape {
3
+ contextPath: string;
4
+ text: string;
5
+ }
6
+ export declare function summarizeChunkStats(chunks: readonly ChunkShape[]): ChunkStats;
7
+ export {};
8
+ //# sourceMappingURL=chunk-stats.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chunk-stats.d.ts","sourceRoot":"","sources":["../src/chunk-stats.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAE5C,UAAU,UAAU;IAClB,WAAW,EAAE,MAAM,CAAA;IACnB,IAAI,EAAE,MAAM,CAAA;CACb;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,SAAS,UAAU,EAAE,GAAG,UAAU,CA2B7E"}
@@ -0,0 +1,31 @@
1
+ export function summarizeChunkStats(chunks) {
2
+ if (chunks.length === 0) {
3
+ return {
4
+ count: 0,
5
+ minChars: 0,
6
+ averageChars: 0,
7
+ p50Chars: 0,
8
+ p95Chars: 0,
9
+ maxChars: 0,
10
+ contextualChunks: 0,
11
+ contextualRatio: 0,
12
+ };
13
+ }
14
+ const lengths = chunks.map((chunk) => chunk.text.length).sort((left, right) => left - right);
15
+ const contextualChunks = chunks.filter((chunk) => chunk.contextPath.trim().length > 0).length;
16
+ return {
17
+ count: chunks.length,
18
+ minChars: lengths[0] ?? 0,
19
+ averageChars: lengths.reduce((sum, length) => sum + length, 0) / lengths.length,
20
+ p50Chars: percentile(lengths, 0.5),
21
+ p95Chars: percentile(lengths, 0.95),
22
+ maxChars: lengths.at(-1) ?? 0,
23
+ contextualChunks,
24
+ contextualRatio: contextualChunks / chunks.length,
25
+ };
26
+ }
27
+ function percentile(sortedValues, quantile) {
28
+ const index = Math.ceil(quantile * sortedValues.length) - 1;
29
+ return sortedValues[Math.max(0, index)] ?? 0;
30
+ }
31
+ //# sourceMappingURL=chunk-stats.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chunk-stats.js","sourceRoot":"","sources":["../src/chunk-stats.ts"],"names":[],"mappings":"AAOA,MAAM,UAAU,mBAAmB,CAAC,MAA6B;IAC/D,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,OAAO;YACL,KAAK,EAAE,CAAC;YACR,QAAQ,EAAE,CAAC;YACX,YAAY,EAAE,CAAC;YACf,QAAQ,EAAE,CAAC;YACX,QAAQ,EAAE,CAAC;YACX,QAAQ,EAAE,CAAC;YACX,gBAAgB,EAAE,CAAC;YACnB,eAAe,EAAE,CAAC;SACnB,CAAA;IACH,CAAC;IAED,MAAM,OAAO,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,GAAG,KAAK,CAAC,CAAA;IAC5F,MAAM,gBAAgB,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,MAAM,CAAA;IAE7F,OAAO;QACL,KAAK,EAAE,MAAM,CAAC,MAAM;QACpB,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;QACzB,YAAY,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,CAAC,GAAG,GAAG,MAAM,EAAE,CAAC,CAAC,GAAG,OAAO,CAAC,MAAM;QAC/E,QAAQ,EAAE,UAAU,CAAC,OAAO,EAAE,GAAG,CAAC;QAClC,QAAQ,EAAE,UAAU,CAAC,OAAO,EAAE,IAAI,CAAC;QACnC,QAAQ,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QAC7B,gBAAgB;QAChB,eAAe,EAAE,gBAAgB,GAAG,MAAM,CAAC,MAAM;KAClD,CAAA;AACH,CAAC;AAED,SAAS,UAAU,CAAC,YAAsB,EAAE,QAAgB;IAC1D,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,GAAG,YAAY,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;IAC3D,OAAO,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,IAAI,CAAC,CAAA;AAC9C,CAAC"}
package/dist/cli.js CHANGED
@@ -14,10 +14,12 @@ import { countSkippedByReason } from "./files.js";
14
14
  import { getIndexFreshnessWarning, getLexicalScanWarning } from "./index-diagnostics.js";
15
15
  import { audit, ingest } from "./ingest.js";
16
16
  import { initProject } from "./init.js";
17
+ import { discoverKnowledgeBases, knowledgeBaseIdentity } from "./knowledge-bases.js";
17
18
  import { ingestionLimits } from "./limits.js";
18
19
  import { serveMcp } from "./mcp.js";
19
20
  import { configurePdfOcr, extractPdfPage, inspectPdfOcr, parsePdfOcrEngine } from "./ocr.js";
20
21
  import { rgrCommand } from "./package-manager.js";
22
+ import { previewChunks } from "./preview.js";
21
23
  import { routePrompt } from "./prompt-routing.js";
22
24
  import { ask, search } from "./query.js";
23
25
  import { compactResearchReport, compactSearchResults, research } from "./research.js";
@@ -163,14 +165,15 @@ program
163
165
  });
164
166
  program
165
167
  .command("setup")
166
- .description("Initialize Ragmir, install the agent kit, run doctor, and ingest when safe.")
168
+ .description("Initialize Ragmir, expose native agent skills, generate MCP helpers, and ingest when safe.")
167
169
  .option("--target-dir <path>", "Directory where the skill folder should be copied.", DEFAULT_SKILL_TARGET_DIR)
168
- .option("--agents <list>", `Agent MCP helpers to generate: all, ${SUPPORTED_AGENT_TARGETS.join(", ")}.`, "all")
169
- .option("--mcp-name <name>", "MCP server name used in generated config.", "ragmir")
170
+ .option("--agents <list>", `Native agent skills and MCP helpers to install: all, ${SUPPORTED_AGENT_TARGETS.join(", ")}.`, "all")
171
+ .option("--mcp-name <name>", "MCP server name. Nested monorepo bases get a unique name by default.")
170
172
  .option("--mcp-command <command>", "Custom MCP stdio command for generated helper files.")
171
173
  .option("--mcp-arg <arg>", "Argument for --mcp-command. Repeat for multiple arguments.", collectOptionValue, [])
172
174
  .option("--semantic", "Download the configured Transformers.js embedding model and enable higher-quality semantic retrieval.")
173
175
  .option("--no-ingest", "Skip automatic indexing even when supported files are present.")
176
+ .option("--force-agent-skills", "Replace same-name native skills after reviewing that they can be overwritten.")
174
177
  .option("--json", "Print machine-readable JSON.")
175
178
  .action(async (options, command) => {
176
179
  const cwd = projectRoot(command);
@@ -178,11 +181,12 @@ program
178
181
  cwd,
179
182
  targetDir: options.targetDir,
180
183
  agents: parseAgentTargets(options.agents),
181
- mcpServerName: options.mcpName,
182
184
  };
185
+ addOption(setupOptions, "mcpServerName", options.mcpName);
183
186
  addOption(setupOptions, "semantic", options.semantic);
184
187
  addOption(setupOptions, "ingest", options.ingest);
185
188
  addOption(setupOptions, "mcpCommand", options.mcpCommand);
189
+ addOption(setupOptions, "forceAgentSkills", options.forceAgentSkills);
186
190
  if (options.mcpArg.length > 0) {
187
191
  setupOptions.mcpArgs = options.mcpArg;
188
192
  }
@@ -300,6 +304,56 @@ program
300
304
  process.exitCode = 1;
301
305
  }
302
306
  });
307
+ program
308
+ .command("preview")
309
+ .description("Preview redacted chunks and structure without writing the index.")
310
+ .option("--path <prefix>", "Preview only source paths under this prefix. Repeat for multiple prefixes.", collectOptionValue, [])
311
+ .option("--max-files <number>", "Maximum number of matching files to parse.", parsePositiveInt)
312
+ .option("--max-chunks <number>", "Maximum number of chunks to show per file.", parsePositiveInt)
313
+ .option("--json", "Print machine-readable JSON.")
314
+ .action(async (options, command) => {
315
+ const cwd = projectRoot(command);
316
+ const previewOptions = { cwd };
317
+ if (options.path.length > 0) {
318
+ previewOptions.paths = options.path;
319
+ }
320
+ addOption(previewOptions, "maxFiles", options.maxFiles);
321
+ addOption(previewOptions, "maxChunksPerFile", options.maxChunks);
322
+ const report = await previewChunks(previewOptions);
323
+ if (options.json) {
324
+ console.log(JSON.stringify(report, null, 2));
325
+ if (report.errors.length > 0 ||
326
+ (report.requestedPaths.length > 0 && report.matchedFiles === 0)) {
327
+ process.exitCode = 1;
328
+ }
329
+ return;
330
+ }
331
+ console.log(`chunkSize=${report.chunkSize}`);
332
+ console.log(`chunkOverlap=${report.chunkOverlap}`);
333
+ console.log(`matchedFiles=${report.matchedFiles}`);
334
+ console.log(`omittedFiles=${report.omittedFiles}`);
335
+ for (const unmatchedPath of report.unmatchedPaths) {
336
+ console.log(pc.yellow(`unmatched: ${unmatchedPath}`));
337
+ }
338
+ for (const file of report.files) {
339
+ console.log(`\n${pc.cyan(file.relativePath)} chunks=${file.chunkStats.count} redactions=${file.redactions} minChars=${file.chunkStats.minChars} p50Chars=${file.chunkStats.p50Chars} p95Chars=${file.chunkStats.p95Chars} maxChars=${file.chunkStats.maxChars} contextualRatio=${file.chunkStats.contextualRatio.toFixed(3)}`);
340
+ for (const chunk of file.chunks) {
341
+ const context = chunk.contextPath ? ` context=${chunk.contextPath}` : "";
342
+ console.log(`\n${pc.dim(chunk.citation)}${context}`);
343
+ console.log(chunk.text.slice(0, SEARCH_TEXT_PREVIEW_LENGTH));
344
+ }
345
+ if (file.omittedChunks > 0) {
346
+ console.log(pc.dim(`omittedChunks=${file.omittedChunks}`));
347
+ }
348
+ }
349
+ for (const error of report.errors) {
350
+ console.error(pc.red(`${error.path}: ${error.message}`));
351
+ }
352
+ if (report.errors.length > 0 ||
353
+ (report.requestedPaths.length > 0 && report.matchedFiles === 0)) {
354
+ process.exitCode = 1;
355
+ }
356
+ });
303
357
  program
304
358
  .command("search")
305
359
  .description("Retrieve the most relevant passages without calling an LLM.")
@@ -308,6 +362,8 @@ program
308
362
  .option("--context-radius <number>", "Include neighboring chunks around each matched passage.", parseNonNegativeInt)
309
363
  .option("--include-path <prefix>", "Search only indexed source paths under this prefix. Repeat for multiple prefixes.", collectOptionValue, [])
310
364
  .option("--exclude-path <prefix>", "Exclude indexed source paths under this prefix. Repeat for multiple prefixes.", collectOptionValue, [])
365
+ .option("--context-path <prefix>", "Search only chunks under this structural context. Repeat for multiple prefixes.", collectOptionValue, [])
366
+ .option("--explain", "Include hybrid score contributions, ranks, and matched terms.")
311
367
  .option("--compact", "Return short snippets instead of full passages.")
312
368
  .option("--json", "Print machine-readable JSON.")
313
369
  .action(async (query, options, command) => {
@@ -332,6 +388,11 @@ program
332
388
  const distance = result.distance === null ? "n/a" : result.distance.toFixed(4);
333
389
  console.log(`\n${pc.cyan(`[${index + 1}] ${result.citation}`)} chunk=${result.chunkIndex} distance=${distance}`);
334
390
  console.log("snippet" in result ? result.snippet : result.text.slice(0, SEARCH_TEXT_PREVIEW_LENGTH));
391
+ if (result.score) {
392
+ const vectorRank = result.score.vectorRank ?? "n/a";
393
+ const lexicalRank = result.score.lexicalRank ?? "n/a";
394
+ console.log(pc.dim(`score=${result.score.combinedScore.toFixed(6)} fusion=${result.score.fusion} vector=${result.score.vectorContribution.toFixed(6)} lexical=${result.score.lexicalContribution.toFixed(6)} vectorRank=${vectorRank} lexicalRank=${lexicalRank} matchedTerms=${result.score.matchedTerms.join(",") || "n/a"}`));
395
+ }
335
396
  }
336
397
  });
337
398
  program
@@ -342,6 +403,8 @@ program
342
403
  .option("--context-radius <number>", "Include neighboring chunks around each matched passage.", parseNonNegativeInt)
343
404
  .option("--include-path <prefix>", "Use only indexed source paths under this prefix. Repeat for multiple prefixes.", collectOptionValue, [])
344
405
  .option("--exclude-path <prefix>", "Exclude indexed source paths under this prefix. Repeat for multiple prefixes.", collectOptionValue, [])
406
+ .option("--context-path <prefix>", "Use only chunks under this structural context. Repeat for multiple prefixes.", collectOptionValue, [])
407
+ .option("--explain", "Include hybrid score contributions, ranks, and matched terms.")
345
408
  .option("--json", "Print machine-readable JSON.")
346
409
  .action(async (query, options, command) => {
347
410
  const cwd = projectRoot(command);
@@ -360,7 +423,8 @@ program
360
423
  if (result.sources.length > 0) {
361
424
  console.log(pc.dim("Sources:"));
362
425
  for (const [index, source] of result.sources.entries()) {
363
- console.log(` [${index + 1}] ${source.citation} chunk=${source.chunkIndex}`);
426
+ const score = source.score ? ` score=${source.score.combinedScore.toFixed(6)}` : "";
427
+ console.log(` [${index + 1}] ${source.citation} chunk=${source.chunkIndex}${score}`);
364
428
  }
365
429
  }
366
430
  });
@@ -372,6 +436,7 @@ program
372
436
  .option("--no-code", "Skip the lightweight repository code search.")
373
437
  .option("--include-path <prefix>", "Use only indexed source paths under this prefix. Repeat for multiple prefixes.", collectOptionValue, [])
374
438
  .option("--exclude-path <prefix>", "Exclude indexed source paths under this prefix. Repeat for multiple prefixes.", collectOptionValue, [])
439
+ .option("--context-path <prefix>", "Use only chunks under this structural context. Repeat for multiple prefixes.", collectOptionValue, [])
375
440
  .option("--compact", "Return snippets instead of full retrieved passages.")
376
441
  .option("--json", "Print machine-readable JSON.")
377
442
  .action(async (query, options, command) => {
@@ -493,6 +558,13 @@ program
493
558
  console.log(`sensitiveFiles=${countSkippedByReason(report.skippedFiles, "sensitive-name")}`);
494
559
  console.log(`indexedFiles=${report.indexedFiles.length}`);
495
560
  console.log(`totalChunks=${report.totalChunks}`);
561
+ console.log(`chunkStats.minChars=${report.chunkStats.minChars}`);
562
+ console.log(`chunkStats.averageChars=${report.chunkStats.averageChars.toFixed(1)}`);
563
+ console.log(`chunkStats.p50Chars=${report.chunkStats.p50Chars}`);
564
+ console.log(`chunkStats.p95Chars=${report.chunkStats.p95Chars}`);
565
+ console.log(`chunkStats.maxChars=${report.chunkStats.maxChars}`);
566
+ console.log(`chunkStats.contextualChunks=${report.chunkStats.contextualChunks}`);
567
+ console.log(`chunkStats.contextualRatio=${report.chunkStats.contextualRatio.toFixed(3)}`);
496
568
  console.log(`emptyTextFiles=${report.emptyTextFiles.length}`);
497
569
  console.log(`missingFromIndex=${report.missingFromIndex.length}`);
498
570
  console.log(`staleInIndex=${report.staleInIndex.length}`);
@@ -578,6 +650,25 @@ program
578
650
  console.log(`note: ${note}`);
579
651
  }
580
652
  });
653
+ program
654
+ .command("bases")
655
+ .description("List Ragmir knowledge bases in the active monorepo and mark the selected base.")
656
+ .option("--json", "Print machine-readable JSON.")
657
+ .action(async (options, command) => {
658
+ const inventory = await discoverKnowledgeBases(projectRoot(command));
659
+ if (options.json) {
660
+ console.log(JSON.stringify(inventory, null, 2));
661
+ return;
662
+ }
663
+ console.log(`workspaceRoot=${inventory.workspaceRoot}`);
664
+ console.log(`activeBase=${inventory.activeId ?? "none"}`);
665
+ console.log(`bases=${inventory.bases.length}`);
666
+ for (const base of inventory.bases) {
667
+ const marker = base.active ? "*" : "-";
668
+ const format = base.legacy ? "legacy" : "ragmir";
669
+ console.log(`${marker} ${base.id} format=${format} root=${base.projectRoot}`);
670
+ }
671
+ });
581
672
  program
582
673
  .command("status")
583
674
  .description("Show active configuration and index row count.")
@@ -585,8 +676,10 @@ program
585
676
  .action(async (options, command) => {
586
677
  const cwd = projectRoot(command);
587
678
  const config = await loadConfig(cwd);
679
+ const identity = knowledgeBaseIdentity(config.projectRoot);
588
680
  const rows = await countRows(config);
589
681
  const status = {
682
+ knowledgeBaseId: identity?.id ?? null,
590
683
  projectRoot: config.projectRoot,
591
684
  rawDir: config.rawDir,
592
685
  storageDir: config.storageDir,
@@ -619,6 +712,7 @@ program
619
712
  console.log(JSON.stringify(status, null, 2));
620
713
  return;
621
714
  }
715
+ console.log(`knowledgeBaseId=${identity?.id ?? "none"}`);
622
716
  console.log(`projectRoot=${config.projectRoot}`);
623
717
  console.log(`rawDir=${config.rawDir}`);
624
718
  console.log(`storageDir=${config.storageDir}`);
@@ -840,7 +934,7 @@ program
840
934
  .description("Copy the bundled agent skill and MCP config snippet into the current repository.")
841
935
  .option("--target-dir <path>", "Directory where the skill folder should be copied.", DEFAULT_SKILL_TARGET_DIR)
842
936
  .option("--agents <list>", `Agent MCP helpers to generate: all, ${SUPPORTED_AGENT_TARGETS.join(", ")}.`, "all")
843
- .option("--mcp-name <name>", "MCP server name used in generated config.", "ragmir")
937
+ .option("--mcp-name <name>", "MCP server name. Nested monorepo bases get a unique name by default.")
844
938
  .option("--mcp-command <command>", "Custom MCP stdio command for generated helper files.")
845
939
  .option("--mcp-arg <arg>", "Argument for --mcp-command. Repeat for multiple arguments.", collectOptionValue, [])
846
940
  .action(async (options, command) => {
@@ -849,8 +943,8 @@ program
849
943
  cwd,
850
944
  targetDir: options.targetDir,
851
945
  agents: parseAgentTargets(options.agents),
852
- mcpServerName: options.mcpName,
853
946
  };
947
+ addOption(installOptions, "mcpServerName", options.mcpName);
854
948
  addOption(installOptions, "mcpCommand", options.mcpCommand);
855
949
  if (options.mcpArg.length > 0) {
856
950
  installOptions.mcpArgs = options.mcpArg;
@@ -881,13 +975,20 @@ program
881
975
  .option("--agents <list>", `Comma-separated agents: all, ${SUPPORTED_AGENT_TARGETS.join(", ")}.`, "all")
882
976
  .option("--scope <scope>", "Install scope: project or user.", "project")
883
977
  .option("--mode <mode>", "Expose skills as links or physical copies: link or copy.", "link")
978
+ .option("--force", "Replace same-name native skills after reviewing that they can be overwritten.")
884
979
  .option("--json", "Print machine-readable JSON.")
885
980
  .action(async (options, command) => {
886
981
  const cwd = projectRoot(command);
887
982
  const scope = parseAgentInstallScope(options.scope);
888
983
  const mode = parseAgentInstallMode(options.mode);
889
984
  const agents = parseAgentTargets(options.agents);
890
- const result = await installAgentSkills({ cwd, agents, scope, mode });
985
+ const result = await installAgentSkills({
986
+ cwd,
987
+ agents,
988
+ scope,
989
+ mode,
990
+ force: options.force ?? false,
991
+ });
891
992
  if (options.json) {
892
993
  console.log(JSON.stringify(result, null, 2));
893
994
  return;
@@ -954,6 +1055,7 @@ function withSearchOptions(cwd, options) {
954
1055
  addOption(result, "topK", options.topK);
955
1056
  addOption(result, "contextRadius", options.contextRadius);
956
1057
  addPathFilters(result, options);
1058
+ addOption(result, "explain", options.explain);
957
1059
  return result;
958
1060
  }
959
1061
  function addPathFilters(target, options) {
@@ -963,6 +1065,9 @@ function addPathFilters(target, options) {
963
1065
  if (options.excludePath && options.excludePath.length > 0) {
964
1066
  target.excludePaths = options.excludePath;
965
1067
  }
1068
+ if (options.contextPath && options.contextPath.length > 0) {
1069
+ target.contextPaths = options.contextPath;
1070
+ }
966
1071
  }
967
1072
  async function loadChat() {
968
1073
  const module = await import(CHAT_PACKAGE_NAME);
@@ -1043,6 +1148,15 @@ function printDoctor(report) {
1043
1148
  console.log(`packageManager=${report.packageManager}`);
1044
1149
  console.log(`runCommand=${report.runCommand}`);
1045
1150
  console.log(`agentKitInstalled=${report.agentKitInstalled}`);
1151
+ console.log(`agentIntegration.ready=${report.agentIntegration.ready}`);
1152
+ console.log(`agentIntegration.runnerReady=${report.agentIntegration.runnerReady}`);
1153
+ console.log(`agentIntegration.runnerMode=${report.agentIntegration.runnerMode ?? "none"}`);
1154
+ console.log(`agentIntegration.projectAgents=${report.agentIntegration.projectAgents.join(",")}`);
1155
+ console.log(`agentIntegration.userAgents=${report.agentIntegration.userAgents.join(",")}`);
1156
+ console.log(`agentIntegration.nativeAgents=${report.agentIntegration.nativeAgents.join(",")}`);
1157
+ for (const warning of report.agentIntegration.warnings) {
1158
+ console.log(pc.yellow(`agentIntegration.warning: ${warning}`));
1159
+ }
1046
1160
  console.log(`embeddingProvider=${report.embeddingProvider}`);
1047
1161
  console.log(`transformersAllowRemoteModels=${report.transformersAllowRemoteModels}`);
1048
1162
  console.log(`redactionEnabled=${report.redactionEnabled}`);
@@ -1172,6 +1286,9 @@ function printSetup(result, title) {
1172
1286
  console.log(` - ${helper.label} MCP helper: ${helper.path}`);
1173
1287
  }
1174
1288
  console.log(` - agent setup guide: ${result.agentKit.agentSetupPath}`);
1289
+ for (const installation of result.agentInstallations) {
1290
+ console.log(` - ${installation.label} skills: ${installation.targetDir} (${installation.mode})`);
1291
+ }
1175
1292
  console.log("");
1176
1293
  if (result.semantic) {
1177
1294
  console.log(pc.cyan("Semantic retrieval:"));