homegraph 1.1.2 → 1.1.3
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/LICENSE +21 -21
- package/README.md +243 -243
- package/dist/bin/homegraph.js +371 -9
- package/dist/bin/homegraph.js.map +1 -1
- package/dist/db/migrations.js +18 -18
- package/dist/db/queries.js +140 -140
- package/dist/db/schema.sql +152 -152
- package/dist/directory.js +5 -5
- package/dist/extraction/wasm/tree-sitter-c_sharp.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-pascal.wasm +0 -0
- package/dist/installer/instructions-template.js +9 -9
- package/dist/installer/targets/shared.d.ts +5 -6
- package/dist/installer/targets/shared.d.ts.map +1 -1
- package/dist/installer/targets/shared.js +5 -6
- package/dist/installer/targets/shared.js.map +1 -1
- package/dist/mcp/liveness-watchdog.js +16 -16
- package/dist/mcp/server-instructions.js +69 -69
- package/dist/mcp/tools.d.ts +17 -4
- package/dist/mcp/tools.d.ts.map +1 -1
- package/dist/mcp/tools.js +192 -22
- package/dist/mcp/tools.js.map +1 -1
- package/dist/reasoning/reasoner.js +32 -32
- package/dist/spec/config.d.ts +43 -0
- package/dist/spec/config.d.ts.map +1 -0
- package/dist/spec/config.js +275 -0
- package/dist/spec/config.js.map +1 -0
- package/dist/spec/db/commit-node.d.ts +23 -0
- package/dist/spec/db/commit-node.d.ts.map +1 -0
- package/dist/spec/db/commit-node.js +62 -0
- package/dist/spec/db/commit-node.js.map +1 -0
- package/dist/spec/db/fragment-node.d.ts +23 -0
- package/dist/spec/db/fragment-node.d.ts.map +1 -0
- package/dist/spec/db/fragment-node.js +120 -0
- package/dist/spec/db/fragment-node.js.map +1 -0
- package/dist/spec/db/fts.d.ts +60 -0
- package/dist/spec/db/fts.d.ts.map +1 -0
- package/dist/spec/db/fts.js +285 -0
- package/dist/spec/db/fts.js.map +1 -0
- package/dist/spec/db/index.d.ts +13 -0
- package/dist/spec/db/index.d.ts.map +1 -0
- package/dist/spec/db/index.js +50 -0
- package/dist/spec/db/index.js.map +1 -0
- package/dist/spec/db/relations.d.ts +55 -0
- package/dist/spec/db/relations.d.ts.map +1 -0
- package/dist/spec/db/relations.js +158 -0
- package/dist/spec/db/relations.js.map +1 -0
- package/dist/spec/db/schema.d.ts +30 -0
- package/dist/spec/db/schema.d.ts.map +1 -0
- package/dist/spec/db/schema.js +87 -0
- package/dist/spec/db/schema.js.map +1 -0
- package/dist/spec/db/schema.sql +107 -0
- package/dist/spec/db/spec-node.d.ts +41 -0
- package/dist/spec/db/spec-node.d.ts.map +1 -0
- package/dist/spec/db/spec-node.js +114 -0
- package/dist/spec/db/spec-node.js.map +1 -0
- package/dist/spec/evolve/impact-locator.d.ts +13 -0
- package/dist/spec/evolve/impact-locator.d.ts.map +1 -0
- package/dist/spec/evolve/impact-locator.js +25 -0
- package/dist/spec/evolve/impact-locator.js.map +1 -0
- package/dist/spec/evolve/llm-client.d.ts +50 -0
- package/dist/spec/evolve/llm-client.d.ts.map +1 -0
- package/dist/spec/evolve/llm-client.js +176 -0
- package/dist/spec/evolve/llm-client.js.map +1 -0
- package/dist/spec/evolve/logic-checker.d.ts +12 -0
- package/dist/spec/evolve/logic-checker.d.ts.map +1 -0
- package/dist/spec/evolve/logic-checker.js +48 -0
- package/dist/spec/evolve/logic-checker.js.map +1 -0
- package/dist/spec/evolve/pipeline.d.ts +40 -0
- package/dist/spec/evolve/pipeline.d.ts.map +1 -0
- package/dist/spec/evolve/pipeline.js +314 -0
- package/dist/spec/evolve/pipeline.js.map +1 -0
- package/dist/spec/evolve/spec-rewriter.d.ts +42 -0
- package/dist/spec/evolve/spec-rewriter.d.ts.map +1 -0
- package/dist/spec/evolve/spec-rewriter.js +254 -0
- package/dist/spec/evolve/spec-rewriter.js.map +1 -0
- package/dist/spec/graph/queries.d.ts +58 -0
- package/dist/spec/graph/queries.d.ts.map +1 -0
- package/dist/spec/graph/queries.js +215 -0
- package/dist/spec/graph/queries.js.map +1 -0
- package/dist/spec/mining/diff-parser.d.ts +33 -0
- package/dist/spec/mining/diff-parser.d.ts.map +1 -0
- package/dist/spec/mining/diff-parser.js +166 -0
- package/dist/spec/mining/diff-parser.js.map +1 -0
- package/dist/spec/mining/git-scanner.d.ts +91 -0
- package/dist/spec/mining/git-scanner.d.ts.map +1 -0
- package/dist/spec/mining/git-scanner.js +266 -0
- package/dist/spec/mining/git-scanner.js.map +1 -0
- package/dist/spec/mining/pipeline.d.ts +53 -0
- package/dist/spec/mining/pipeline.d.ts.map +1 -0
- package/dist/spec/mining/pipeline.js +165 -0
- package/dist/spec/mining/pipeline.js.map +1 -0
- package/dist/spec/mining/scope-resolver.d.ts +45 -0
- package/dist/spec/mining/scope-resolver.d.ts.map +1 -0
- package/dist/spec/mining/scope-resolver.js +103 -0
- package/dist/spec/mining/scope-resolver.js.map +1 -0
- package/dist/spec/mining/spec-extractor.d.ts +69 -0
- package/dist/spec/mining/spec-extractor.d.ts.map +1 -0
- package/dist/spec/mining/spec-extractor.js +369 -0
- package/dist/spec/mining/spec-extractor.js.map +1 -0
- package/dist/spec/types.d.ts +149 -0
- package/dist/spec/types.d.ts.map +1 -0
- package/dist/spec/types.js +15 -0
- package/dist/spec/types.js.map +1 -0
- package/dist/spec/utils.d.ts +166 -0
- package/dist/spec/utils.d.ts.map +1 -0
- package/dist/spec/utils.js +461 -0
- package/dist/spec/utils.js.map +1 -0
- package/package.json +58 -57
- package/scripts/add-lang/bench.sh +60 -60
- package/scripts/add-lang/check-grammar.mjs +75 -75
- package/scripts/add-lang/dump-ast.mjs +103 -103
- package/scripts/add-lang/verify-extraction.mjs +70 -70
- package/scripts/agent-eval/ab-adoption.sh +91 -91
- package/scripts/agent-eval/ab-hook.sh +86 -86
- package/scripts/agent-eval/ab-impl.sh +78 -78
- package/scripts/agent-eval/ab-new-vs-baseline.sh +102 -102
- package/scripts/agent-eval/ab-sufficiency.sh +78 -78
- package/scripts/agent-eval/arms-F.sh +21 -21
- package/scripts/agent-eval/arms-matrix.sh +37 -37
- package/scripts/agent-eval/audit.sh +68 -68
- package/scripts/agent-eval/bench-readme.sh +28 -28
- package/scripts/agent-eval/bench-why-repo.sh +22 -22
- package/scripts/agent-eval/block-read-hook.sh +19 -19
- package/scripts/agent-eval/hook-settings.json +15 -15
- package/scripts/agent-eval/itrun.sh +120 -120
- package/scripts/agent-eval/offload-eval-3arm.sh +72 -72
- package/scripts/agent-eval/offload-eval-cost.mjs +133 -133
- package/scripts/agent-eval/offload-eval-effort.mjs +108 -108
- package/scripts/agent-eval/offload-eval-frontload-matrix.sh +25 -25
- package/scripts/agent-eval/offload-eval-frontload.sh +47 -47
- package/scripts/agent-eval/offload-eval-ground-truth.json +18 -18
- package/scripts/agent-eval/offload-eval-hook.mjs +84 -84
- package/scripts/agent-eval/offload-eval-judge.mjs +103 -103
- package/scripts/agent-eval/offload-eval-matrix.sh +20 -20
- package/scripts/agent-eval/offload-eval-metrics.mjs +94 -94
- package/scripts/agent-eval/offload-eval-refs1.sh +50 -50
- package/scripts/agent-eval/offload-eval-setup.sh +24 -24
- package/scripts/agent-eval/offload-eval-styles.sh +71 -71
- package/scripts/agent-eval/offload-eval-summarize.mjs +68 -68
- package/scripts/agent-eval/offload-eval.md +76 -76
- package/scripts/agent-eval/parse-arms.mjs +116 -116
- package/scripts/agent-eval/parse-bench-readme.mjs +84 -84
- package/scripts/agent-eval/parse-run.mjs +45 -45
- package/scripts/agent-eval/parse-session.mjs +93 -93
- package/scripts/agent-eval/probe-context.mjs +21 -21
- package/scripts/agent-eval/probe-explore.mjs +40 -40
- package/scripts/agent-eval/probe-node.mjs +20 -20
- package/scripts/agent-eval/probe-sweep.mjs +119 -119
- package/scripts/agent-eval/probe-trace.mjs +20 -20
- package/scripts/agent-eval/redirect-read-hook.sh +38 -38
- package/scripts/agent-eval/run-agent.sh +34 -34
- package/scripts/agent-eval/run-all.sh +69 -69
- package/scripts/agent-eval/run-arms.sh +56 -56
- package/scripts/agent-eval/seq-matrix.mjs +137 -137
- package/scripts/build-bundle.sh +118 -118
- package/scripts/extract-release-notes.mjs +130 -130
- package/scripts/local-install.sh +41 -41
- package/scripts/npm-sdk.js +75 -75
- package/scripts/npm-shim.js +246 -246
- package/scripts/pack-npm.sh +119 -119
- package/scripts/prepare-release.mjs +270 -270
- package/scripts/qa_eval/README.md +407 -404
- package/scripts/qa_eval/_test_deveco_probe.py +41 -41
- package/scripts/qa_eval/agent_runner.py +526 -526
- package/scripts/qa_eval/data/.gitignore +4 -4
- package/scripts/qa_eval/data/test-set.jsonl +2 -22
- package/scripts/qa_eval/eval_metrics.py +274 -233
- package/scripts/qa_eval/external_agent.py +976 -671
- package/scripts/qa_eval/llm_config.py +92 -92
- package/scripts/qa_eval/memory_monitor.py +132 -132
- package/scripts/qa_eval/my_answer_accuracy.py +187 -187
- package/scripts/qa_eval/requirements.txt +2 -2
- package/scripts/qa_eval/run_pipeline.py +804 -711
- package/scripts/qa_eval/stats_efficiency.py +279 -279
- package/scripts/qa_eval/stats_scores.py +207 -207
package/dist/mcp/tools.d.ts
CHANGED
|
@@ -193,10 +193,11 @@ export declare class ToolHandler {
|
|
|
193
193
|
hasDefaultHomeGraph(): boolean;
|
|
194
194
|
/**
|
|
195
195
|
* Optional allowlist of exposed tools, parsed from the HOMEGRAPH_MCP_TOOLS
|
|
196
|
-
* env var (comma-separated short names, e.g. "
|
|
197
|
-
* Unset/empty → every tool is exposed.
|
|
198
|
-
*
|
|
199
|
-
*
|
|
196
|
+
* env var (comma-separated short names, e.g. "explore,search,node").
|
|
197
|
+
* Unset/empty → every tool is exposed. Set → only the listed tools are
|
|
198
|
+
* exposed. Lets an operator (or an A/B harness) trim the tool surface
|
|
199
|
+
* without rebuilding the client config; the ablated tool is then truly
|
|
200
|
+
* absent from ListTools rather than merely denied on call.
|
|
200
201
|
* Matching is on the short form, so "node" and "homegraph_node" both work.
|
|
201
202
|
*/
|
|
202
203
|
private toolAllowlist;
|
|
@@ -312,6 +313,8 @@ export declare class ToolHandler {
|
|
|
312
313
|
* Handle homegraph_impact
|
|
313
314
|
*/
|
|
314
315
|
private handleImpact;
|
|
316
|
+
/** Whether a graph edge may be traversed by homegraph_explore's main Flow BFS. */
|
|
317
|
+
private isExploreFlowEdge;
|
|
315
318
|
/**
|
|
316
319
|
* Describe a synthesized (dynamic-dispatch) edge for human output: how the
|
|
317
320
|
* callback was wired up — the bridge static parsing can't see. Returns null
|
|
@@ -467,6 +470,16 @@ export declare class ToolHandler {
|
|
|
467
470
|
* Format files as a tree structure
|
|
468
471
|
*/
|
|
469
472
|
private formatFilesTree;
|
|
473
|
+
/**
|
|
474
|
+
* Match a spec/feature description against the Commit4Spec knowledge graph.
|
|
475
|
+
*
|
|
476
|
+
* Uses FTS5 full-text search to find the most similar historical specs,
|
|
477
|
+
* returning each with its linked commits and optional code fragments.
|
|
478
|
+
* The database lives at `.homegraph/commit4spec/commit4spec.db` by default and is
|
|
479
|
+
* separate from the HomeGraph code-symbol index — this tool works whether
|
|
480
|
+
* or not the code index is present.
|
|
481
|
+
*/
|
|
482
|
+
private handleSpecMatch;
|
|
470
483
|
/**
|
|
471
484
|
* Find a symbol by name, handling disambiguation when multiple matches exist.
|
|
472
485
|
* Returns the best match and a note about alternatives if any.
|
package/dist/mcp/tools.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../../src/mcp/tools.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,SAAS,MAAM,UAAU,CAAC;AAetC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../../src/mcp/tools.ts"],"names":[],"mappings":"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"}
|
package/dist/mcp/tools.js
CHANGED
|
@@ -25,6 +25,14 @@ const fs_1 = require("fs");
|
|
|
25
25
|
const utils_1 = require("../utils");
|
|
26
26
|
const generated_detection_1 = require("../extraction/generated-detection");
|
|
27
27
|
const dynamic_boundaries_1 = require("./dynamic-boundaries");
|
|
28
|
+
/** ViewTree structural `references` vias — not UI event bindings. */
|
|
29
|
+
const VIEWTREE_STRUCTURE_VIAS = new Set([
|
|
30
|
+
'child-component',
|
|
31
|
+
'state-binding',
|
|
32
|
+
'prop-transfer',
|
|
33
|
+
'builder',
|
|
34
|
+
'builder-param',
|
|
35
|
+
]);
|
|
28
36
|
/**
|
|
29
37
|
* An expected, recoverable "homegraph can't serve this" condition — most
|
|
30
38
|
* importantly a project with no index. The dispatch catch converts these to
|
|
@@ -551,6 +559,40 @@ exports.tools = [
|
|
|
551
559
|
},
|
|
552
560
|
},
|
|
553
561
|
},
|
|
562
|
+
{
|
|
563
|
+
name: 'homegraph_spec_match',
|
|
564
|
+
description: 'Match a new spec/feature description against the Commit4Spec knowledge graph using FTS5 full-text search. ' +
|
|
565
|
+
'Returns the most similar historical specs with their associated commits and code fragments. ' +
|
|
566
|
+
'The database defaults to .homegraph/commit4spec/commit4spec.db under the repo path.',
|
|
567
|
+
inputSchema: {
|
|
568
|
+
type: 'object',
|
|
569
|
+
properties: {
|
|
570
|
+
query: {
|
|
571
|
+
type: 'string',
|
|
572
|
+
description: 'Spec text (title + description) to match against historical specs.',
|
|
573
|
+
},
|
|
574
|
+
repoPath: {
|
|
575
|
+
type: 'string',
|
|
576
|
+
description: 'Path to the repository root. Defaults to the current working directory.',
|
|
577
|
+
},
|
|
578
|
+
topK: {
|
|
579
|
+
type: 'number',
|
|
580
|
+
description: 'Maximum number of similar specs to return (default: 5).',
|
|
581
|
+
default: 5,
|
|
582
|
+
},
|
|
583
|
+
includeFragments: {
|
|
584
|
+
type: 'boolean',
|
|
585
|
+
description: 'Whether to include full code diffs per commit (default: true).',
|
|
586
|
+
default: true,
|
|
587
|
+
},
|
|
588
|
+
dbPath: {
|
|
589
|
+
type: 'string',
|
|
590
|
+
description: 'Explicit path to the Commit4Spec database. Overrides repoPath-based resolution.',
|
|
591
|
+
},
|
|
592
|
+
},
|
|
593
|
+
required: ['query'],
|
|
594
|
+
},
|
|
595
|
+
},
|
|
554
596
|
];
|
|
555
597
|
/**
|
|
556
598
|
* Allowlist-filtered tool definitions WITHOUT an engine — the static surface the
|
|
@@ -561,23 +603,11 @@ exports.tools = [
|
|
|
561
603
|
function getStaticTools() {
|
|
562
604
|
const raw = process.env.HOMEGRAPH_MCP_TOOLS ?? process.env.HOMEGRAPH_MCP_TOOLS;
|
|
563
605
|
if (!raw || !raw.trim()) {
|
|
564
|
-
return exports.tools
|
|
606
|
+
return exports.tools;
|
|
565
607
|
}
|
|
566
608
|
const allow = new Set(raw.split(',').map(s => s.trim().replace(/^homegraph_/, '').replace(/^homegraph_/, '')).filter(Boolean));
|
|
567
609
|
return allow.size ? exports.tools.filter(t => allow.has(t.name.replace(/^homegraph_/, ''))) : exports.tools;
|
|
568
610
|
}
|
|
569
|
-
/**
|
|
570
|
-
* The MCP tools served by DEFAULT (short names). Pared to ONLY `homegraph_explore`
|
|
571
|
-
* — the single tool that reliably earns its place: one capped call returns the
|
|
572
|
-
* verbatim source of the relevant symbols grouped by file. Every other tool is a
|
|
573
|
-
* narrower slice of what explore already does, and presence itself steers
|
|
574
|
-
* mis-picks, so they are no longer LISTED to agents.
|
|
575
|
-
*
|
|
576
|
-
* The other defined tools (`node`, `search`, `callers`, plus callees/impact/files/
|
|
577
|
-
* status) remain fully functional — handlers stay, the library API and CLI are
|
|
578
|
-
* untouched, and `HOMEGRAPH_MCP_TOOLS=explore,node,...` re-enables any of them.
|
|
579
|
-
*/
|
|
580
|
-
const DEFAULT_MCP_TOOLS = new Set(['explore']);
|
|
581
611
|
/**
|
|
582
612
|
* Tool handler that executes tools against a HomeGraph instance
|
|
583
613
|
*
|
|
@@ -680,10 +710,11 @@ class ToolHandler {
|
|
|
680
710
|
}
|
|
681
711
|
/**
|
|
682
712
|
* Optional allowlist of exposed tools, parsed from the HOMEGRAPH_MCP_TOOLS
|
|
683
|
-
* env var (comma-separated short names, e.g. "
|
|
684
|
-
* Unset/empty → every tool is exposed.
|
|
685
|
-
*
|
|
686
|
-
*
|
|
713
|
+
* env var (comma-separated short names, e.g. "explore,search,node").
|
|
714
|
+
* Unset/empty → every tool is exposed. Set → only the listed tools are
|
|
715
|
+
* exposed. Lets an operator (or an A/B harness) trim the tool surface
|
|
716
|
+
* without rebuilding the client config; the ablated tool is then truly
|
|
717
|
+
* absent from ListTools rather than merely denied on call.
|
|
687
718
|
* Matching is on the short form, so "node" and "homegraph_node" both work.
|
|
688
719
|
*/
|
|
689
720
|
toolAllowlist() {
|
|
@@ -707,12 +738,11 @@ class ToolHandler {
|
|
|
707
738
|
*/
|
|
708
739
|
getTools() {
|
|
709
740
|
const allow = this.toolAllowlist();
|
|
710
|
-
// No explicit allowlist →
|
|
711
|
-
//
|
|
712
|
-
// default entirely, so any defined tool can be re-enabled.
|
|
741
|
+
// No explicit allowlist → expose every defined tool. An allowlist trims
|
|
742
|
+
// the surface to only the listed short names.
|
|
713
743
|
let visible = allow
|
|
714
744
|
? exports.tools.filter(t => allow.has(t.name.replace(/^homegraph_/, '')))
|
|
715
|
-
: exports.tools
|
|
745
|
+
: exports.tools;
|
|
716
746
|
if (!this.cg)
|
|
717
747
|
return visible;
|
|
718
748
|
try {
|
|
@@ -1147,6 +1177,9 @@ class ToolHandler {
|
|
|
1147
1177
|
case 'homegraph_files':
|
|
1148
1178
|
result = await this.handleFiles(args);
|
|
1149
1179
|
break;
|
|
1180
|
+
case 'homegraph_spec_match':
|
|
1181
|
+
result = await this.handleSpecMatch(args);
|
|
1182
|
+
break;
|
|
1150
1183
|
default:
|
|
1151
1184
|
return this.errorResult(`Unknown tool: ${toolName}`);
|
|
1152
1185
|
}
|
|
@@ -1427,6 +1460,18 @@ class ToolHandler {
|
|
|
1427
1460
|
}
|
|
1428
1461
|
return this.textResult(this.truncateOutput(sections.join('\n') + filterNote));
|
|
1429
1462
|
}
|
|
1463
|
+
/** Whether a graph edge may be traversed by homegraph_explore's main Flow BFS. */
|
|
1464
|
+
isExploreFlowEdge(edge) {
|
|
1465
|
+
if (edge.kind === 'calls')
|
|
1466
|
+
return true;
|
|
1467
|
+
if (edge.kind !== 'references' || edge.provenance !== 'heuristic')
|
|
1468
|
+
return false;
|
|
1469
|
+
const m = edge.metadata;
|
|
1470
|
+
if (m?.synthesizedBy !== 'viewtree')
|
|
1471
|
+
return false;
|
|
1472
|
+
const via = typeof m.via === 'string' ? m.via : '';
|
|
1473
|
+
return via.length > 0 && !VIEWTREE_STRUCTURE_VIAS.has(via);
|
|
1474
|
+
}
|
|
1430
1475
|
/**
|
|
1431
1476
|
* Describe a synthesized (dynamic-dispatch) edge for human output: how the
|
|
1432
1477
|
* callback was wired up — the bridge static parsing can't see. Returns null
|
|
@@ -1479,6 +1524,16 @@ class ToolHandler {
|
|
|
1479
1524
|
registeredAt,
|
|
1480
1525
|
};
|
|
1481
1526
|
}
|
|
1527
|
+
if (m?.synthesizedBy === 'viewtree') {
|
|
1528
|
+
const via = typeof m.via === 'string' ? m.via : '';
|
|
1529
|
+
if (via && !VIEWTREE_STRUCTURE_VIAS.has(via)) {
|
|
1530
|
+
return {
|
|
1531
|
+
label: `ArkUI event \`.${via}\` — bound handler (dynamic dispatch)`,
|
|
1532
|
+
compact: `dynamic: ArkUI .${via}${at}`,
|
|
1533
|
+
registeredAt,
|
|
1534
|
+
};
|
|
1535
|
+
}
|
|
1536
|
+
}
|
|
1482
1537
|
if (m?.synthesizedBy === 'interface-impl') {
|
|
1483
1538
|
return {
|
|
1484
1539
|
label: `interface/abstract dispatch — runs the implementation override (dynamic dispatch)`,
|
|
@@ -1687,7 +1742,7 @@ class ToolHandler {
|
|
|
1687
1742
|
if (depth >= MAX_HOPS - 1)
|
|
1688
1743
|
continue;
|
|
1689
1744
|
for (const c of cg.getCallees(id)) {
|
|
1690
|
-
if (c.edge
|
|
1745
|
+
if (!this.isExploreFlowEdge(c.edge) || parent.has(c.node.id))
|
|
1691
1746
|
continue;
|
|
1692
1747
|
const newStreak = named.has(c.node.id) ? 0 : streak + 1;
|
|
1693
1748
|
if (newStreak > MAX_BRIDGE)
|
|
@@ -3814,6 +3869,121 @@ class ToolHandler {
|
|
|
3814
3869
|
return lines.join('\n');
|
|
3815
3870
|
}
|
|
3816
3871
|
// =========================================================================
|
|
3872
|
+
// handleSpecMatch — Commit4Spec knowledge-graph search
|
|
3873
|
+
// =========================================================================
|
|
3874
|
+
/**
|
|
3875
|
+
* Match a spec/feature description against the Commit4Spec knowledge graph.
|
|
3876
|
+
*
|
|
3877
|
+
* Uses FTS5 full-text search to find the most similar historical specs,
|
|
3878
|
+
* returning each with its linked commits and optional code fragments.
|
|
3879
|
+
* The database lives at `.homegraph/commit4spec/commit4spec.db` by default and is
|
|
3880
|
+
* separate from the HomeGraph code-symbol index — this tool works whether
|
|
3881
|
+
* or not the code index is present.
|
|
3882
|
+
*/
|
|
3883
|
+
async handleSpecMatch(args) {
|
|
3884
|
+
const query = this.validateString(args.query, 'query');
|
|
3885
|
+
if (typeof query !== 'string')
|
|
3886
|
+
return query;
|
|
3887
|
+
const repoPath = args.repoPath;
|
|
3888
|
+
const explicitDbPath = args.dbPath;
|
|
3889
|
+
const topK = Math.max(1, Math.min(Number(args.topK) || 5, 50));
|
|
3890
|
+
const includeFragments = args.includeFragments !== false;
|
|
3891
|
+
// Lazily require spec modules so the MCP startup path stays lean.
|
|
3892
|
+
const { resolveDbPath } = require('../spec/utils');
|
|
3893
|
+
const { createDatabase } = require('../db/sqlite-adapter');
|
|
3894
|
+
const { initSpecSchema } = require('../spec/db/schema');
|
|
3895
|
+
const { searchAndGetContext } = require('../spec/graph/queries');
|
|
3896
|
+
const { truncateCodeDiff, truncateSubtitles, computeBudgetProfile, } = require('../spec/utils');
|
|
3897
|
+
// Resolve the database path.
|
|
3898
|
+
const dbPath = resolveDbPath(repoPath || process.cwd(), explicitDbPath);
|
|
3899
|
+
// Open the database.
|
|
3900
|
+
let db;
|
|
3901
|
+
try {
|
|
3902
|
+
db = createDatabase(dbPath).db;
|
|
3903
|
+
}
|
|
3904
|
+
catch (err) {
|
|
3905
|
+
return this.errorResult(`Failed to open Commit4Spec database at "${dbPath}": ` +
|
|
3906
|
+
`${err instanceof Error ? err.message : String(err)}`);
|
|
3907
|
+
}
|
|
3908
|
+
try {
|
|
3909
|
+
// Ensure the schema exists (idempotent).
|
|
3910
|
+
initSpecSchema(db);
|
|
3911
|
+
// Search and traverse.
|
|
3912
|
+
const contexts = searchAndGetContext(db, query, topK, includeFragments);
|
|
3913
|
+
if (contexts.length === 0) {
|
|
3914
|
+
return this.textResult(JSON.stringify({
|
|
3915
|
+
query: query.slice(0, 200),
|
|
3916
|
+
matched_count: 0,
|
|
3917
|
+
results: [],
|
|
3918
|
+
}, null, 2));
|
|
3919
|
+
}
|
|
3920
|
+
// Build budget profile for truncation.
|
|
3921
|
+
const profile = computeBudgetProfile(contexts.length);
|
|
3922
|
+
// Serialize to JSON (matching Python's spec_contexts_to_results format).
|
|
3923
|
+
const results = contexts.map((ctx) => {
|
|
3924
|
+
const subtitles = profile.tier === 'vlarge'
|
|
3925
|
+
? [] // vlarge disables subtitles entirely
|
|
3926
|
+
: truncateSubtitles(ctx.spec.subtitles, 200, profile.maxContents);
|
|
3927
|
+
const commits = ctx.commits.slice(0, profile.maxContents || 5).map((c) => {
|
|
3928
|
+
const cd = {
|
|
3929
|
+
hash: c.commit.hash,
|
|
3930
|
+
message: c.commit.message,
|
|
3931
|
+
relation_type: c.relationType,
|
|
3932
|
+
};
|
|
3933
|
+
if (includeFragments) {
|
|
3934
|
+
const maxFrags = profile.maxFragments || 3;
|
|
3935
|
+
cd.fragments = c.fragments.slice(0, maxFrags).map((f) => ({
|
|
3936
|
+
file_path: f.filePath,
|
|
3937
|
+
change_type: f.changeType,
|
|
3938
|
+
start_line: f.startLine,
|
|
3939
|
+
end_line: f.endLine,
|
|
3940
|
+
code_diff: truncateCodeDiff(f.codeDiff),
|
|
3941
|
+
}));
|
|
3942
|
+
}
|
|
3943
|
+
return cd;
|
|
3944
|
+
});
|
|
3945
|
+
return {
|
|
3946
|
+
spec_id: ctx.spec.id,
|
|
3947
|
+
title: ctx.spec.title,
|
|
3948
|
+
subtitles,
|
|
3949
|
+
file_path: ctx.spec.filePath,
|
|
3950
|
+
commits,
|
|
3951
|
+
};
|
|
3952
|
+
});
|
|
3953
|
+
const response = {
|
|
3954
|
+
query: query.slice(0, 200),
|
|
3955
|
+
matched_count: results.length,
|
|
3956
|
+
results,
|
|
3957
|
+
};
|
|
3958
|
+
const json = JSON.stringify(response, null, 2);
|
|
3959
|
+
// Hard cap to MAX_OUTPUT_LENGTH to prevent context bloat.
|
|
3960
|
+
if (json.length <= MAX_OUTPUT_LENGTH) {
|
|
3961
|
+
return this.textResult(json);
|
|
3962
|
+
}
|
|
3963
|
+
// When the full payload exceeds the cap, drop fragments first,
|
|
3964
|
+
// then trim commits, then trim the whole thing.
|
|
3965
|
+
if (includeFragments) {
|
|
3966
|
+
const slimResults = results.map((r) => ({
|
|
3967
|
+
...r,
|
|
3968
|
+
commits: r.commits.map((c) => {
|
|
3969
|
+
const { fragments: _, ...rest } = c;
|
|
3970
|
+
return rest;
|
|
3971
|
+
}),
|
|
3972
|
+
}));
|
|
3973
|
+
const slim = JSON.stringify({ ...response, results: slimResults }, null, 2);
|
|
3974
|
+
if (slim.length <= MAX_OUTPUT_LENGTH) {
|
|
3975
|
+
return this.textResult(`(Fragments elided — output exceeded ${MAX_OUTPUT_LENGTH} chars)\n\n` + slim);
|
|
3976
|
+
}
|
|
3977
|
+
}
|
|
3978
|
+
// Fallback: hard-truncate the JSON.
|
|
3979
|
+
const truncated = json.slice(0, MAX_OUTPUT_LENGTH - 3) + '...';
|
|
3980
|
+
return this.textResult(truncated);
|
|
3981
|
+
}
|
|
3982
|
+
finally {
|
|
3983
|
+
db.close();
|
|
3984
|
+
}
|
|
3985
|
+
}
|
|
3986
|
+
// =========================================================================
|
|
3817
3987
|
// Symbol resolution helpers
|
|
3818
3988
|
// =========================================================================
|
|
3819
3989
|
/**
|