homegraph 1.5.0 → 1.5.2

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 (213) hide show
  1. package/dist/bin/command-supervision.d.ts.map +1 -1
  2. package/dist/bin/command-supervision.js +7 -4
  3. package/dist/bin/command-supervision.js.map +1 -1
  4. package/dist/extraction/index.d.ts +1 -1
  5. package/dist/extraction/index.d.ts.map +1 -1
  6. package/dist/extraction/index.js +54 -43
  7. package/dist/extraction/index.js.map +1 -1
  8. package/dist/extraction/languages/arkts.d.ts +69 -12
  9. package/dist/extraction/languages/arkts.d.ts.map +1 -1
  10. package/dist/extraction/languages/arkts.js +739 -86
  11. package/dist/extraction/languages/arkts.js.map +1 -1
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js +3 -2
  14. package/dist/index.js.map +1 -1
  15. package/dist/mcp/diff-impact.d.ts +131 -0
  16. package/dist/mcp/diff-impact.d.ts.map +1 -0
  17. package/dist/mcp/diff-impact.js +385 -0
  18. package/dist/mcp/diff-impact.js.map +1 -0
  19. package/dist/mcp/liveness-watchdog.d.ts +18 -0
  20. package/dist/mcp/liveness-watchdog.d.ts.map +1 -1
  21. package/dist/mcp/liveness-watchdog.js +134 -22
  22. package/dist/mcp/liveness-watchdog.js.map +1 -1
  23. package/dist/mcp/server-instructions.d.ts +1 -1
  24. package/dist/mcp/server-instructions.d.ts.map +1 -1
  25. package/dist/mcp/server-instructions.js +2 -0
  26. package/dist/mcp/server-instructions.js.map +1 -1
  27. package/dist/mcp/tools.d.ts +21 -5
  28. package/dist/mcp/tools.d.ts.map +1 -1
  29. package/dist/mcp/tools.js +305 -55
  30. package/dist/mcp/tools.js.map +1 -1
  31. package/dist/resolution/callback-synthesizer.d.ts +2 -1
  32. package/dist/resolution/callback-synthesizer.d.ts.map +1 -1
  33. package/dist/resolution/callback-synthesizer.js +153 -53
  34. package/dist/resolution/callback-synthesizer.js.map +1 -1
  35. package/dist/resolution/frameworks/arkts-entry.d.ts.map +1 -1
  36. package/dist/resolution/frameworks/arkts-entry.js +20 -9
  37. package/dist/resolution/frameworks/arkts-entry.js.map +1 -1
  38. package/dist/resolution/index.d.ts.map +1 -1
  39. package/dist/resolution/index.js +1 -1
  40. package/dist/resolution/index.js.map +1 -1
  41. package/dist/resolution/memory-budget.d.ts +33 -0
  42. package/dist/resolution/memory-budget.d.ts.map +1 -1
  43. package/dist/resolution/memory-budget.js +58 -0
  44. package/dist/resolution/memory-budget.js.map +1 -1
  45. package/dist/resolution/resolver-pool.d.ts +2 -0
  46. package/dist/resolution/resolver-pool.d.ts.map +1 -1
  47. package/dist/resolution/resolver-pool.js +4 -0
  48. package/dist/resolution/resolver-pool.js.map +1 -1
  49. package/dist/ui/shimmer-progress.d.ts.map +1 -1
  50. package/dist/ui/shimmer-progress.js +4 -1
  51. package/dist/ui/shimmer-progress.js.map +1 -1
  52. package/package.json +4 -5
  53. package/dist/arkts/ohos-api-index.d.ts +0 -15
  54. package/dist/arkts/ohos-api-index.d.ts.map +0 -1
  55. package/dist/arkts/ohos-api-index.js +0 -190
  56. package/dist/arkts/ohos-api-index.js.map +0 -1
  57. package/dist/arkts/ohos-sdk-input.d.ts +0 -36
  58. package/dist/arkts/ohos-sdk-input.d.ts.map +0 -1
  59. package/dist/arkts/ohos-sdk-input.js +0 -214
  60. package/dist/arkts/ohos-sdk-input.js.map +0 -1
  61. package/dist/extraction/languages/arkts-state-decorators.d.ts +0 -13
  62. package/dist/extraction/languages/arkts-state-decorators.d.ts.map +0 -1
  63. package/dist/extraction/languages/arkts-state-decorators.js +0 -26
  64. package/dist/extraction/languages/arkts-state-decorators.js.map +0 -1
  65. package/dist/extraction/languages/arkts-viewtree.d.ts +0 -24
  66. package/dist/extraction/languages/arkts-viewtree.d.ts.map +0 -1
  67. package/dist/extraction/languages/arkts-viewtree.js +0 -148
  68. package/dist/extraction/languages/arkts-viewtree.js.map +0 -1
  69. package/dist/extraction/languages/ohos-api-consumer.d.ts +0 -34
  70. package/dist/extraction/languages/ohos-api-consumer.d.ts.map +0 -1
  71. package/dist/extraction/languages/ohos-api-consumer.js +0 -283
  72. package/dist/extraction/languages/ohos-api-consumer.js.map +0 -1
  73. package/dist/reasoning/config.d.ts +0 -45
  74. package/dist/reasoning/config.d.ts.map +0 -1
  75. package/dist/reasoning/config.js +0 -171
  76. package/dist/reasoning/config.js.map +0 -1
  77. package/dist/reasoning/credentials.d.ts +0 -5
  78. package/dist/reasoning/credentials.d.ts.map +0 -1
  79. package/dist/reasoning/credentials.js +0 -83
  80. package/dist/reasoning/credentials.js.map +0 -1
  81. package/dist/reasoning/login.d.ts +0 -21
  82. package/dist/reasoning/login.d.ts.map +0 -1
  83. package/dist/reasoning/login.js +0 -85
  84. package/dist/reasoning/login.js.map +0 -1
  85. package/dist/reasoning/reasoner.d.ts +0 -43
  86. package/dist/reasoning/reasoner.d.ts.map +0 -1
  87. package/dist/reasoning/reasoner.js +0 -308
  88. package/dist/reasoning/reasoner.js.map +0 -1
  89. package/dist/spec/build/git-scanner.d.ts +0 -93
  90. package/dist/spec/build/git-scanner.d.ts.map +0 -1
  91. package/dist/spec/build/git-scanner.js +0 -254
  92. package/dist/spec/build/git-scanner.js.map +0 -1
  93. package/dist/spec/evolve/llm-client.d.ts +0 -50
  94. package/dist/spec/evolve/llm-client.d.ts.map +0 -1
  95. package/dist/spec/evolve/llm-client.js +0 -176
  96. package/dist/spec/evolve/llm-client.js.map +0 -1
  97. package/dist/spec/evolve/logic-checker.d.ts +0 -12
  98. package/dist/spec/evolve/logic-checker.d.ts.map +0 -1
  99. package/dist/spec/evolve/logic-checker.js +0 -24
  100. package/dist/spec/evolve/logic-checker.js.map +0 -1
  101. package/dist/spec/git-utils.d.ts +0 -8
  102. package/dist/spec/git-utils.d.ts.map +0 -1
  103. package/dist/spec/git-utils.js +0 -14
  104. package/dist/spec/git-utils.js.map +0 -1
  105. package/dist/spec/llm/index.d.ts +0 -3
  106. package/dist/spec/llm/index.d.ts.map +0 -1
  107. package/dist/spec/llm/index.js +0 -11
  108. package/dist/spec/llm/index.js.map +0 -1
  109. package/dist/spec/mine/clusterer.d.ts +0 -63
  110. package/dist/spec/mine/clusterer.d.ts.map +0 -1
  111. package/dist/spec/mine/clusterer.js +0 -904
  112. package/dist/spec/mine/clusterer.js.map +0 -1
  113. package/dist/spec/mine/progress-handler.d.ts +0 -22
  114. package/dist/spec/mine/progress-handler.d.ts.map +0 -1
  115. package/dist/spec/mine/progress-handler.js +0 -108
  116. package/dist/spec/mine/progress-handler.js.map +0 -1
  117. package/dist/spec/mine/progress.d.ts +0 -23
  118. package/dist/spec/mine/progress.d.ts.map +0 -1
  119. package/dist/spec/mine/progress.js +0 -12
  120. package/dist/spec/mine/progress.js.map +0 -1
  121. package/dist/spec/mining/diff-parser.d.ts +0 -33
  122. package/dist/spec/mining/diff-parser.d.ts.map +0 -1
  123. package/dist/spec/mining/diff-parser.js +0 -166
  124. package/dist/spec/mining/diff-parser.js.map +0 -1
  125. package/dist/spec/mining/git-scanner.d.ts +0 -103
  126. package/dist/spec/mining/git-scanner.d.ts.map +0 -1
  127. package/dist/spec/mining/git-scanner.js +0 -307
  128. package/dist/spec/mining/git-scanner.js.map +0 -1
  129. package/dist/spec/mining/pipeline.d.ts +0 -53
  130. package/dist/spec/mining/pipeline.d.ts.map +0 -1
  131. package/dist/spec/mining/pipeline.js +0 -178
  132. package/dist/spec/mining/pipeline.js.map +0 -1
  133. package/dist/spec/mining/scope-resolver.d.ts +0 -45
  134. package/dist/spec/mining/scope-resolver.d.ts.map +0 -1
  135. package/dist/spec/mining/scope-resolver.js +0 -103
  136. package/dist/spec/mining/scope-resolver.js.map +0 -1
  137. package/dist/spec/mining/spec-extractor.d.ts +0 -69
  138. package/dist/spec/mining/spec-extractor.d.ts.map +0 -1
  139. package/dist/spec/mining/spec-extractor.js +0 -369
  140. package/dist/spec/mining/spec-extractor.js.map +0 -1
  141. package/dist/spec/utils.d.ts +0 -155
  142. package/dist/spec/utils.d.ts.map +0 -1
  143. package/dist/spec/utils.js +0 -411
  144. package/dist/spec/utils.js.map +0 -1
  145. package/scripts/add-lang/bench.sh +0 -60
  146. package/scripts/add-lang/check-grammar.mjs +0 -75
  147. package/scripts/add-lang/dump-ast.mjs +0 -103
  148. package/scripts/add-lang/verify-extraction.mjs +0 -70
  149. package/scripts/agent-eval/ab-adoption.sh +0 -91
  150. package/scripts/agent-eval/ab-hook.sh +0 -86
  151. package/scripts/agent-eval/ab-impl.sh +0 -78
  152. package/scripts/agent-eval/ab-new-vs-baseline.sh +0 -102
  153. package/scripts/agent-eval/ab-sufficiency.sh +0 -78
  154. package/scripts/agent-eval/arms-F.sh +0 -21
  155. package/scripts/agent-eval/arms-matrix.sh +0 -37
  156. package/scripts/agent-eval/audit.sh +0 -68
  157. package/scripts/agent-eval/bench-readme.sh +0 -28
  158. package/scripts/agent-eval/bench-why-repo.sh +0 -22
  159. package/scripts/agent-eval/block-read-hook.sh +0 -19
  160. package/scripts/agent-eval/hook-settings.json +0 -15
  161. package/scripts/agent-eval/itrun.sh +0 -120
  162. package/scripts/agent-eval/offload-eval-3arm.sh +0 -72
  163. package/scripts/agent-eval/offload-eval-cost.mjs +0 -133
  164. package/scripts/agent-eval/offload-eval-effort.mjs +0 -108
  165. package/scripts/agent-eval/offload-eval-frontload-matrix.sh +0 -25
  166. package/scripts/agent-eval/offload-eval-frontload.sh +0 -47
  167. package/scripts/agent-eval/offload-eval-ground-truth.json +0 -18
  168. package/scripts/agent-eval/offload-eval-hook.mjs +0 -84
  169. package/scripts/agent-eval/offload-eval-judge.mjs +0 -103
  170. package/scripts/agent-eval/offload-eval-matrix.sh +0 -20
  171. package/scripts/agent-eval/offload-eval-metrics.mjs +0 -94
  172. package/scripts/agent-eval/offload-eval-refs1.sh +0 -50
  173. package/scripts/agent-eval/offload-eval-setup.sh +0 -24
  174. package/scripts/agent-eval/offload-eval-styles.sh +0 -72
  175. package/scripts/agent-eval/offload-eval-summarize.mjs +0 -68
  176. package/scripts/agent-eval/offload-eval.md +0 -76
  177. package/scripts/agent-eval/parse-arms.mjs +0 -116
  178. package/scripts/agent-eval/parse-bench-readme.mjs +0 -84
  179. package/scripts/agent-eval/parse-run.mjs +0 -45
  180. package/scripts/agent-eval/parse-session.mjs +0 -93
  181. package/scripts/agent-eval/probe-context.mjs +0 -21
  182. package/scripts/agent-eval/probe-explore.mjs +0 -40
  183. package/scripts/agent-eval/probe-node.mjs +0 -20
  184. package/scripts/agent-eval/probe-sweep.mjs +0 -119
  185. package/scripts/agent-eval/probe-trace.mjs +0 -20
  186. package/scripts/agent-eval/redirect-read-hook.sh +0 -38
  187. package/scripts/agent-eval/repro-concurrent-explore.mjs +0 -119
  188. package/scripts/agent-eval/repro-daemon-clients.mjs +0 -125
  189. package/scripts/agent-eval/run-agent.sh +0 -34
  190. package/scripts/agent-eval/run-all.sh +0 -75
  191. package/scripts/agent-eval/run-arms.sh +0 -56
  192. package/scripts/agent-eval/seq-matrix.mjs +0 -137
  193. package/scripts/build-bundle.sh +0 -123
  194. package/scripts/exp_boundary_eval/README.md +0 -247
  195. package/scripts/exp_boundary_eval/_test_mcp_chain.py +0 -78
  196. package/scripts/exp_boundary_eval/_test_stdin.py +0 -8
  197. package/scripts/exp_boundary_eval/_utils.py +0 -1116
  198. package/scripts/exp_boundary_eval/analyze.py +0 -1313
  199. package/scripts/exp_boundary_eval/deveco_arm.py +0 -519
  200. package/scripts/exp_boundary_eval/run_all.py +0 -378
  201. package/scripts/exp_boundary_eval/run_one.py +0 -165
  202. package/scripts/exp_boundary_eval/run_session.py +0 -158
  203. package/scripts/exp_boundary_eval/setup.py +0 -120
  204. package/scripts/exp_boundary_eval/win_mcp_launcher.py +0 -73
  205. package/scripts/exp_boundary_eval/win_mcp_stdio_wrap.js +0 -36
  206. package/scripts/exp_boundary_eval/win_node_launcher.py +0 -24
  207. package/scripts/extract-release-notes.mjs +0 -130
  208. package/scripts/local-install.sh +0 -41
  209. package/scripts/npm-sdk.js +0 -75
  210. package/scripts/npm-shim.js +0 -275
  211. package/scripts/ohos-sdk-publish.mjs +0 -133
  212. package/scripts/pack-npm.sh +0 -119
  213. package/scripts/prepare-release.mjs +0 -270
