homegraph 1.6.1 → 1.6.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 (40) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/dist/bin/homegraph.js +2 -2
  3. package/dist/db/index.js +1 -0
  4. package/dist/db/migrations.d.ts +6 -1
  5. package/dist/db/migrations.js +26 -1
  6. package/dist/db/queries.d.ts +5 -0
  7. package/dist/db/queries.js +17 -7
  8. package/dist/db/schema.sql +4 -0
  9. package/dist/extraction/grammars.js +2 -1
  10. package/dist/extraction/languages/c-cpp.d.ts +8 -0
  11. package/dist/extraction/languages/c-cpp.js +44 -0
  12. package/dist/extraction/wasm-runtime-flags.d.ts +4 -0
  13. package/dist/extraction/wasm-runtime-flags.js +9 -0
  14. package/dist/index.js +7 -1
  15. package/dist/installer/targets/icode.d.ts +43 -0
  16. package/dist/installer/targets/icode.js +631 -0
  17. package/dist/installer/targets/registry.js +2 -0
  18. package/dist/installer/targets/types.d.ts +1 -1
  19. package/dist/mcp/evidence-rendering.js +1 -1
  20. package/dist/mcp/explore-repeat-guard.d.ts +19 -5
  21. package/dist/mcp/explore-repeat-guard.js +35 -14
  22. package/dist/mcp/explore-session-state.d.ts +5 -0
  23. package/dist/mcp/explore-session-state.js +1 -0
  24. package/dist/mcp/locate-contract.js +1 -1
  25. package/dist/mcp/server-instructions.d.ts +2 -2
  26. package/dist/mcp/server-instructions.js +10 -10
  27. package/dist/mcp/session.js +3 -2
  28. package/dist/mcp/tools.d.ts +21 -2
  29. package/dist/mcp/tools.js +109 -50
  30. package/dist/resolution/c-feature-variants.d.ts +40 -0
  31. package/dist/resolution/c-feature-variants.js +272 -0
  32. package/dist/resolution/cpp-macro-visibility.d.ts +21 -0
  33. package/dist/resolution/cpp-macro-visibility.js +393 -0
  34. package/dist/resolution/index.js +11 -0
  35. package/dist/resolution/name-matcher.js +49 -2
  36. package/dist/resolution/types.d.ts +8 -0
  37. package/dist/resolution/types.js +8 -0
  38. package/dist/sync/watcher.d.ts +30 -0
  39. package/dist/sync/watcher.js +45 -0
  40. package/package.json +1 -1
package/dist/mcp/tools.js CHANGED
@@ -4,6 +4,7 @@ exports.ToolHandler = exports.DEFAULT_MCP_TOOL_SHORT_NAMES = exports.tools = exp
4
4
  exports.__setLoadHomeGraphForTests = __setLoadHomeGraphForTests;
5
5
  exports.normalizeQuerySpelling = normalizeQuerySpelling;
6
6
  exports.getExploreBudget = getExploreBudget;
7
+ exports.formatExploreCallBudgetNote = formatExploreCallBudgetNote;
7
8
  exports.getExploreOutputBudget = getExploreOutputBudget;
8
9
  exports.tightenExploreBudgetForQuery = tightenExploreBudgetForQuery;
9
10
  exports.exploreLocatorDigestEnabled = exploreLocatorDigestEnabled;
@@ -16,6 +17,8 @@ exports.formatStaleBanner = formatStaleBanner;
16
17
  exports.formatStaleFooter = formatStaleFooter;
17
18
  exports.formatDegradedBanner = formatDegradedBanner;
18
19
  exports.resolveMcpToolAllowlist = resolveMcpToolAllowlist;
20
+ exports.shortMcpToolNamesEnabled = shortMcpToolNamesEnabled;
21
+ exports.canonicalizeMcpToolName = canonicalizeMcpToolName;
19
22
  exports.getStaticTools = getStaticTools;
20
23
  exports.extractBareUsageSymbols = extractBareUsageSymbols;
21
24
  exports.queryAsBareSymbolInventory = queryAsBareSymbolInventory;
