@codragraph/cli 2.1.5 → 2.2.0-rc.6

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 (113) hide show
  1. package/README.md +18 -13
  2. package/dist/cli/analyze.d.ts +9 -4
  3. package/dist/cli/analyze.js +37 -13
  4. package/dist/cli/graphpack.d.ts +48 -0
  5. package/dist/cli/graphpack.js +217 -0
  6. package/dist/cli/index.js +81 -3
  7. package/dist/cli/status.d.ts +1 -1
  8. package/dist/cli/status.js +8 -0
  9. package/dist/cli/tool.d.ts +11 -2
  10. package/dist/cli/tool.js +138 -8
  11. package/dist/core/adaptive-profile.d.ts +52 -0
  12. package/dist/core/adaptive-profile.js +180 -0
  13. package/dist/core/cgdb/cgdb-adapter.d.ts +34 -5
  14. package/dist/core/cgdb/cgdb-adapter.js +418 -5
  15. package/dist/core/cgdb/pool-adapter.js +1 -1
  16. package/dist/core/graphpack/index.d.ts +14 -0
  17. package/dist/core/graphpack/index.js +474 -0
  18. package/dist/core/graphpack/types.d.ts +129 -0
  19. package/dist/core/graphpack/types.js +4 -0
  20. package/dist/core/ingestion/pipeline-phases/parse-impl.js +3 -1
  21. package/dist/core/ingestion/pipeline-phases/structure.js +19 -3
  22. package/dist/core/ingestion/pipeline.d.ts +10 -0
  23. package/dist/core/run-analyze.d.ts +27 -2
  24. package/dist/core/run-analyze.js +598 -27
  25. package/dist/core/search/bm25-index.d.ts +19 -0
  26. package/dist/core/search/bm25-index.js +68 -29
  27. package/dist/core/semantic/relationships.d.ts +36 -0
  28. package/dist/core/semantic/relationships.js +261 -0
  29. package/dist/mcp/local/local-backend.js +48 -3
  30. package/dist/mcp/resources.js +125 -0
  31. package/dist/mcp/tools.js +105 -0
  32. package/dist/server/api.js +112 -0
  33. package/dist/storage/repo-manager.d.ts +29 -0
  34. package/dist/web/assets/agent-CQNZQ-hg.js +1139 -0
  35. package/dist/web/assets/architectureDiagram-UL44E2DR-B5_goS_i.js +36 -0
  36. package/dist/web/assets/blockDiagram-7IZFK4PR-D7ZAlDyv.js +132 -0
  37. package/dist/web/assets/{c4Diagram-DFAF54RM-C4Hl3J2U.js → c4Diagram-Y2BXMSZH-Djcgm_54.js} +1 -1
  38. package/dist/web/assets/{chunk-7RZVMHOQ-BitYcNVR.js → chunk-3SSMPTDK-Cv2Zy2FO.js} +1 -1
  39. package/dist/web/assets/{chunk-TBF5ZNIQ-DL5stGM1.js → chunk-6764PJDD-Cppb-jH-.js} +1 -1
  40. package/dist/web/assets/{chunk-KSICW3F5-BYzvDLNI.js → chunk-AZZRMDJM-BHlLC7p3.js} +1 -1
  41. package/dist/web/assets/{chunk-AEOMTBSW-BgTIXPsY.js → chunk-JQRUD6KW-3F8Zg-1N.js} +1 -1
  42. package/dist/web/assets/chunk-KRXBNO2N-C0mbN9a7.js +1 -0
  43. package/dist/web/assets/chunk-LCXTWHL2-BoiuJpIF.js +231 -0
  44. package/dist/web/assets/{chunk-O5ABG6QK-dHwHzA6n.js → chunk-LII3EMHJ-Dqq0Qguw.js} +1 -1
  45. package/dist/web/assets/chunk-RG4AUYOV-Bl5F_gDs.js +206 -0
  46. package/dist/web/assets/{chunk-TU3PZOEN-RLyvLcv-.js → chunk-T5OCTHI4-B2tIcggA.js} +1 -1
  47. package/dist/web/assets/chunk-W44A43WB-BHe37iN7.js +13 -0
  48. package/dist/web/assets/{chunk-RWUO3TPN-BgRTY0_k.js → chunk-ZXARS5L4-wcrIaQvY.js} +1 -1
  49. package/dist/web/assets/classDiagram-KGZ6W3CR-IbI6v_24.js +1 -0
  50. package/dist/web/assets/classDiagram-v2-72OJOZXJ-IbI6v_24.js +1 -0
  51. package/dist/web/assets/{cose-bilkent-PNC4W37J-DVhePRYg.js → cose-bilkent-UX7MHV2Q-BWr7v0Wr.js} +1 -1
  52. package/dist/web/assets/dagre-ND4H6XIP-De5LIh1B.js +4 -0
  53. package/dist/web/assets/diagram-3NCE3AQN-Dd22FSHy.js +43 -0
  54. package/dist/web/assets/diagram-GF46GFSD-Cev3THY8.js +24 -0
  55. package/dist/web/assets/diagram-HNR7UZ2L-D8Z8RQGs.js +3 -0
  56. package/dist/web/assets/diagram-QXG6HAR7-B8VOJOiE.js +24 -0
  57. package/dist/web/assets/diagram-WEQXMOUZ-va1bLoMD.js +10 -0
  58. package/dist/web/assets/{erDiagram-GCSMX5X6-C3dhDFA8.js → erDiagram-L5TCEMPS-B3_9uAoP.js} +5 -5
  59. package/dist/web/assets/{flowDiagram-OTCZ4VVT-CWSFWmhr.js → flowDiagram-H6V6AXG4-98m6maI1.js} +9 -9
  60. package/dist/web/assets/ganttDiagram-JCBTUEKG-vE2nzETb.js +292 -0
  61. package/dist/web/assets/gitGraphDiagram-S2ZK5IYY-DKc8uUg_.js +106 -0
  62. package/dist/web/assets/index-BAhe1HSk.css +1 -0
  63. package/dist/web/assets/index-VTKdaklA.js +1415 -0
  64. package/dist/web/assets/infoDiagram-3YFTVSEB-DYP-Srzx.js +2 -0
  65. package/dist/web/assets/{ishikawaDiagram-YMYX4NHK-DUoJvNP2.js → ishikawaDiagram-BNXS4ZKH-QZnkpmmb.js} +3 -3
  66. package/dist/web/assets/{journeyDiagram-SO5T7YLQ-RMFPNNqz.js → journeyDiagram-M6C3CM3L-B5ojIuqu.js} +1 -1
  67. package/dist/web/assets/{kanban-definition-LJHFXRCJ-BzpDs1K9.js → kanban-definition-75IXJCU3-BJA8liRR.js} +4 -4
  68. package/dist/web/assets/{katex-GD7MH7QM-DBQvrix-.js → katex-K3KEBU37-DUqZiCRL.js} +1 -1
  69. package/dist/web/assets/mindmap-definition-2TDM6QVE-BQj5yylD.js +96 -0
  70. package/dist/web/assets/pieDiagram-CU6KROY3-4eSrPiQz.js +30 -0
  71. package/dist/web/assets/quadrantDiagram-VICAPDV7-PzxN8j55.js +7 -0
  72. package/dist/web/assets/{requirementDiagram-M5DCFWZL-DLHOVTSv.js → requirementDiagram-JXO7QTGE-CtplTc5y.js} +2 -2
  73. package/dist/web/assets/sankeyDiagram-URQDO5SZ-CoSgvkxv.js +40 -0
  74. package/dist/web/assets/sequenceDiagram-VS2MUI6T-D7ygyXvJ.js +162 -0
  75. package/dist/web/assets/stateDiagram-7D4R322I-v01gvwji.js +1 -0
  76. package/dist/web/assets/stateDiagram-v2-36443NZ5-DFD2b8_x.js +1 -0
  77. package/dist/web/assets/{timeline-definition-5SPVSISX-TRSDRgPw.js → timeline-definition-O6YCAMPW-CTI3M65J.js} +4 -4
  78. package/dist/web/assets/{vennDiagram-IE5QUKF5-DNy7HRBM.js → vennDiagram-MWXL3ELB-RnB0XMP7.js} +6 -6
  79. package/dist/web/assets/wardley-L42UT6IY-5TKZOOLJ-C-ZcgEBb.js +173 -0
  80. package/dist/web/assets/wardleyDiagram-CUQ6CDDI-EwRi4kwo.js +78 -0
  81. package/dist/web/assets/{xychartDiagram-ZHJ5623Y-Dr9r7a35.js → xychartDiagram-N2JHSOCM-DA38II6y.js} +4 -4
  82. package/dist/web/index.html +2 -2
  83. package/package.json +2 -2
  84. package/vendor/node_modules/node-addon-api/node_addon_api_except.stamp +0 -0
  85. package/dist/web/assets/agent-D5lb0zXz.js +0 -1089
  86. package/dist/web/assets/architectureDiagram-EMZXCZ2Q-CZtc99v_.js +0 -36
  87. package/dist/web/assets/blockDiagram-IGV67L2C-BtoUp-6Y.js +0 -132
  88. package/dist/web/assets/chunk-3GS5O3IE-DkUjU0WD.js +0 -231
  89. package/dist/web/assets/chunk-3YCYZ6SJ-CQkVgT_z.js +0 -1
  90. package/dist/web/assets/chunk-H3VCZNTA-Cx5XV_aC.js +0 -13
  91. package/dist/web/assets/chunk-HN6EAY2L-BBnyTNdB.js +0 -1
  92. package/dist/web/assets/chunk-PK6DOVAG-CvsEnugt.js +0 -206
  93. package/dist/web/assets/classDiagram-PPOCWD7C-DTr8QIOf.js +0 -1
  94. package/dist/web/assets/classDiagram-v2-23LJLIIU-DTr8QIOf.js +0 -1
  95. package/dist/web/assets/dagre-E77IOHMT-Dzx0A6ZU.js +0 -4
  96. package/dist/web/assets/diagram-H7BISOXX-CC9pRew1.js +0 -43
  97. package/dist/web/assets/diagram-JC5VWROH-Bau_i9tf.js +0 -24
  98. package/dist/web/assets/diagram-LXUTUG65-D9_FM2Gt.js +0 -10
  99. package/dist/web/assets/diagram-WEHSV5V5-BMlayouL.js +0 -24
  100. package/dist/web/assets/ganttDiagram-MUNLMDZQ-D3a67Yol.js +0 -292
  101. package/dist/web/assets/gitGraphDiagram-3HKGZ4G3-7jmry-vM.js +0 -106
  102. package/dist/web/assets/index-BgeqpYgd.js +0 -1415
  103. package/dist/web/assets/index-CT0GtFLZ.css +0 -1
  104. package/dist/web/assets/infoDiagram-MN7RKWGX-G7lhP0Ib.js +0 -2
  105. package/dist/web/assets/mindmap-definition-2EUWGEK5-Bk0O4roa.js +0 -96
  106. package/dist/web/assets/pieDiagram-3IATQBI2-DKU7kpgS.js +0 -30
  107. package/dist/web/assets/quadrantDiagram-E256RVCF-BY0TGWCS.js +0 -7
  108. package/dist/web/assets/sankeyDiagram-L3NBLAOT-DVMj5rX2.js +0 -10
  109. package/dist/web/assets/sequenceDiagram-ZOUHS735-CJC73bV-.js +0 -157
  110. package/dist/web/assets/stateDiagram-MLPALWAM-BCFyESls.js +0 -1
  111. package/dist/web/assets/stateDiagram-v2-B5LQ5ZB2-DahzzIca.js +0 -1
  112. package/dist/web/assets/wardley-RL74JXVD-BCRCBASE-B-eZEzf9.js +0 -161
  113. package/dist/web/assets/wardleyDiagram-XU3VSMPF-BP-r1xzR.js +0 -20