package/dist/mcp/tools.js CHANGED
@@ -31,6 +31,7 @@ const generated_detection_1 = require("../extraction/generated-detection");
31
31
  const arkts_1 = require("../extraction/languages/arkts");
32
32
  const dynamic_boundaries_1 = require("./dynamic-boundaries");
33
33
  const query_cache_1 = require("./query-cache");
34
+ const diff_impact_1 = require("./diff-impact");
34
35
  /** ViewTree structural `references` vias — not UI event bindings. */
35
36
  const VIEWTREE_STRUCTURE_VIAS = new Set([
36
37
  'child-component',
@@ -73,6 +74,18 @@ const MAX_OUTPUT_LENGTH = 15000;
73
74
  * far beyond any realistic legitimate query.
74
75
  */
75
76
  const MAX_INPUT_LENGTH = 10_000;
77
+ /** Example values for success-shaped bad-arg guidance (keyed by arg name). */
78
+ const BAD_ARG_EXAMPLES = {
79
+ query: 'authenticate login',
80
+ symbol: 'authenticate',
81
+ filePath: 'src/auth.ts',
82
+ file: 'src/auth.ts',
83
+ path: 'src/components',
84
+ pattern: '*.ets',
85
+ projectPath: '/absolute/path/to/your/project',
86
+ repoPath: '/absolute/path/to/your/project',
87
+ diff: 'diff --git a/src/auth.ts b/src/auth.ts\n--- a/src/auth.ts\n+++ b/src/auth.ts\n@@ -10,3 +10,4 @@\n+export function authenticate() {}\n',
88
+ };
76
89
  /**
77
90
  * Maximum length for path-like string inputs (projectPath, path
78
91
  * filter, glob pattern). Paths beyond a few thousand chars are
@@ -481,17 +494,19 @@ const READ_ONLY_ANNOTATIONS = {
481
494
  exports.tools = [
482
495
  {
483
496
  name: 'homegraph_search',
484
- description: 'LAST RESORT spelling lookup — locations only, no source. Prefer homegraph_explore whenever the question already names a symbol/file/@kit. If you do call search with a bare name, HomeGraph may answer with a compact explore result instead of locations.',
497
+ description: 'LAST RESORT spelling lookup — locations only, no source. Required: `query` (e.g. "signIn"). ' +
498
+ 'Prefer homegraph_explore when the question already names a symbol/file/@kit. ' +
499
+ 'Bare-name search may return a compact explore result instead of locations.',
485
500
  inputSchema: {
486
501
  type: 'object',
487
502
  properties: {
488
503
  query: {
489
504
  type: 'string',
490
- description: 'Symbol name or partial name (e.g., "auth", "signIn", "UserService")',
505
+ description: 'Required. Symbol name or partial name (e.g. "auth", "signIn", "UserService").',
491
506
  },
492
507
  kind: {
493
508
  type: 'string',
494
- description: 'Filter by node kind',
509
+ description: 'Optional filter by node kind',
495
510
  enum: ['function', 'method', 'class', 'interface', 'type', 'variable', 'route', 'component'],
496
511
  },
497
512
  limit: {
@@ -507,17 +522,20 @@ exports.tools = [
507
522
  },
508
523
  {
509
524
  name: 'homegraph_callers',
510
- description: 'Compact caller list for a NAMED in-repo symbol (no bodies). Use after you know the exact name. For full flows use homegraph_explore. DO NOT call for SDK catalogs, one-function semantics, or "what if X fails" hypothetics — Read that function instead. Prefer one explore over parallel callers+callees+node.',
525
+ description: 'Compact caller list for one NAMED in-repo symbol (no bodies). Required: `symbol` (e.g. "authenticate"). ' +
526
+ 'Use after you know the exact name. For full flows use homegraph_explore. ' +
527
+ 'DO NOT call for SDK catalogs, one-function semantics, or "what if X fails" hypothetics. ' +
528
+ 'Prefer one explore over parallel callers+callees+node.',
511
529
  inputSchema: {
512
530
  type: 'object',
513
531
  properties: {
514
532
  symbol: {
515
533
  type: 'string',
516
- description: 'Name of the function, method, or class to find callers for',
534
+ description: 'Required. Exact function/method/class name (e.g. "authenticate", "AuthService.login").',
517
535
  },
518
536
  file: {
519
537
  type: 'string',
520
- description: 'Narrow to the definition in this file (path or suffix) when several same-named symbols exist (e.g. one UserService per app in a monorepo)',
538
+ description: 'Optional. Narrow to the definition in this file (path or suffix) when same-named symbols collide.',
521
539
  },
522
540
  limit: {
523
541
  type: 'number',
@@ -532,17 +550,19 @@ exports.tools = [
532
550
  },
533
551
  {
534
552
  name: 'homegraph_callees',
535
- description: 'Compact callee list for a NAMED in-repo symbol (no bodies). For full flows use homegraph_explore. DO NOT use for out-of-repo SDK internals or counterfactual analysis. Prefer one explore over parallel node+callers+callees.',
553
+ description: 'Compact callee list for one NAMED in-repo symbol (no bodies). Required: `symbol` (e.g. "authenticate"). ' +
554
+ 'For full flows use homegraph_explore. DO NOT use for out-of-repo SDK internals or counterfactual analysis. ' +
555
+ 'Prefer one explore over parallel node+callers+callees.',
536
556
  inputSchema: {
537
557
  type: 'object',
538
558
  properties: {
539
559
  symbol: {
540
560
  type: 'string',
541
- description: 'Name of the function, method, or class to find callees for',
561
+ description: 'Required. Exact function/method/class name (e.g. "authenticate", "AuthService.login").',
542
562
  },
543
563
  file: {
544
564
  type: 'string',
545
- description: 'Narrow to the definition in this file (path or suffix) when several same-named symbols exist',
565
+ description: 'Optional. Narrow to the definition in this file (path or suffix) when same-named symbols collide.',
546
566
  },
547
567
  limit: {
548
568
  type: 'number',
@@ -557,17 +577,19 @@ exports.tools = [
557
577
  },
558
578
  {
559
579
  name: 'homegraph_impact',
560
- description: 'Blast radius for a NAMED in-repo symbol before a refactor. Not for SDK docs, permission judgments, or hypothetical failure effects — those need Read/Grep, not impact.',
580
+ description: 'Blast radius for one NAMED in-repo symbol before a refactor. Required: `symbol` (e.g. "authenticate"). ' +
581
+ 'Not for SDK docs, permission judgments, or hypothetical failure effects — those need Read/Grep. ' +
582
+ 'For PR/diff review use homegraph_diff_impact instead.',
561
583
  inputSchema: {
562
584
  type: 'object',
563
585
  properties: {
564
586
  symbol: {
565
587
  type: 'string',
566
- description: 'Name of the symbol to analyze impact for',
588
+ description: 'Required. Exact symbol name to analyze (e.g. "authenticate", "CartRepository").',
567
589
  },
568
590
  file: {
569
591
  type: 'string',
570
- description: 'Narrow to the definition in this file (path or suffix) when several same-named symbols exist',
592
+ description: 'Optional. Narrow to the definition in this file (path or suffix) when same-named symbols collide.',
571
593
  },
572
594
  depth: {
573
595
  type: 'number',
@@ -580,15 +602,64 @@ exports.tools = [
580
602
  },
581
603
  annotations: READ_ONLY_ANNOTATIONS,
582
604
  },
605
+ {
606
+ name: 'homegraph_diff_impact',
607
+ description: 'PR / code-review evidence pack from a unified diff (or explicit hunks). ' +
608
+ 'Required: `diff` (unified diff text) OR `hunks` [{ path, startLine, endLine }]. ' +
609
+ 'Intersects NEW-side changed lines with indexed symbol spans — does NOT dump every symbol in touched files. ' +
610
+ 'Returns changedSymbols + capped callers + impactSummary + UI edges (viewtree/arkui-*); optional relatedSpecs. ' +
611
+ 'Index should match the post-change tree. Does not write review text or judge Specs.',
612
+ inputSchema: {
613
+ type: 'object',
614
+ properties: {
615
+ diff: {
616
+ type: 'string',
617
+ description: 'Unified diff text (preferred). New-side @@ +line ranges are used to find changed symbols. ' +
618
+ 'Example: output of `git diff` / PR patch for the files under review.',
619
+ },
620
+ hunks: {
621
+ type: 'array',
622
+ description: 'Alternative to `diff`: explicit changed ranges. Each item: { path, startLine, endLine } (1-based, inclusive, new-file lines).',
623
+ items: {
624
+ type: 'object',
625
+ properties: {
626
+ path: { type: 'string', description: 'Project-relative file path' },
627
+ startLine: { type: 'number', description: '1-based start line (new file)' },
628
+ endLine: { type: 'number', description: '1-based end line (new file)' },
629
+ },
630
+ },
631
+ },
632
+ depth: {
633
+ type: 'number',
634
+ description: 'Impact / caller traversal depth (default: 2, max: 5)',
635
+ default: 2,
636
+ },
637
+ includeSpecs: {
638
+ type: 'boolean',
639
+ description: 'If true, attach related Commit4Spec hits for changed files/symbols (needs commit4spec.db). Default: false.',
640
+ default: false,
641
+ },
642
+ projectPath: projectPathProperty,
643
+ },
644
+ required: [],
645
+ },
646
+ annotations: READ_ONLY_ANNOTATIONS,
647
+ },
583
648
  {
584
649
  name: 'homegraph_node',
585
- description: 'Depth on ONE known in-repo symbol or indexed file — not a survey tool. (1) FILE: pass `file` alone → bounded line-numbered source + dependents. (2) SYMBOL: body (includeCode) + short trail; overloads return every body. USE after explore named the symbol and you still need one body. DO NOT call repeatedly to crawl a feature (prefer one explore). DO NOT call for: @kit/SDK catalogs, broad "how does X work", or after explore already returned that symbol\'s source. Treat returned source as already Read — do not grep/read the same path.',
650
+ description: 'Depth on ONE known in-repo symbol or indexed file — not a survey tool. ' +
651
+ 'Required: pass `symbol` (symbol mode) OR `file` alone (file mode). ' +
652
+ 'FILE: `file` only → line-numbered source + dependents. ' +
653
+ 'SYMBOL: body via includeCode + short trail; overloads return every body. ' +
654
+ 'USE after explore named the symbol and you still need one body. ' +
655
+ 'DO NOT crawl a feature with repeated node calls (prefer one explore). ' +
656
+ 'Treat returned source as already Read.',
586
657
  inputSchema: {
587
658
  type: 'object',
588
659
  properties: {
589
660
  symbol: {
590
661
  type: 'string',
591
- description: 'Name of the symbol to read (symbol mode). Omit it and pass `file` alone to read a whole file like Read.',
662
+ description: 'Symbol mode (required unless `file` alone). Exact name (e.g. "authenticate").',
592
663
  },
593
664
  includeCode: {
594
665
  type: 'boolean',
@@ -597,7 +668,8 @@ exports.tools = [
597
668
  },
598
669
  file: {
599
670
  type: 'string',
600
- description: 'A file path or basename (e.g. "harness.rs", "src/auth/session.ts"). Pass it ALONE (no symbol) to READ the file like the Read tool its full source with line numbers + which files depend on it. Or pass it WITH a symbol to disambiguate an overloaded name to the definition in this file.',
671
+ description: 'File mode: pass ALONE (no symbol) to read like Read — e.g. "src/auth/session.ts". ' +
672
+ 'Or with `symbol` to disambiguate an overloaded name to that file.',
601
673
  },
602
674
  offset: {
603
675
  type: 'number',
@@ -624,13 +696,16 @@ exports.tools = [
624
696
  },
625
697
  {
626
698
  name: 'homegraph_explore',
627
- description: 'PRIMARY tool for THIS REPO\'s symbol graph (call paths + often line-numbered source). CALL when you need in-repo structure: how a named feature/component is wired, A→B path, callers/callees, Type.member → who uses it, click/handler flow, in-repo @kit import usages — put concrete symbol/file/@kit names in query; skip search. If the question does not need that graph, do not call. One explore; answer from returned Source + trail; treat as already Read; do not re-grep/search the same names. Busy/partial → retry same explore once.',
699
+ description: 'PRIMARY tool for THIS REPO\'s symbol graph (call paths + often line-numbered source). Required: `query` with concrete symbol/file/@kit names. ' +
700
+ 'CALL for in-repo structure: feature wiring, A→B path, callers/callees, Type.member usage, click→handler, in-repo @kit usages. ' +
701
+ 'Skip search when names are already known. One explore; answer from Source + trail; treat as already Read. Busy/partial → retry same explore once.',
628
702
  inputSchema: {
629
703
  type: 'object',
630
704
  properties: {
631
705
  query: {
632
706
  type: 'string',
633
- description: 'In-repo symbols, file basenames, or @kit names for USAGE questions (import/call sites), not for asking the kit\'s official full API list. For flows, name both endpoints. Prefer concrete names from the question.',
707
+ description: 'Required. In-repo symbols, file basenames, or @kit names (e.g. "ParentPage onClick build", "CartRepository.addItem"). ' +
708
+ 'For flows, name both endpoints. Not for official SDK API catalogs.',
634
709
  },
635
710
  maxFiles: {
636
711
  type: 'number',
@@ -645,7 +720,7 @@ exports.tools = [
645
720
  },
646
721
  {
647
722
  name: 'homegraph_status',
648
- description: 'Index health check (files / nodes / edges). Skip unless debugging.',
723
+ description: 'Index health check (files / nodes / edges). No required args when a default project is loaded. Skip unless debugging.',
649
724
  inputSchema: {
650
725
  type: 'object',
651
726
  properties: {
@@ -656,17 +731,18 @@ exports.tools = [
656
731
  },
657
732
  {
658
733
  name: 'homegraph_files',
659
- description: 'Indexed directory tree (paths and symbol counts only — NO source code). Do NOT use to answer where/what/how code questions; use homegraph_explore. Only for coarse folder layout when explore cannot help.',
734
+ description: 'Indexed directory tree (paths and symbol counts only — NO source). ' +
735
+ 'Only for coarse folder layout when explore cannot help. For where/what/how code questions use homegraph_explore.',
660
736
  inputSchema: {
661
737
  type: 'object',
662
738
  properties: {
663
739
  path: {
664
740
  type: 'string',
665
- description: 'Filter to files under this directory path (e.g., "src/components"). Returns all files if not specified.',
741
+ description: 'Optional directory prefix filter (e.g. "src/components"). Omit to list all indexed files.',
666
742
  },
667
743
  pattern: {
668
744
  type: 'string',
669
- description: 'Filter files matching this glob pattern (e.g., "*.tsx", "**/*.test.ts")',
745
+ description: 'Optional glob filter (e.g. "*.tsx", "**/*.ets").',
670
746
  },
671
747
  format: {
672
748
  type: 'string',
@@ -690,19 +766,19 @@ exports.tools = [
690
766
  },
691
767
  {
692
768
  name: 'homegraph_spec_match',
693
- description: 'Match a new spec/feature description against the Commit4Spec knowledge graph using FTS5 full-text search. ' +
694
- 'Returns the most similar historical specs with their associated commits and code fragments. ' +
695
- 'The database defaults to .homegraph/commit4spec/commit4spec.db under the repo path.',
769
+ description: 'Match a new feature/spec description against the Commit4Spec knowledge graph (FTS5). ' +
770
+ 'Required: `query` (title + description text). Returns similar historical specs with commits/fragments. ' +
771
+ 'Needs `.homegraph/commit4spec/commit4spec.db` (from `homegraph spec build` / `mine`).',
696
772
  inputSchema: {
697
773
  type: 'object',
698
774
  properties: {
699
775
  query: {
700
776
  type: 'string',
701
- description: 'Spec text (title + description) to match against historical specs.',
777
+ description: 'Required. Spec text (title + description) to match against historical specs.',
702
778
  },
703
779
  repoPath: {
704
780
  type: 'string',
705
- description: 'Path to the repository root. Defaults to the current working directory.',
781
+ description: 'Optional repository root (default: cwd). Spec DB lives under `.homegraph/commit4spec/`.',
706
782
  },
707
783
  topK: {
708
784
  type: 'number',
@@ -721,20 +797,18 @@ exports.tools = [
721
797
  },
722
798
  {
723
799
  name: 'homegraph_spec_find',
724
- description: 'Find which specs are related to the given file path by matching against code-fragment file paths ' +
725
- 'in the Commit4Spec knowledge graph. Traverses code_fragment_nodes → commit_fragment_relations ' +
726
- '→ spec_commit_relations → spec_nodes. Useful for answering "which specs does this file affect?" ' +
727
- 'The database defaults to .homegraph/commit4spec/commit4spec.db under the repo path.',
800
+ description: 'Find which Commit4Spec specs touch a file path. Required: `filePath` (e.g. "src/auth.ts"). ' +
801
+ 'Needs `.homegraph/commit4spec/commit4spec.db`.',
728
802
  inputSchema: {
729
803
  type: 'object',
730
804
  properties: {
731
805
  filePath: {
732
806
  type: 'string',
733
- description: 'File path to look up (substring LIKE match). E.g. "src/auth.ts" or "src/auth".',
807
+ description: 'Required. File path substring to match (e.g. "src/auth.ts" or "src/auth").',
734
808
  },
735
809
  repoPath: {
736
810
  type: 'string',
737
- description: 'Path to the repository root. Defaults to the current working directory.',
811
+ description: 'Optional repository root (default: cwd). Spec DB lives under `.homegraph/commit4spec/`.',
738
812
  },
739
813
  },
740
814
  required: ['filePath'],
@@ -743,31 +817,26 @@ exports.tools = [
743
817
  },
744
818
  {
745
819
  name: 'homegraph_spec_trace',
746
- description: 'Trace a code symbol (function, method, class) back to its associated design Specs in the Commit4Spec ' +
747
- 'knowledge graph. Resolves the symbol via the HomeGraph code index, then matches against code-fragment ' +
748
- 'records in the Spec database using five-dimensional scoring: file-path match, code-diff content search ' +
749
- '(FTS5), Spec title/subtitle name match, Spec recency, and line-range overlap. ' +
750
- 'Returns ranked Specs with score breakdowns even when exact line overlap is absent — code drifts over ' +
751
- 'time, so recency and content matching compensate. ' +
752
- 'The Spec DB defaults to .homegraph/commit4spec/commit4spec.db under the repo path.',
820
+ description: 'Trace one code symbol back to related design Specs (Commit4Spec). Required: `symbol` (e.g. "authenticate"). ' +
821
+ 'Optional `file`/`line` to disambiguate. Needs code index + `.homegraph/commit4spec/commit4spec.db`.',
753
822
  inputSchema: {
754
823
  type: 'object',
755
824
  properties: {
756
825
  symbol: {
757
826
  type: 'string',
758
- description: 'Symbol name (bare or qualified). E.g. "authenticate", "AuthService.login", "auth::validate".',
827
+ description: 'Required. Symbol name (bare or qualified). E.g. "authenticate", "AuthService.login".',
759
828
  },
760
829
  file: {
761
830
  type: 'string',
762
- description: 'File path for disambiguation when multiple symbols share the same name (optional).',
831
+ description: 'Optional file path for disambiguation when multiple symbols share the same name.',
763
832
  },
764
833
  line: {
765
834
  type: 'number',
766
- description: 'Line number for disambiguation (optional).',
835
+ description: 'Optional line number for disambiguation.',
767
836
  },
768
837
  repoPath: {
769
838
  type: 'string',
770
- description: 'Path to the repository root. Defaults to the current working directory.',
839
+ description: 'Optional repository root (default: cwd).',
771
840
  },
772
841
  topK: {
773
842
  type: 'number',
@@ -1017,6 +1086,7 @@ class ToolHandler {
1017
1086
  'homegraph_explore',
1018
1087
  'homegraph_search',
1019
1088
  'homegraph_node',
1089
+ 'homegraph_diff_impact',
1020
1090
  ]);
1021
1091
  if (stats.fileCount < TINY_REPO_FILE_THRESHOLD) {
1022
1092
  visible = visible.filter(t => TINY_REPO_CORE_TOOLS.has(t.name));
@@ -1148,32 +1218,40 @@ class ToolHandler {
1148
1218
  /**
1149
1219
  * Validate that a value is a non-empty string within length bounds.
1150
1220
  *
1151
- * The `maxLength` cap protects against MCP clients that ship huge
1152
- * payloads (10MB+ query strings either by accident or maliciously).
1153
- * Without this, a single oversized input can pin the FTS5 index or
1154
- * exhaust memory before any real work runs.
1221
+ * Bad / oversize args return SUCCESS-shaped guidance (no `isError`) with a
1222
+ * retry example same policy as {@link NotIndexedError}: early `isError`
1223
+ * teaches agents to abandon the whole toolset. The length cap still blocks
1224
+ * DoS before FTS/work runs.
1155
1225
  */
1156
1226
  validateString(value, name, maxLength = MAX_INPUT_LENGTH) {
1157
1227
  if (typeof value !== 'string' || value.length === 0) {
1158
- return this.errorResult(`${name} must be a non-empty string`);
1228
+ const got = value === undefined || value === null
1229
+ ? 'it was missing'
1230
+ : typeof value !== 'string'
1231
+ ? `got ${typeof value}`
1232
+ : 'got an empty string';
1233
+ return this.badArgResult(`\`${name}\` must be a non-empty string (${got}).`, name);
1159
1234
  }
1160
1235
  if (value.length > maxLength) {
1161
- return this.errorResult(`${name} exceeds maximum length of ${maxLength} characters (got ${value.length})`);
1236
+ return this.badArgResult(`\`${name}\` exceeds maximum length of ${maxLength} characters (got ${value.length}). ` +
1237
+ 'Shorten it — pass a concrete symbol/file name, not a pasted dump.', name);
1162
1238
  }
1163
1239
  return value;
1164
1240
  }
1165
1241
  /**
1166
1242
  * Validate an optional path-like string input. Returns the value if
1167
- * valid (or undefined), or a ToolResult with the error.
1243
+ * valid (or undefined), or SUCCESS-shaped guidance when the value is present
1244
+ * but invalid (wrong type / oversize).
1168
1245
  */
1169
1246
  validateOptionalPath(value, name) {
1170
1247
  if (value === undefined || value === null)
1171
1248
  return undefined;
1172
1249
  if (typeof value !== 'string') {
1173
- return this.errorResult(`${name} must be a string`);
1250
+ return this.badArgResult(`\`${name}\` must be a string when provided (got ${typeof value}).`, name);
1174
1251
  }
1175
1252
  if (value.length > MAX_PATH_LENGTH) {
1176
- return this.errorResult(`${name} exceeds maximum length of ${MAX_PATH_LENGTH} characters (got ${value.length})`);
1253
+ return this.badArgResult(`\`${name}\` exceeds maximum length of ${MAX_PATH_LENGTH} characters (got ${value.length}). ` +
1254
+ 'Pass a normal project-relative or absolute path.', name);
1177
1255
  }
1178
1256
  return value;
1179
1257
  }
@@ -1600,6 +1678,7 @@ class ToolHandler {
1600
1678
  case 'homegraph_callers': return await this.handleCallers(args);
1601
1679
  case 'homegraph_callees': return await this.handleCallees(args);
1602
1680
  case 'homegraph_impact': return await this.handleImpact(args);
1681
+ case 'homegraph_diff_impact': return await this.handleDiffImpact(args);
1603
1682
  case 'homegraph_explore': return await this.handleExplore(args);
1604
1683
  case 'homegraph_node': return await this.handleNode(args);
1605
1684
  case 'homegraph_files': return await this.handleFiles(args);
@@ -1901,6 +1980,147 @@ class ToolHandler {
1901
1980
  }
1902
1981
  return this.textResult(this.truncateOutput(sections.join('\n') + filterNote));
1903
1982
  }
1983
+ /**
1984
+ * Handle homegraph_diff_impact — diff line ranges ∩ symbol spans → evidence pack.
1985
+ */
1986
+ async handleDiffImpact(args) {
1987
+ const resolved = (0, diff_impact_1.resolveDiffImpactHunks)({
1988
+ diff: args.diff,
1989
+ hunks: args.hunks,
1990
+ });
1991
+ if (resolved.error) {
1992
+ return this.badArgResult(resolved.error, 'diff');
1993
+ }
1994
+ if (resolved.hunks.length === 0) {
1995
+ return this.badArgResult('No file hunks found. Pass a unified `diff` with `+++` / `@@` headers, or `hunks: [{ path, startLine, endLine }]`.', 'diff');
1996
+ }
1997
+ const cg = this.getHomeGraph(args.projectPath);
1998
+ const depthRaw = Number(args.depth);
1999
+ const depth = Number.isFinite(depthRaw) ? depthRaw : 2;
2000
+ const includeSpecs = args.includeSpecs === true;
2001
+ const pack = (0, diff_impact_1.buildDiffImpactPack)(cg, resolved.hunks, {
2002
+ depth,
2003
+ notes: resolved.notes,
2004
+ });
2005
+ const relatedSpecs = [];
2006
+ if (includeSpecs) {
2007
+ const { resolveDbPath } = require('../spec/utils');
2008
+ const { createDatabase } = require('../db/sqlite-adapter');
2009
+ const { findSpecsByFilePath, findSpecsByCodeSymbol, } = require('../spec/graph/queries');
2010
+ const projectRoot = (() => {
2011
+ try {
2012
+ return cg.getProjectRoot();
2013
+ }
2014
+ catch {
2015
+ return args.projectPath || process.cwd();
2016
+ }
2017
+ })();
2018
+ const dbPath = resolveDbPath(projectRoot);
2019
+ let db = null;
2020
+ try {
2021
+ db = createDatabase(dbPath).db;
2022
+ }
2023
+ catch {
2024
+ pack.notes.push(`includeSpecs was true but Commit4Spec DB is unavailable at ${dbPath} (run \`homegraph spec build\` / \`mine\` first).`);
2025
+ }
2026
+ if (db) {
2027
+ try {
2028
+ const seenFileSpecs = new Set();
2029
+ for (const filePath of pack.changedFiles) {
2030
+ try {
2031
+ const found = findSpecsByFilePath(db, filePath);
2032
+ for (const r of found.results ?? []) {
2033
+ if (!r.id || seenFileSpecs.has(r.id))
2034
+ continue;
2035
+ seenFileSpecs.add(r.id);
2036
+ relatedSpecs.push({
2037
+ specId: r.id,
2038
+ title: r.title,
2039
+ via: 'file',
2040
+ filePath,
2041
+ });
2042
+ }
2043
+ }
2044
+ catch {
2045
+ /* ignore per-file */
2046
+ }
2047
+ }
2048
+ const forTrace = pack.changedSymbols.slice(0, diff_impact_1.DIFF_IMPACT_LIMITS.maxSpecSymbols);
2049
+ if (pack.changedSymbols.length > forTrace.length) {
2050
+ pack.notes.push(`Spec symbol-trace limited to ${diff_impact_1.DIFF_IMPACT_LIMITS.maxSpecSymbols} of ${pack.changedSymbols.length} changed symbols.`);
2051
+ }
2052
+ const seenSymKeys = new Set();
2053
+ for (const sym of forTrace) {
2054
+ try {
2055
+ const traced = findSpecsByCodeSymbol(db, {
2056
+ name: sym.name,
2057
+ qualifiedName: sym.name,
2058
+ kind: sym.kind,
2059
+ filePath: sym.filePath,
2060
+ startLine: sym.startLine,
2061
+ endLine: sym.endLine,
2062
+ }, 5);
2063
+ for (const m of traced.matches ?? []) {
2064
+ const id = m.spec?.id;
2065
+ if (!id)
2066
+ continue;
2067
+ const key = `${id}::${sym.name}`;
2068
+ if (seenSymKeys.has(key))
2069
+ continue;
2070
+ seenSymKeys.add(key);
2071
+ relatedSpecs.push({
2072
+ specId: id,
2073
+ title: m.spec.title,
2074
+ via: 'symbol',
2075
+ symbol: sym.name,
2076
+ score: typeof m.score === 'number' ? m.score : undefined,
2077
+ });
2078
+ }
2079
+ }
2080
+ catch {
2081
+ /* ignore per-symbol */
2082
+ }
2083
+ }
2084
+ }
2085
+ finally {
2086
+ try {
2087
+ db.close();
2088
+ }
2089
+ catch {
2090
+ /* ignore */
2091
+ }
2092
+ }
2093
+ }
2094
+ }
2095
+ const response = {
2096
+ ...pack,
2097
+ // Drop raw hunk ranges from default agent payload noise — keep changedFiles + symbols.
2098
+ hunks: pack.hunks.map((h) => ({
2099
+ path: h.path,
2100
+ ranges: h.ranges,
2101
+ })),
2102
+ relatedSpecs: includeSpecs ? relatedSpecs : undefined,
2103
+ };
2104
+ const json = JSON.stringify(response, null, 2);
2105
+ if (json.length <= MAX_OUTPUT_LENGTH) {
2106
+ return this.textResult(json);
2107
+ }
2108
+ const slim = {
2109
+ ...response,
2110
+ callers: response.callers.slice(0, 20),
2111
+ impactSummary: response.impactSummary.map((s) => ({
2112
+ ...s,
2113
+ sampleNames: s.sampleNames.slice(0, 3),
2114
+ })),
2115
+ uiEdges: response.uiEdges.slice(0, 15),
2116
+ relatedSpecs: response.relatedSpecs?.slice(0, 15),
2117
+ notes: [
2118
+ ...response.notes,
2119
+ 'Output trimmed to fit MCP size limit — re-run with a smaller diff if you need full lists.',
2120
+ ],
2121
+ };
2122
+ return this.textResult(this.truncateOutput(JSON.stringify(slim, null, 2)));
2123
+ }
1904
2124
  /** Whether a graph edge may be traversed by homegraph_explore's main Flow BFS. */
1905
2125
  isExploreFlowEdge(edge) {
1906
2126
  if (edge.kind === 'calls')
@@ -2097,12 +2317,21 @@ class ToolHandler {
2097
2317
  const hits = this.findAllSymbols(cg, t).nodes;
2098
2318
  const cands = hits.filter((n) => CALLABLE.has(n.kind));
2099
2319
  tokenFamily.set(t, cands);
2320
+ // Prefer in-repo callables over attached OHOS SDK stubs (`ohos-sdk:…`).
2321
+ // Lifecycle names like `aboutToAppear` / `build` collide with dozens of
2322
+ // API defs; counting those as ambiguity empties co-naming and drops the
2323
+ // project method the agent actually meant (ArkTS explore Flow regression).
2324
+ // Do NOT fall back to the full pool when co-naming empties — that would
2325
+ // pin a polymorphic name like `execute` (9+ impls) onto the Flow spine
2326
+ // and silence the Interface-dispatch announcement.
2327
+ const projectCands = cands.filter((n) => !(0, arkts_1.isOhosApiFilePath)(n.filePath));
2328
+ const pool = projectCands.length > 0 ? projectCands : cands;
2100
2329
  // A qualified or otherwise-specific name (<=3 hits) keeps all; an
2101
2330
  // ambiguous simple name keeps only candidates whose container is named.
2102
- const specific = cands.length <= 3;
2331
+ const specific = pool.length <= 3;
2103
2332
  const pick = specific
2104
- ? cands
2105
- : cands.filter((n) => {
2333
+ ? pool
2334
+ : pool.filter((n) => {
2106
2335
  const segs = (n.qualifiedName || '').toLowerCase().split(/::|\./).filter(Boolean);
2107
2336
  const container = segs.length >= 2 ? segs[segs.length - 2] : '';
2108
2337
  return !!container && segPool.has(container);
@@ -5946,6 +6175,12 @@ class ToolHandler {
5946
6175
  if (!symbolRaw && fileHint) {
5947
6176
  return this.handleFileView(cg, fileHint, { offset, limit, symbolsOnly });
5948
6177
  }
6178
+ if (!symbolRaw && !fileHint) {
6179
+ return this.badArgResult('`homegraph_node` needs either `symbol` (symbol mode) or `file` alone (file mode). Both were missing.', 'symbol', {
6180
+ symbol: 'authenticate',
6181
+ includeCode: true,
6182
+ });
6183
+ }
5949
6184
  const symbol = this.validateString(args.symbol, 'symbol');
5950
6185
  if (typeof symbol !== 'string')
5951
6186
  return symbol;
@@ -7111,6 +7346,21 @@ class ToolHandler {
7111
7346
  content: [{ type: 'text', text }],
7112
7347
  };
7113
7348
  }
7349
+ /**
7350
+ * Recoverable bad-argument guidance: SUCCESS-shaped (no `isError`) so agents
7351
+ * retry with fixed args instead of abandoning HomeGraph for the session.
7352
+ * Reserved `isError` cases stay in {@link errorResult} (security / real faults).
7353
+ */
7354
+ badArgResult(problem, argName, exampleOverride) {
7355
+ const example = exampleOverride ??
7356
+ { [argName]: BAD_ARG_EXAMPLES[argName] ?? '…' };
7357
+ return this.textResult(`${problem}\n\n` +
7358
+ 'This is not a HomeGraph failure — fix the arguments and retry the same tool.\n' +
7359
+ 'Example:\n' +
7360
+ '```json\n' +
7361
+ `${JSON.stringify(example, null, 2)}\n` +
7362
+ '```');
7363
+ }
7114
7364
  errorResult(message) {
7115
7365
  return {
7116
7366
  content: [{ type: 'text', text: `Error: ${message}` }],