@@ -309,6 +312,14 @@ function getExploreBudget(fileCount) {
309
312
  return 4;
310
313
  return 5;
311
314
  }
315
+ /**
316
+ * Cooldown shown on `homegraph_explore` when the project is indexed.
317
+ * Matches {@link exploreCooldownSeconds}. Output tiers stay on {@link getExploreBudget}.
318
+ */
319
+ function formatExploreCallBudgetNote(fileCount) {
320
+ const files = Math.max(0, fileCount).toLocaleString();
321
+ return `Wait ${(0, explore_repeat_guard_1.exploreCooldownSeconds)()} seconds after homegraph_explore returns before calling it again (${files} files indexed).`;
322
+ }
312
323
  function getExploreOutputBudget(fileCount) {
313
324
  // Tiered budget, scaled to project size. The budget is a CEILING (relevance
314
325
  // still gates WHAT is included), and it MUST stay under the agent's INLINE
@@ -884,11 +895,17 @@ function formatDegradedBanner(reason) {
884
895
  (reason ? `\n Reason: ${reason}` : ''));
885
896
  }
886
897
  /**
887
- * Common projectPath property for cross-project queries
898
+ * projectPath property descriptions — kept in sync with whether the field is
899
+ * in `inputSchema.required`. The optional text is the module-level default
900
+ * (session has a bound root). {@link withRequiredProjectPath} swaps in the
901
+ * required text when there is no default project, so schema and prose agree.
888
902
  */
903
+ const PROJECT_PATH_DESC_OPTIONAL = 'Absolute path to the project to query (or any directory inside it) — homegraph uses the nearest .homegraph/ index at or above that path. Omit to use this session\'s default project. Pass it to query a second codebase, or when the server root has no index of its own (e.g. a monorepo where only sub-projects are indexed, so there is no default project).';
904
+ const PROJECT_PATH_DESC_REQUIRED = 'Required. Absolute path to the project to query (or any directory inside it) — homegraph uses the nearest .homegraph/ index at or above that path. This session has no default project, so every call must pass projectPath (e.g. a monorepo sub-project with its own .homegraph/, or any already-indexed folder).';
905
+ /** Common projectPath property for cross-project queries (optional / default-project sessions). */
889
906
  const projectPathProperty = {
890
907
  type: 'string',
891
- description: 'Absolute path to the project to query (or any directory inside it) — homegraph uses the nearest .homegraph/ index at or above that path. Omit to use this session\'s default project. Pass it to query a second codebase, or when the server root has no index of its own (e.g. a monorepo where only sub-projects are indexed, so there is no default project).',
908
+ description: PROJECT_PATH_DESC_OPTIONAL,
892
909
  };