@@ -96,6 +96,24 @@ export function getResourceTemplates() {
96
96
  description: "Phase 4: the indexed repo's current branch + head commit, plus the snapshot it points at. Pairs with graphstore_log/diff/blame.",
97
97
  mimeType: 'text/yaml',
98
98
  },
99
+ {
100
+ uriTemplate: 'codragraph://repo/{name}/graphpack/status',
101
+ name: 'Team Graphpack Status',
102
+ description: 'Team graphpack provenance: canonical main graph, PR overlay, local graph, chunk health, and compatibility.',
103
+ mimeType: 'text/yaml',
104
+ },
105
+ {
106
+ uriTemplate: 'codragraph://repo/{name}/graphpack/lock',
107
+ name: 'Team Graphpack Lock',
108
+ description: 'Committed .codragraph/index.lock.json content for shared team graph bootstrapping.',
109
+ mimeType: 'application/json',
110
+ },
111
+ {
112
+ uriTemplate: 'codragraph://repo/{name}/semantic-relationships',
113
+ name: 'Semantic Relationships',
114
+ description: 'Developer-intent relationship families with confidence, evidence, provenance, and extractor version.',
115
+ mimeType: 'text/yaml',
116
+ },
99
117
  {
100
118
  uriTemplate: 'codragraph://repo/{name}/recipes',
101
119
  name: 'Harness Recipes',
@@ -267,6 +285,12 @@ export async function readResource(uri, backend) {
267
285
  return getGraphstoreBranchesResource(backend, repoName);
268
286
  case 'graphstore/head':
269
287
  return getGraphstoreHeadResource(backend, repoName);
288
+ case 'graphpack/status':
289
+ return getGraphpackStatusResource(backend, repoName);
290
+ case 'graphpack/lock':
291
+ return getGraphpackLockResource(backend, repoName);
292
+ case 'semantic-relationships':
293
+ return getSemanticRelationshipsResource(backend, repoName);
270
294
  case 'recipes':
271
295
  return getRecipesResource(backend, repoName);
272
296
  default:
@@ -340,6 +364,8 @@ async function getContextResource(backend, repoName) {
340
364
  lines.push(' - impact: Blast radius analysis (what breaks if you change a symbol)');
341
365
  lines.push(' - detect_changes: Git-diff impact analysis (what do your changes affect)');
342
366
  lines.push(' - rename: Multi-file coordinated rename with confidence tags');
367
+ lines.push(' - graphpack_status: Team graph provenance and artifact health');
368
+ lines.push(' - semantic_relationships: Developer-intent semantic edge families');
343
369
  lines.push(' - cypher: Raw graph queries');
344
370
  lines.push(' - list_repos: Discover all indexed repositories');
345
371
  lines.push('');
@@ -350,6 +376,8 @@ async function getContextResource(backend, repoName) {
350
376
  lines.push(` - codragraph://repo/${context.projectName}/clusters: All functional areas`);
351
377
  lines.push(` - codragraph://repo/${context.projectName}/feature-clusters: Human-facing feature areas`);
352
378
  lines.push(` - codragraph://repo/${context.projectName}/processes: All execution flows`);
379
+ lines.push(` - codragraph://repo/${context.projectName}/graphpack/status: Team graphpack status`);
380
+ lines.push(` - codragraph://repo/${context.projectName}/semantic-relationships: Developer-intent relationships`);
353
381
  lines.push(` - codragraph://repo/${context.projectName}/cluster/{name}: Module details`);
354
382
  lines.push(` - codragraph://repo/${context.projectName}/feature/{name}: Feature context pack`);
355
383
  lines.push(` - codragraph://repo/${context.projectName}/process/{name}: Process trace`);
@@ -759,6 +787,100 @@ async function getGraphstoreBranchesResource(backend, repoName) {
759
787
  }
760
788
  return lines.join('\n');
761
789
  }
790
+ async function getGraphpackStatusResource(backend, repoName) {
791
+ const repo = await backend.resolveRepo(repoName);
792
+ const { getGraphpackStatus } = await import('../core/graphpack/index.js');
793
+ try {
794
+ const status = await getGraphpackStatus({
795
+ repoPath: repo.repoPath,
796
+ storagePath: repo.storagePath,
797
+ });
798
+ const lines = [
799
+ `repo: "${repo.name}"`,
800
+ `source: "${status.source}"`,
801
+ `lockPresent: ${status.lockPresent}`,
802
+ `compatible: ${status.compatibility.ok}`,
803
+ `localGraphstore: ${status.local.graphstorePresent}`,
804
+ `verifiedChunks: ${status.chunks.verified}`,
805
+ `expectedChunks: ${status.chunks.expected}`,
806
+ ];
807
+ if (status.lock?.graphpack.id)
808
+ lines.push(`graphpack: "${status.lock.graphpack.id}"`);
809
+ if (status.lock?.graphpack.graphstoreSnapshot) {
810
+ lines.push(`snapshot: "${status.lock.graphpack.graphstoreSnapshot}"`);
811
+ }
812
+ if (status.local.headCommit)
813
+ lines.push(`localHead: "${status.local.headCommit}"`);
814
+ if (status.compatibility.reasons.length > 0) {
815
+ lines.push('reasons:');
816
+ for (const reason of status.compatibility.reasons)
817
+ lines.push(` - ${JSON.stringify(reason)}`);
818
+ }
819
+ if (status.chunks.missing.length > 0) {
820
+ lines.push('missingChunks:');
821
+ for (const chunk of status.chunks.missing)
822
+ lines.push(` - "${chunk}"`);
823
+ }
824
+ if (status.chunks.mismatched.length > 0) {
825
+ lines.push('mismatchedChunks:');
826
+ for (const chunk of status.chunks.mismatched)
827
+ lines.push(` - "${chunk}"`);
828
+ }
829
+ return lines.join('\n');
830
+ }
831
+ catch (err) {
832
+ return `error: ${err instanceof Error ? err.message : String(err)}`;
833
+ }
834
+ }
835
+ async function getGraphpackLockResource(backend, repoName) {
836
+ const repo = await backend.resolveRepo(repoName);
837
+ const { defaultLockPath, readGraphpackLock } = await import('../core/graphpack/index.js');
838
+ const lock = await readGraphpackLock(defaultLockPath(repo.repoPath));
839
+ if (!lock) {
840
+ return JSON.stringify({ error: 'No .codragraph/index.lock.json found', repo: repo.name });
841
+ }
842
+ return JSON.stringify(lock, null, 2);
843
+ }
844
+ async function getSemanticRelationshipsResource(backend, repoName) {
845
+ const repo = await backend.resolveRepo(repoName);
846
+ const { analyzeSemanticRelationships } = await import('../core/semantic/relationships.js');
847
+ try {
848
+ const report = await analyzeSemanticRelationships({
849
+ storagePath: repo.storagePath,
850
+ limit: 200,
851
+ write: false,
852
+ });
853
+ const lines = [
854
+ `repo: "${repo.name}"`,
855
+ `snapshot: "${report.snapshotId ?? 'unknown'}"`,
856
+ `extractor: "${report.extractorVersion}"`,
857
+ `relationships: ${report.relationships.length}`,
858
+ 'summary:',
859
+ ];
860
+ for (const [family, count] of Object.entries(report.summary)) {
861
+ lines.push(` ${family}: ${count}`);
862
+ }
863
+ lines.push('edges:');
864
+ for (const rel of report.relationships.slice(0, 50)) {
865
+ lines.push(` - family: "${rel.family}"`);
866
+ lines.push(` source: ${JSON.stringify(rel.sourceName ?? rel.sourceId)}`);
867
+ lines.push(` target: ${JSON.stringify(rel.targetName ?? rel.targetId)}`);
868
+ lines.push(` confidence: ${rel.confidence}`);
869
+ lines.push(` provenance: "${rel.provenance}"`);
870
+ lines.push(` extractor: "${rel.extractorVersion}"`);
871
+ if (rel.evidence.filePath)
872
+ lines.push(` file: "${rel.evidence.filePath}"`);
873
+ if (rel.evidence.startLine !== undefined)
874
+ lines.push(` startLine: ${rel.evidence.startLine}`);
875
+ lines.push(` reason: ${JSON.stringify(rel.evidence.reason)}`);
876
+ lines.push(` rawEdgeType: "${rel.evidence.rawEdgeType}"`);
877
+ }
878
+ return lines.join('\n');
879
+ }
880
+ catch (err) {
881
+ return `error: ${err instanceof Error ? err.message : String(err)}`;
882
+ }
883
+ }
762
884
  async function getRecipesResource(backend, repoName, taskFamily) {
763
885
  const repo = await backend.resolveRepo(repoName);
764
886
  let result;
@@ -794,6 +916,9 @@ async function getRecipesResource(backend, repoName, taskFamily) {
794
916
  lines.push(` - id: "${r.id}"`);
795
917
  lines.push(` taskFamily: "${r.taskFamily}"`);
796
918
  lines.push(` snapshotId: "${r.snapshotId}"`);
919
+ if (r.requiredSubgraphSignature) {
920
+ lines.push(` requiredSubgraphSignature: "${r.requiredSubgraphSignature}"`);
921
+ }
797
922
  lines.push(` accuracy: ${r.accuracy}`);
798
923
  lines.push(` tokens: ${r.tokens}`);
799
924
  lines.push(` latencyMs: ${r.latencyMs}`);
package/dist/mcp/tools.js CHANGED
@@ -642,6 +642,99 @@ WHEN TO USE: After changing group.yaml or re-indexing member repos.`,
642
642
  required: ['name'],
643
643
  },
644
644
  },
645
+ {
646
+ name: 'graphpack_status',
647
+ description: `Show the team graphpack state for a repo.
648
+
649
+ WHEN TO USE: before answering team-shared-context questions. Reports whether the answer should be considered canonical main graph, PR overlay, local graph, or missing/fallback.`,
650
+ inputSchema: {
651
+ type: 'object',
652
+ properties: {
653
+ repo: {
654
+ type: 'string',
655
+ description: 'Repository name or path. Omit if only one repo is indexed.',
656
+ },
657
+ strict: {
658
+ type: 'boolean',
659
+ description: 'Recompute graphstore CAS digest instead of trusting local presence.',
660
+ default: false,
661
+ },
662
+ },
663
+ required: [],
664
+ },
665
+ },
666
+ {
667
+ name: 'graphpack_publish',
668
+ description: `Create a thin .codragraph/index.lock.json plus graphpack manifest for GitHub-native artifact publishing.
669
+
670
+ WHEN TO USE: CI on main or PR branches after analyze. Commits the lock only; heavy graphstore chunks remain artifact storage.`,
671
+ inputSchema: {
672
+ type: 'object',
673
+ properties: {
674
+ repo: {
675
+ type: 'string',
676
+ description: 'Repository name or path. Omit if only one repo is indexed.',
677
+ },
678
+ target: {
679
+ type: 'string',
680
+ enum: ['main', 'pr'],
681
+ description: 'Graphpack target.',
682
+ default: 'main',
683
+ },
684
+ artifact_dir: { type: 'string', description: 'Local artifact staging directory.' },
685
+ artifact_url: { type: 'string', description: 'Remote artifact URL to record in the lock.' },
686
+ base_snapshot_id: { type: 'string', description: 'PR overlay base snapshot id.' },
687
+ head_snapshot_id: { type: 'string', description: 'PR overlay head snapshot id.' },
688
+ pull_request: { type: 'string', description: 'Pull request number or URL.' },
689
+ },
690
+ required: [],
691
+ },
692
+ },
693
+ {
694
+ name: 'graphpack_pull',
695
+ description: `Validate/pull a graphpack from a lock and report whether local materialization can proceed.
696
+
697
+ WHEN TO USE: developer bootstrap after clone/checkout. If unavailable or incompatible, the result includes a safe local analyze fallback reason.`,
698
+ inputSchema: {
699
+ type: 'object',
700
+ properties: {
701
+ repo: {
702
+ type: 'string',
703
+ description: 'Repository name or path. Omit if only one repo is indexed.',
704
+ },
705
+ artifact_dir: { type: 'string', description: 'Local graphpack artifact directory.' },
706
+ },
707
+ required: [],
708
+ },
709
+ },
710
+ {
711
+ name: 'semantic_relationships',
712
+ description: `Return developer-intent semantic relationships above raw graph edges.
713
+
714
+ Families include COMPOSES, ADAPTS, DELEGATES_TO, WRAPS, CONFIGURES, FACTORY_CREATES, ORCHESTRATES, PROXIES_TO, and MAPS_TO. Every edge includes evidence, confidence, extractor version, and provenance.`,
715
+ inputSchema: {
716
+ type: 'object',
717
+ properties: {
718
+ repo: {
719
+ type: 'string',
720
+ description: 'Repository name or path. Omit if only one repo is indexed.',
721
+ },
722
+ limit: {
723
+ type: 'number',
724
+ description: 'Maximum relationships to return.',
725
+ default: 1000,
726
+ minimum: 1,
727
+ maximum: 10000,
728
+ },
729
+ llm: {
730
+ type: 'boolean',
731
+ description: 'Allow optional LLM-inferred edges when provider support is configured.',
732
+ default: false,
733
+ },
734
+ },
735
+ required: [],
736
+ },
737
+ },
645
738
  {
646
739
  name: 'harness_swarm_run',
647
740
  description: `Phase 3 swarm: Explorer + Exploiter (subprocess) + Critic (in-process), with hybrid termination.
@@ -727,6 +820,10 @@ Returns the Pareto frontier plus per-role attribution stats (which role contribu
727
820
  type: 'string',
728
821
  description: 'Phase 4 moat: codragraph-graphstore snapshot id (sha256:...). Required to enable recipe caching.',
729
822
  },
823
+ required_subgraph_signature: {
824
+ type: 'string',
825
+ description: 'Phase 4 moat: stable signature of the task-required subgraph. Exact cache reuse keys on (snapshot_id, task_family, required_subgraph_signature).',
826
+ },
730
827
  use_cache: {
731
828
  type: 'boolean',
732
829
  description: 'Phase 4 moat: when true and an exact (snapshot_id, task_family) recipe exists, return it instead of running the swarm.',
@@ -757,6 +854,10 @@ This is the Phase 4 × Phase 3 moat — versioned recipe memory. Recipes auto-in
757
854
  properties: {
758
855
  task_family: { type: 'string', description: 'Filter by task family.' },
759
856
  snapshot_id: { type: 'string', description: 'Filter by snapshot id (sha256:...).' },
857
+ required_subgraph_signature: {
858
+ type: 'string',
859
+ description: 'Filter by required subgraph signature.',
860
+ },
760
861
  recipe_store: {
761
862
  type: 'string',
762
863
  description: 'Recipe store root. Defaults to <cwd>/.codragraph/recipes.',
@@ -780,6 +881,10 @@ WHEN TO USE: before kicking off a swarm. If exact matches exist, you can skip th
780
881
  properties: {
781
882
  task_family: { type: 'string', description: 'Task family identifier.' },
782
883
  snapshot_id: { type: 'string', description: 'Current codragraph-graphstore snapshot id.' },
884
+ required_subgraph_signature: {
885
+ type: 'string',
886
+ description: 'Stable signature of the subgraph this task needs. Exact reuse requires matching snapshot, family, and signature.',
887
+ },
783
888
  recipe_store: {
784
889
  type: 'string',
785
890
  description: 'Recipe store root. Defaults to <cwd>/.codragraph/recipes.',
@@ -758,6 +758,112 @@ export const createServer = async (port, host = '127.0.0.1', options = {}) => {
758
758
  // Lazy-imports the handler from codragraph-harness so the package
759
759
  // stays optional at runtime — same pattern as harness_run /
760
760
  // harness_swarm_run in local-backend.ts.
761
+ app.get('/api/graphpack/status', async (req, res) => {
762
+ try {
763
+ const entry = await resolveRepo(requestedRepo(req));
764
+ if (!entry) {
765
+ res.status(404).json({ error: 'Repository not found' });
766
+ return;
767
+ }
768
+ const { getGraphpackStatus } = await import('../core/graphpack/index.js');
769
+ const result = await getGraphpackStatus({
770
+ repoPath: entry.path,
771
+ storagePath: entry.storagePath,
772
+ strict: req.query.strict === 'true',
773
+ });
774
+ res.json(result);
775
+ }
776
+ catch (err) {
777
+ res.status(500).json({ error: err.message || 'graphpack status failed' });
778
+ }
779
+ });
780
+ app.get('/api/graphpack/lock', async (req, res) => {
781
+ try {
782
+ const entry = await resolveRepo(requestedRepo(req));
783
+ if (!entry) {
784
+ res.status(404).json({ error: 'Repository not found' });
785
+ return;
786
+ }
787
+ const { defaultLockPath, readGraphpackLock } = await import('../core/graphpack/index.js');
788
+ const lock = await readGraphpackLock(defaultLockPath(entry.path));
789
+ if (!lock) {
790
+ res.status(404).json({ error: 'No .codragraph/index.lock.json found' });
791
+ return;
792
+ }
793
+ res.json(lock);
794
+ }
795
+ catch (err) {
796
+ res.status(500).json({ error: err.message || 'graphpack lock failed' });
797
+ }
798
+ });
799
+ app.post('/api/graphpack/publish', async (req, res) => {
800
+ try {
801
+ const entry = await resolveRepo(requestedRepo(req));
802
+ if (!entry) {
803
+ res.status(404).json({ error: 'Repository not found' });
804
+ return;
805
+ }
806
+ const { publishGraphpack } = await import('../core/graphpack/index.js');
807
+ const body = (req.body ?? {});
808
+ const result = await publishGraphpack({
809
+ repoPath: entry.path,
810
+ storagePath: entry.storagePath,
811
+ repoName: typeof body.repo === 'string' ? body.repo : entry.name,
812
+ analyzerVersion: typeof body.analyzerVersion === 'string' ? body.analyzerVersion : 'http',
813
+ target: body.target === 'pr' ? 'pr' : 'main',
814
+ artifactDir: typeof body.artifactDir === 'string' ? body.artifactDir : undefined,
815
+ artifactUrl: typeof body.artifactUrl === 'string' ? body.artifactUrl : undefined,
816
+ baseSnapshotId: typeof body.baseSnapshotId === 'string' ? body.baseSnapshotId : undefined,
817
+ headSnapshotId: typeof body.headSnapshotId === 'string' ? body.headSnapshotId : undefined,
818
+ pullRequest: typeof body.pullRequest === 'string' ? body.pullRequest : undefined,
819
+ });
820
+ res.json(result);
821
+ }
822
+ catch (err) {
823
+ res.status(500).json({ error: err.message || 'graphpack publish failed' });
824
+ }
825
+ });
826
+ app.post('/api/graphpack/pull', async (req, res) => {
827
+ try {
828
+ const entry = await resolveRepo(requestedRepo(req));
829
+ if (!entry) {
830
+ res.status(404).json({ error: 'Repository not found' });
831
+ return;
832
+ }
833
+ const { pullGraphpack } = await import('../core/graphpack/index.js');
834
+ const body = (req.body ?? {});
835
+ const result = await pullGraphpack({
836
+ repoPath: entry.path,
837
+ storagePath: entry.storagePath,
838
+ artifactDir: typeof body.artifactDir === 'string' ? body.artifactDir : undefined,
839
+ });
840
+ res.json(result);
841
+ }
842
+ catch (err) {
843
+ res.status(500).json({ error: err.message || 'graphpack pull failed' });
844
+ }
845
+ });
846
+ app.get('/api/semantic/relationships', async (req, res) => {
847
+ try {
848
+ const entry = await resolveRepo(requestedRepo(req));
849
+ if (!entry) {
850
+ res.status(404).json({ error: 'Repository not found' });
851
+ return;
852
+ }
853
+ const { analyzeSemanticRelationships } = await import('../core/semantic/relationships.js');
854
+ const limit = typeof req.query.limit === 'string' ? Number.parseInt(req.query.limit, 10) : undefined;
855
+ const result = await analyzeSemanticRelationships({
856
+ storagePath: entry.storagePath,
857
+ limit: Number.isFinite(limit) ? limit : 1000,
858
+ llm: req.query.llm === 'true',
859
+ write: false,
860
+ });
861
+ res.json(result);
862
+ }
863
+ catch (err) {
864
+ res.status(500).json({ error: err.message || 'semantic relationships failed' });
865
+ }
866
+ });
761
867
  const importRecipeHandler = async (handlerName) => {
762
868
  const moduleId = '@codragraph/harness/mcp/handler';
763
869
  try {
@@ -785,6 +891,9 @@ export const createServer = async (port, host = '127.0.0.1', options = {}) => {
785
891
  recipe_store: path.join(entry.storagePath, 'recipes'),
786
892
  task_family: typeof req.query.task_family === 'string' ? req.query.task_family : undefined,
787
893
  snapshot_id: typeof req.query.snapshot_id === 'string' ? req.query.snapshot_id : undefined,
894
+ required_subgraph_signature: typeof req.query.required_subgraph_signature === 'string'
895
+ ? req.query.required_subgraph_signature
896
+ : undefined,
788
897
  limit: Number.isFinite(limitParam) ? limitParam : undefined,
789
898
  });
790
899
  res.json(result);
@@ -813,6 +922,9 @@ export const createServer = async (port, host = '127.0.0.1', options = {}) => {
813
922
  recipe_store: path.join(entry.storagePath, 'recipes'),
814
923
  task_family: taskFamily,
815
924
  snapshot_id: snapshotId,
925
+ required_subgraph_signature: typeof req.query.required_subgraph_signature === 'string'
926
+ ? req.query.required_subgraph_signature
927
+ : undefined,
816
928
  limit: typeof req.query.limit === 'string' ? Number.parseInt(req.query.limit, 10) : undefined,
817
929
  });
818
930
  res.json(result);
@@ -83,6 +83,35 @@ export interface RepoMeta {
83
83
  * — they decode at the read boundary.
84
84
  */
85
85
  compress?: 'none' | 'brotli' | 'zstd';
86
+ /**
87
+ * Query-time index capabilities written by analyze after optional search
88
+ * indexes are successfully created. Read-only MCP/CLI query paths use this
89
+ * to avoid expensive probes for indexes that older analyses never wrote.
90
+ */
91
+ searchIndexes?: {
92
+ fts?: boolean;
93
+ };
94
+ /**
95
+ * Runtime policy chosen by `analyze --profile auto|lean|balanced|power`.
96
+ * This is diagnostic metadata only; readers must not require it because
97
+ * older indexes do not have a policy record.
98
+ */
99
+ adaptiveProfile?: {
100
+ requested?: 'auto' | 'lean' | 'balanced' | 'power';
101
+ resolved?: 'lean' | 'balanced' | 'power';
102
+ platform?: NodeJS.Platform;
103
+ arch?: NodeJS.Architecture;
104
+ cpuCount?: number;
105
+ totalMemoryBytes?: number;
106
+ heapLimitBytes?: number;
107
+ compression?: 'none' | 'brotli' | 'zstd';
108
+ embeddingMode?: 'auto' | 'off' | 'on';
109
+ embeddingNodeLimit?: number;
110
+ embeddingDecision?: 'enabled' | 'skipped';
111
+ embeddingReason?: string;
112
+ workerPoolSize?: number;
113
+ workerSubBatchSize?: number;
114
+ };
86
115
  /**
87
116
  * Canonical `origin` remote URL captured at index time. Used to
88
117
  * fingerprint the same logical repo across multiple on-disk clones