893
910
  /**
894
911
  * EVERY homegraph tool is query-only: it reads the pre-built index and never
@@ -1181,15 +1198,15 @@ exports.tools = [
1181
1198
  },
1182
1199
  {
1183
1200
  name: 'homegraph_explore',
1184
- description: 'Optional graph evidence for a concrete unresolved cross-symbol mechanism in THIS repo. Required: `query`. ' +
1201
+ description: 'Default locator when the task touches existing code in THIS repo. Required: `query`. ' +
1202
+ 'Open alone (no parallel grep/glob/read in the same step) with feature/component/symbol keywords; `taskContext` = the requirement. ' +
1185
1203
  'For module/route-profile **paths** and engineering overview use homegraph_project first (not this tool). ' +
1186
- 'Use ordinary bash/search/read for paths, symbols, literal strings and local changes; continue editing when that evidence suffices. ' +
1187
- 'Do not call for routine pre-edit orientation or merely because implementation is difficult. ' +
1204
+ 'Do NOT skip because the index might still be building — call anyway. ' +
1188
1205
  'Located ArkTS code may include render scope, imported types, module configuration, indexed SDK signatures and control-state gaps. ' +
1189
1206
  'Returns call paths and compact line-numbered source (Harmony route_map queries may lead with Registration sources; form/shortcuts queries may lead with Capability profiles; element/string.json literals may lead with Resource hits + optional bound .ets anchors; Seam notes may flag stubs). ArkTS symbol evidence uses complete declarations and bounded directed paths with intermediate source dependencies; explicit Gaps and stop reasons name omitted or unverified evidence. Qualify ambiguous symbols by owning type or file. State the missing relation with known anchors, requested action, scope and constraints; taskContext can carry the full task. ' +
1190
1207
  'Reuse unchanged complete ranges; refresh missing, edited or truncated evidence. ' +
1191
1208
  'No new evidence → change to a targeted source inspection, not a paraphrased explore. ' +
1192
- 'Partial/busy → at most one focused recovery for the named gap; budgets are ceilings, not required calls. ' +
1209
+ `After this tool returns, wait ${(0, explore_repeat_guard_1.exploreCooldownSeconds)()} seconds before calling it again. ` +
1193
1210
  'Retrieval completion is not task completion: continue implementation and required validation.',
1194
1211
  inputSchema: {
1195
1212
  type: 'object',
@@ -1390,11 +1407,12 @@ exports.tools = [
1390
1407
  },
1391
1408
  ];
1392
1409
  /**
1393
- * Return `defs` with `projectPath` marked `required` in each tool's inputSchema.
1410
+ * Return `defs` with `projectPath` marked `required` in each tool's inputSchema
1411
+ * and with a matching property description (no "Omit…" wording).
1394
1412
  *
1395
1413
  * Used for the NO-DEFAULT-PROJECT tool surface (issue #993): when the MCP server
1396
1414
  * has no default project to fall back to — a gateway server started outside any
1397
- * repo, or a monorepo root whose `.codegraph/` indexes live only in sub-projects
1415
+ * repo, or a monorepo root whose `.homegraph/` indexes live only in sub-projects
1398
1416
  * — every call MUST carry an explicit `projectPath`, so the schema should say so.
1399
1417
  * A `required` field is a HIGH-salience channel (MCP clients surface and often
1400
1418
  * validate it), unlike the instructions text the reporter found too weak to stop
@@ -1403,20 +1421,31 @@ exports.tools = [
1403
1421
  *
1404
1422
  * Pure: clones each tool's schema rather than mutating the shared module-level
1405
1423
  * `tools` array (reused by every session and the static surface). A tool that
1406
- * doesn't expose projectPath, or already requires it, is returned untouched;
1407
- * explore's `['query']` becomes `['query', 'projectPath']`, and a tool with no
1408
- * `required` list (status/files) gains `['projectPath']`.
1424
+ * doesn't expose projectPath is returned untouched; explore's `['query']`
1425
+ * becomes `['query', 'projectPath']`, and a tool with no `required` list
1426
+ * (status/files) gains `['projectPath']`. The property description is always
1427
+ * swapped to {@link PROJECT_PATH_DESC_REQUIRED} so it cannot contradict
1428
+ * `required`.
1409
1429
  */
1410
1430
  function withRequiredProjectPath(defs) {
1411
1431
  return defs.map((tool) => {
1412
- if (!tool.inputSchema.properties.projectPath)
1432
+ const prop = tool.inputSchema.properties.projectPath;
1433
+ if (!prop)
1413
1434
  return tool;
1414
1435
  const required = tool.inputSchema.required ?? [];
1415
- if (required.includes('projectPath'))
1416
- return tool;
1436
+ const nextRequired = required.includes('projectPath')
1437
+ ? required
1438
+ : [...required, 'projectPath'];
1417
1439
  return {
1418
1440
  ...tool,
1419
- inputSchema: { ...tool.inputSchema, required: [...required, 'projectPath'] },
1441
+ inputSchema: {
1442
+ ...tool.inputSchema,
1443
+ properties: {
1444
+ ...tool.inputSchema.properties,
1445
+ projectPath: { ...prop, description: PROJECT_PATH_DESC_REQUIRED },
1446
+ },
1447
+ required: nextRequired,
1448
+ },
1420
1449
  };
1421
1450
  });
1422
1451
  }
@@ -1442,6 +1471,40 @@ function filterToolsByAllowlist(defs, allow) {
1442
1471
  return defs;
1443
1472
  return defs.filter((t) => allow.has(t.name.replace(/^homegraph_/, '')));
1444
1473
  }
1474
+ /**
1475
+ * Product hosts that prefix MCP tools with the server name (e.g. DevEco Code
1476
+ * → `homegraph_` + tool name) set `HOMEGRAPH_MCP_SHORT_TOOL_NAMES=1` so
1477
+ * tools/list exposes `explore` / `project` instead of `homegraph_explore`.
1478
+ * After the host prefix, the model-visible name is still `homegraph_explore`.
1479
+ * Cursor / Claude leave this unset and keep the canonical `homegraph_*` names.
1480
+ */
1481
+ function shortMcpToolNamesEnabled(raw = process.env.HOMEGRAPH_MCP_SHORT_TOOL_NAMES) {
1482
+ const v = (raw ?? '').trim().toLowerCase();
1483
+ return v === '1' || v === 'true' || v === 'yes';
1484
+ }
1485
+ const CANONICAL_TOOL_NAMES = new Set(exports.tools.map((t) => t.name));
1486
+ /**
1487
+ * Map a tools/call name to the canonical `homegraph_*` form.
1488
+ * Accepts both short (`explore`) and long (`homegraph_explore`) so hosts that
1489
+ * list short names (or still pass the long name) both work.
1490
+ */
1491
+ function canonicalizeMcpToolName(name) {
1492
+ if (!name)
1493
+ return name;
1494
+ if (name.startsWith('homegraph_'))
1495
+ return name;
1496
+ const canonical = `homegraph_${name}`;
1497
+ return CANONICAL_TOOL_NAMES.has(canonical) ? canonical : name;
1498
+ }
1499
+ /** Clone defs with `homegraph_` stripped from each tool name (list surface only). */
1500
+ function withShortToolNames(defs) {
1501
+ if (!shortMcpToolNamesEnabled())
1502
+ return defs;
1503
+ return defs.map((t) => ({
1504
+ ...t,
1505
+ name: t.name.replace(/^homegraph_/, ''),
1506
+ }));
1507
+ }
1445
1508
  /**
1446
1509
  * Allowlist-filtered tool definitions WITHOUT an engine — the static surface the
1447
1510
  * proxy answers `tools/list` with before any project is open. Mirrors
@@ -1449,7 +1512,7 @@ function filterToolsByAllowlist(defs, allow) {
1449
1512
  * note in a description only adds once `cg` is loaded; the schemas are static).
1450
1513
  */
1451
1514
  function getStaticTools() {
1452
- return filterToolsByAllowlist(exports.tools, resolveMcpToolAllowlist());
1515
+ return withShortToolNames(filterToolsByAllowlist(exports.tools, resolveMcpToolAllowlist()));
1453
1516
  }
1454
1517
  /** Prose that reads like an identifier but never names a symbol worth scanning. */
1455
1518
  const BARE_USAGE_STOPWORDS = new Set([
@@ -1748,7 +1811,7 @@ class ToolHandler {
1748
1811
  // IS open we leave projectPath optional (below): a bare call falls back to
1749
1812
  // it, exactly as in the common single-project launch.
1750
1813
  if (!this.cg)
1751
- return withRequiredProjectPath(visible);
1814
+ return withShortToolNames(withRequiredProjectPath(visible));
1752
1815
  try {
1753
1816
  const stats = this.cg.getStats();
1754
1817
  // Tiny-repo tool gating applies only when the host asked for the full
@@ -1771,7 +1834,7 @@ class ToolHandler {
1771
1834
  if (askedForFullCatalog && stats.fileCount < TINY_REPO_FILE_THRESHOLD) {
1772
1835
  visible = visible.filter(t => TINY_REPO_CORE_TOOLS.has(t.name));
1773
1836
  }
1774
- return visible.map(tool => {
1837
+ return withShortToolNames(visible.map(tool => {
1775
1838
  if (tool.name === 'homegraph_explore') {
1776
1839
  return {
1777
1840
  ...tool,
@@ -1779,10 +1842,10 @@ class ToolHandler {
1779
1842
  };
1780
1843
  }
1781
1844
  return tool;
1782
- });
1845
+ }));
1783
1846
  }
1784
1847
  catch {
1785
- return visible;
1848
+ return withShortToolNames(visible);
1786
1849
  }
1787
1850
  }
1788
1851
  /**
@@ -1798,12 +1861,12 @@ class ToolHandler {
1798
1861
  * guidance so the frozen description and the live call-time response tell
1799
1862
  * the same story: failed build → say so (indexing is the user's call, so
1800
1863
  * the agent relays it); still building → point at homegraph_project and
1801
- * retry; genuinely empty → honest empty note. Only fileCount > 0 carries a
1802
- * call budget — there is nothing to budget over an unbuilt index.
1864
+ * retry; genuinely empty → honest empty note. Only fileCount > 0 carries the
1865
+ * return cooldown — there is nothing to cool down over an unbuilt index.
1803
1866
  */
1804
1867
  exploreSuffix(fileCount) {
1805
1868
  if (fileCount > 0) {
1806
- return `Budget: make at most ${getExploreBudget(fileCount)} calls for this project (${fileCount.toLocaleString()} files indexed).`;
1869
+ return formatExploreCallBudgetNote(fileCount);
1807
1870
  }
1808
1871
  const cg = this.cg;
1809
1872
  if (cg) {
@@ -2262,6 +2325,8 @@ class ToolHandler {
2262
2325
  * it (the CLI does) and explore behaves exactly as before, untracked.
2263
2326
  */
2264
2327
  async execute(toolName, args, sessionState) {
2328
+ // Short list names (`explore`) and canonical (`homegraph_explore`) both work.
2329
+ toolName = canonicalizeMcpToolName(toolName);
2265
2330
  const requestStartedAt = Date.now();
2266
2331
  const result = await this.executeCore(toolName, args, sessionState, requestStartedAt);
2267
2332
  this.traceToolCall(toolName, args, result, requestStartedAt);
@@ -3911,7 +3976,7 @@ class ToolHandler {
3911
3976
  '',
3912
3977
  ...notes,
3913
3978
  '',
3914
- '> These sites choose their call target at runtime (registry / bus / reflection) — the site shown IS where the flow continues. To follow it, run homegraph_explore or homegraph_node on a candidate; source for the sites above is included below.',
3979
+ '> These sites choose their call target at runtime (registry / bus / reflection) — the site shown IS where the flow continues. To follow it, run homegraph_explore on a candidate; source for the sites above is included below.',
3915
3980
  '',
3916
3981
  ].join('\n');
3917
3982
  }
@@ -5152,7 +5217,7 @@ class ToolHandler {
5152
5217
  else {
5153
5218
  lines.push('> **Partial locator** — related writers / sibling Managers above are anchors, not a full answer. ' +
5154
5219
  'ONE tighter explore with a named Type, or ONE narrow Grep for `@ohos` if still missing — ' +
5155
- 'do **not** fan out `homegraph_node` / callers. Do **not** ANSWER NOW from local cache helpers.');
5220
+ 'do **not** fan out callers. Do **not** ANSWER NOW from local cache helpers.');
5156
5221
  }
5157
5222
  lines.push('');
5158
5223
  return { section: lines.join('\n'), edgeCount, sdkImportCount, strongCount };
@@ -5257,7 +5322,7 @@ class ToolHandler {
5257
5322
  '**Event types → Manager / handler sites**',
5258
5323
  '',
5259
5324
  '> **Partial locator** — no event Type/enum exports indexed for the named anchors. '
5260
- + '`homegraph_node` / ONE explore on the Event Type file, or ONE Grep for enum members / '
5325
+ + 'ONE explore on the Event Type file, or ONE Grep for enum members / '
5261
5326
  + '`case` branches — not a repo-wide storm. Do **not** ANSWER NOW.',
5262
5327
  '',
5263
5328
  ].join('\n'),
@@ -5498,7 +5563,7 @@ class ToolHandler {
5498
5563
  lines.push('- No Manager import/use sites indexed for these event types.');
5499
5564
  lines.push('');
5500
5565
  lines.push('> **Partial locator** — event anchors above only. '
5501
- + '`homegraph_node` the Event file or ONE Grep for handler/`case` branches — do **not** ANSWER NOW as a full dispatch map.');
5566
+ + 'ONE Grep for handler/`case` branches — do **not** ANSWER NOW as a full dispatch map.');
5502
5567
  }
5503
5568
  else {
5504
5569
  for (const h of ranked.slice(0, 24)) {
@@ -5524,7 +5589,7 @@ class ToolHandler {
5524
5589
  }
5525
5590
  else {
5526
5591
  lines.push('> **Partial locator** — handler files are anchors, but enum-member coverage may be incomplete. '
5527
- + '`homegraph_node` the Event Type / ONE Grep for members or `case` — do **not** repo-storm; do **not** ANSWER NOW as a full map.');
5592
+ + 'ONE Grep for members or `case` — do **not** repo-storm; do **not** ANSWER NOW as a full map.');
5528
5593
  }
5529
5594
  }
5530
5595
  lines.push('');
@@ -5678,13 +5743,13 @@ class ToolHandler {
5678
5743
  const hasExtends = extendsHits.length > 0;
5679
5744
  if (!hasGraph && !hasExtends) {
5680
5745
  lines.push('> **Partial locator** — Type files above are anchors. '
5681
- + '`homegraph_node` / `homegraph_callees` one Type at a time; do **not** ANSWER NOW as a full graph.');
5746
+ + '`homegraph_callees` one Type at a time; do **not** ANSWER NOW as a full graph.');
5682
5747
  }
5683
5748
  else {
5684
5749
  lines.push('> Multi-Type dependency inventory — **ANSWER NOW** from locations'
5685
5750
  + (hasExtends ? ' + `extends` bases' : '')
5686
5751
  + (hasGraph ? ' + import/caller edges' : '')
5687
- + ' above; only `homegraph_node` one Type if a body detail is still missing.');
5752
+ + ' above; one tighter explore if a body detail is still missing.');
5688
5753
  }
5689
5754
  lines.push('');
5690
5755
  return { section: lines.join('\n'), hitCount: locs.length + edges.length + extendsHits.length };
@@ -6264,9 +6329,9 @@ class ToolHandler {
6264
6329
  soft
6265
6330
  ? `Named-Type dependency inventory — **${multi.hitCount}** locator hit(s). `
6266
6331
  + '**Partial locator** — locations above; ONE tighter explore with a named Type, '
6267
- + 'or ONE narrow Grep — do **not** fan out `homegraph_node` / callees.'
6332
+ + 'or ONE narrow Grep — do **not** fan out callees.'
6268
6333
  : `Named-Type dependency inventory — **${multi.hitCount}** locator hit(s). `
6269
- + '**ANSWER NOW** from locations + cross links; one `homegraph_node` only if a body detail is still missing.',
6334
+ + '**ANSWER NOW** from locations + cross links; one tighter explore only if a body detail is still missing.',
6270
6335
  '',
6271
6336
  multi.section,
6272
6337
  ].join('\n'));
@@ -6909,7 +6974,7 @@ class ToolHandler {
6909
6974
  : '> **Partial locator** — Manager / domain inventory below are anchors, not a full mechanism closure. '
6910
6975
  + 'Pick **ONE** concrete `*Manager` / next-hop name from the list for a tighter explore — '
6911
6976
  + 'do **not** re-explore a paraphrase of the same keyword bag. '
6912
- + 'At most one `homegraph_node` after Partial; prefer ONE narrow Grep for unlisted wiring — not a repo storm. '
6977
+ + 'Prefer ONE narrow Grep for unlisted wiring — not a repo storm. '
6913
6978
  + 'Do **not** ANSWER NOW yet.',
6914
6979
  '',
6915
6980
  `Mechanism anchors: **${seedIds.size}** symbol(s) — lightweight explore (seed + flow spine).`,
@@ -7132,7 +7197,7 @@ class ToolHandler {
7132
7197
  lines.push('---');
7133
7198
  if (mechanismClosed || (hasFlowPath && topIsStrong)) {
7134
7199
  lines.push('> **Mechanism explore complete — ANSWER NOW** from the Manager inventory + spine + digests above. '
7135
- + 'Do **not** call `homegraph_node`, Read, or Grep for symbols/files already shown. '
7200
+ + 'Do **not** Read or Grep for symbols/files already shown. '
7136
7201
  + 'Only one tighter `homegraph_explore` if a **named** Type essential to the answer is still missing.');
7137
7202
  }
7138
7203
  else if (softClose) {
@@ -8293,7 +8358,7 @@ class ToolHandler {
8293
8358
  }
8294
8359
  lines.push('---');
8295
8360
  lines.push('> **Compact local explore complete — ANSWER NOW.** ' +
8296
- 'Do **not** Read/Grep/`homegraph_search`/`homegraph_explore`/`homegraph_node` for the same symbols.');
8361
+ 'Do **not** Read/Grep/`homegraph_search`/`homegraph_explore` for the same symbols.');
8297
8362
  lines.push('');
8298
8363
  return this.textResult(lines.join('\n'));
8299
8364
  }
@@ -10021,14 +10086,9 @@ class ToolHandler {
10021
10086
  const sessionPrior = (0, explore_session_state_1.viewForProject)((0, explore_session_state_1.readExploreSessionView)(args), projectRoot);
10022
10087
  const repeat = (0, explore_repeat_guard_1.decideExploreRepeat)(sessionPrior, query);
10023
10088
  if (!plan && this.shouldRefuseRepeatedEvidence(repeat, projectRoot)) {
10024
- const text = (0, explore_repeat_guard_1.formatExploreRepeatRefuse)(repeat, query);
10025
- return this.exploreResult(text, {
10026
- projectRoot,
10027
- query,
10028
- files: [],
10029
- sourceBytes: 0,
10030
- responseBytes: text.length,
10031
- });
10089
+ // Do not record this refusal. A recorded return would restart the cooldown
10090
+ // and a client retrying inside the window would never get through.
10091
+ return this.textResult((0, explore_repeat_guard_1.formatExploreRepeatRefuse)(repeat, query));
10032
10092
  }
10033
10093
  // A planned step already tried fast paths once, before entering full explore.
10034
10094
  const compactLocal = plan ? null : this.tryFastInventoryExplore(cg, query, projectRoot)
@@ -11220,9 +11280,9 @@ class ToolHandler {
11220
11280
  lines.push(exploreLocatorDigestEnabled()
11221
11281
  ? '> Spine / top-hit digests — treat as already Read. '
11222
11282
  + 'Answer from Flow + Anchors + digests when you can; ONE narrow Grep for residual unindexed wiring. '
11223
- + 'Do not re-dump these files via Read/`homegraph_node`.'
11283
+ + 'Do not re-dump these files via Read.'
11224
11284
  : '> Line-numbered source — treat as already Read. Answer from Flow + Source below when you can; ' +
11225
- 'do not re-read/grep these files, and do not fan out `homegraph_node` / search for symbols already shown. ' +
11285
+ 'do not re-read/grep these files, and do not fan out search for symbols already shown. ' +
11226
11286
  'Another `homegraph_explore` with tighter names only if a needed symbol is missing.');
11227
11287
  lines.push('');
11228
11288
  for (const [filePath, group] of filesToRender) {
@@ -11941,16 +12001,15 @@ class ToolHandler {
11941
12001
  }
11942
12002
  else if (anyFileTrimmed) {
11943
12003
  lines.push('');
11944
- lines.push(`> Some file sections were trimmed for size. For a specific symbol you still need, run another \`homegraph_explore\` (or \`homegraph_node\`) with its exact name — line-numbered source, cheaper and more complete than Read.`);
12004
+ lines.push(`> Some file sections were trimmed for size. For a specific symbol you still need, run another \`homegraph_explore\` with its exact name — line-numbered source, cheaper and more complete than Read.`);
11945
12005
  }
11946
12006
  // Add explore budget note based on project size
11947
12007
  if (budget.includeBudgetNote) {
11948
12008
  try {
11949
12009
  const stats = cg.getStats();
11950
- const callBudget = getExploreBudget(stats.fileCount);
11951
12010
  lines.push('');
11952
- lines.push(`> **Usually one explore is enough** (budget ≤${callBudget} for ${stats.fileCount.toLocaleString()} files). ` +
11953
- 'Answer from Flow + Source above. Extra explore/`homegraph_node`/Read for the **same** symbols multiplies tokens — ' +
12011
+ lines.push(`> **Usually one explore is enough** (wait ${(0, explore_repeat_guard_1.exploreCooldownSeconds)()}s after this result before another \`homegraph_explore\`; ${stats.fileCount.toLocaleString()} files). ` +
12012
+ 'Answer from Flow + Source above. Extra explore/Read for the **same** symbols multiplies tokens — ' +
11954
12013
  'only call again for a missing **named** symbol.');
11955
12014
  }
11956
12015
  catch {
@@ -12028,7 +12087,7 @@ class ToolHandler {
12028
12087
  if (!finalText.includes('**Partial result**') && !finalText.includes('> **Explore complete**')) {
12029
12088
  finalText +=
12030
12089
  '\n\n> **Explore complete — ANSWER NOW.** Flow + Source above are authoritative for this query. ' +
12031
- 'Do **not** grep/read/`homegraph_node`/`homegraph_search` for the same symbols or files (multiplies tokens). ' +
12090
+ 'Do **not** grep/read/`homegraph_search` for the same symbols or files (multiplies tokens). ' +
12032
12091
  'Only call one tighter `homegraph_explore` if a named symbol essential to the answer is missing.';
12033
12092
  }
12034
12093
  // Curated header (#1046): substitute the sentinel with the count of files
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Is a C function defined only for a build that turns on a flag nothing in
3
+ * the project turns on? lua-cjson's fpconv.h defines `static inline void
4
+ * fpconv_init()` under `#ifdef USE_INTERNAL_FPCONV` and otherwise declares it
5
+ * `extern`, for fpconv.c to define. Nothing in redis's tree sets
6
+ * USE_INTERNAL_FPCONV, so its build compiles fpconv.c's.
7
+ *
8
+ * The name matcher asks this only when choosing among definitions of one
9
+ * name, where a call has to reach one of them: of a flag nothing sets, the
10
+ * build to follow is the one that leaves it off. Whether a macro is visible
11
+ * at a call (./cpp-macro-visibility) keeps such a flag unknown instead, since
12
+ * there an unknown flag must not suppress anything.
13
+ *
14
+ * A branch is off only when flags decide its test: `#ifdef X`, `#ifndef X`,
15
+ * `#if defined(X)`, `#if X`, `#if 0`, with `!`, `&&`, `||` and parentheses,
16
+ * and the `#elif` and `#else` after them. A flag is set nowhere when the
17
+ * definition's own file doesn't `#define` it, no build file in the
18
+ * directories from the project root down to the definition or the call names
19
+ * it (a Makefile's `-DX`, CMake's `option(X …)`, configure's `AC_DEFINE(X)`)
20
+ * and no C or C++ file of their components does — a component being the
21
+ * tree under the nearest directory with a build file, redis's `deps/lua/src`
22
+ * for fpconv.h. A macro of the compiler or the platform — a reserved name
23
+ * like `_WIN32` or `__linux__`, or `WIN32`, `linux`, `unix` — is unknown, and
24
+ * so is any other test (`#if LEVEL > 1`).
25
+ */
26
+ import type { Node } from '../types';
27
+ import type { ResolutionContext } from './types';
28
+ export declare function clearCFeatureVariantMemos(context: ResolutionContext): void;
29
+ /**
30
+ * Is `fn`'s definition compiled only by a build that sets a flag nothing in
31
+ * the project sets, for the call at `ref`? A branch around both the
32
+ * definition and the call, in the call's own file, decides nothing.
33
+ */
34
+ export declare function isCFeatureVariant(fn: Node, ref: {
35
+ filePath: string;
36
+ line: number;
37
+ }, context: ResolutionContext): boolean;
38
+ /** Is line `line` of `path` compiled only by a build that sets a flag nothing in the project sets? */
39
+ export declare function isOffByDefault(path: string, line: number, context: ResolutionContext): boolean;
40
+ //# sourceMappingURL=c-feature-variants.d.ts.map