homegraph 1.5.2 → 1.5.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.
Files changed (150) hide show
  1. package/CHANGELOG.md +131 -0
  2. package/README.md +15 -8
  3. package/dist/arkui/index.d.ts +4 -0
  4. package/dist/arkui/index.d.ts.map +1 -0
  5. package/dist/arkui/index.js +23 -0
  6. package/dist/arkui/index.js.map +1 -0
  7. package/dist/arkui/migrate-passage.d.ts +28 -0
  8. package/dist/arkui/migrate-passage.d.ts.map +1 -0
  9. package/dist/arkui/migrate-passage.js +310 -0
  10. package/dist/arkui/migrate-passage.js.map +1 -0
  11. package/dist/arkui/migrate-semantics.d.ts +47 -0
  12. package/dist/arkui/migrate-semantics.d.ts.map +1 -0
  13. package/dist/arkui/migrate-semantics.js +229 -0
  14. package/dist/arkui/migrate-semantics.js.map +1 -0
  15. package/dist/arkui/migrate-snapshot.d.ts +79 -0
  16. package/dist/arkui/migrate-snapshot.d.ts.map +1 -0
  17. package/dist/arkui/migrate-snapshot.js +340 -0
  18. package/dist/arkui/migrate-snapshot.js.map +1 -0
  19. package/dist/bin/fatal-handler.js +2 -2
  20. package/dist/bin/homegraph.js +105 -156
  21. package/dist/bin/homegraph.js.map +1 -1
  22. package/dist/bin/node-version-check.d.ts +6 -15
  23. package/dist/bin/node-version-check.d.ts.map +1 -1
  24. package/dist/bin/node-version-check.js +8 -37
  25. package/dist/bin/node-version-check.js.map +1 -1
  26. package/dist/db/index.d.ts +36 -1
  27. package/dist/db/index.d.ts.map +1 -1
  28. package/dist/db/index.js +92 -6
  29. package/dist/db/index.js.map +1 -1
  30. package/dist/db/migrations.d.ts +1 -1
  31. package/dist/db/migrations.d.ts.map +1 -1
  32. package/dist/db/migrations.js +15 -1
  33. package/dist/db/migrations.js.map +1 -1
  34. package/dist/db/queries.d.ts +42 -0
  35. package/dist/db/queries.d.ts.map +1 -1
  36. package/dist/db/queries.js +177 -10
  37. package/dist/db/queries.js.map +1 -1
  38. package/dist/db/schema.sql +6 -1
  39. package/dist/db/sqlite-adapter.d.ts +8 -3
  40. package/dist/db/sqlite-adapter.d.ts.map +1 -1
  41. package/dist/db/sqlite-adapter.js +45 -3
  42. package/dist/db/sqlite-adapter.js.map +1 -1
  43. package/dist/extraction/extraction-version.d.ts +1 -1
  44. package/dist/extraction/extraction-version.js +1 -1
  45. package/dist/extraction/generated-detection.d.ts +42 -11
  46. package/dist/extraction/generated-detection.d.ts.map +1 -1
  47. package/dist/extraction/generated-detection.js +141 -20
  48. package/dist/extraction/generated-detection.js.map +1 -1
  49. package/dist/extraction/index.d.ts +42 -0
  50. package/dist/extraction/index.d.ts.map +1 -1
  51. package/dist/extraction/index.js +96 -0
  52. package/dist/extraction/index.js.map +1 -1
  53. package/dist/extraction/languages/arkts.d.ts +19 -2
  54. package/dist/extraction/languages/arkts.d.ts.map +1 -1
  55. package/dist/extraction/languages/arkts.js +460 -110
  56. package/dist/extraction/languages/arkts.js.map +1 -1
  57. package/dist/extraction/wasm-runtime-flags.d.ts.map +1 -1
  58. package/dist/extraction/wasm-runtime-flags.js +9 -6
  59. package/dist/extraction/wasm-runtime-flags.js.map +1 -1
  60. package/dist/graph-sources.d.ts +41 -0
  61. package/dist/graph-sources.d.ts.map +1 -0
  62. package/dist/graph-sources.js +82 -0
  63. package/dist/graph-sources.js.map +1 -0
  64. package/dist/index.d.ts +19 -1
  65. package/dist/index.d.ts.map +1 -1
  66. package/dist/index.js +79 -9
  67. package/dist/index.js.map +1 -1
  68. package/dist/installer/index.d.ts.map +1 -1
  69. package/dist/installer/index.js +0 -48
  70. package/dist/installer/index.js.map +1 -1
  71. package/dist/installer/targets/antigravity.js +1 -1
  72. package/dist/installer/targets/antigravity.js.map +1 -1
  73. package/dist/installer/targets/deveco.js +1 -1
  74. package/dist/installer/targets/deveco.js.map +1 -1
  75. package/dist/installer/targets/hermes.d.ts +1 -1
  76. package/dist/installer/targets/hermes.js +2 -2
  77. package/dist/installer/targets/hermes.js.map +1 -1
  78. package/dist/installer/targets/opencode.js +1 -1
  79. package/dist/installer/targets/opencode.js.map +1 -1
  80. package/dist/installer/targets/shared.js +1 -1
  81. package/dist/installer/targets/shared.js.map +1 -1
  82. package/dist/mcp/daemon-paths.d.ts +5 -4
  83. package/dist/mcp/daemon-paths.d.ts.map +1 -1
  84. package/dist/mcp/daemon-paths.js +28 -13
  85. package/dist/mcp/daemon-paths.js.map +1 -1
  86. package/dist/mcp/daemon-registry.d.ts.map +1 -1
  87. package/dist/mcp/daemon-registry.js +4 -1
  88. package/dist/mcp/daemon-registry.js.map +1 -1
  89. package/dist/mcp/daemon.d.ts +1 -1
  90. package/dist/mcp/daemon.js +1 -1
  91. package/dist/mcp/engine.d.ts.map +1 -1
  92. package/dist/mcp/engine.js +12 -3
  93. package/dist/mcp/engine.js.map +1 -1
  94. package/dist/mcp/explore-dedup.d.ts +137 -0
  95. package/dist/mcp/explore-dedup.d.ts.map +1 -0
  96. package/dist/mcp/explore-dedup.js +236 -0
  97. package/dist/mcp/explore-dedup.js.map +1 -0
  98. package/dist/mcp/explore-session-state.d.ts +217 -0
  99. package/dist/mcp/explore-session-state.d.ts.map +1 -0
  100. package/dist/mcp/explore-session-state.js +322 -0
  101. package/dist/mcp/explore-session-state.js.map +1 -0
  102. package/dist/mcp/index.d.ts.map +1 -1
  103. package/dist/mcp/index.js +4 -7
  104. package/dist/mcp/index.js.map +1 -1
  105. package/dist/mcp/proxy.d.ts.map +1 -1
  106. package/dist/mcp/proxy.js +5 -14
  107. package/dist/mcp/proxy.js.map +1 -1
  108. package/dist/mcp/query-worker.d.ts +1 -1
  109. package/dist/mcp/query-worker.js +1 -1
  110. package/dist/mcp/server-instructions.d.ts +7 -2
  111. package/dist/mcp/server-instructions.d.ts.map +1 -1
  112. package/dist/mcp/server-instructions.js +104 -28
  113. package/dist/mcp/server-instructions.js.map +1 -1
  114. package/dist/mcp/session.d.ts +14 -3
  115. package/dist/mcp/session.d.ts.map +1 -1
  116. package/dist/mcp/session.js +17 -14
  117. package/dist/mcp/session.js.map +1 -1
  118. package/dist/mcp/startup-handshake.d.ts +1 -1
  119. package/dist/mcp/startup-handshake.js +1 -1
  120. package/dist/mcp/tools.d.ts +117 -7
  121. package/dist/mcp/tools.d.ts.map +1 -1
  122. package/dist/mcp/tools.js +3056 -503
  123. package/dist/mcp/tools.js.map +1 -1
  124. package/dist/resolution/frameworks/arkts-napi.d.ts +14 -0
  125. package/dist/resolution/frameworks/arkts-napi.d.ts.map +1 -1
  126. package/dist/resolution/frameworks/arkts-napi.js +169 -49
  127. package/dist/resolution/frameworks/arkts-napi.js.map +1 -1
  128. package/dist/search/query-utils.d.ts +116 -4
  129. package/dist/search/query-utils.d.ts.map +1 -1
  130. package/dist/search/query-utils.js +869 -60
  131. package/dist/search/query-utils.js.map +1 -1
  132. package/dist/types.d.ts +8 -0
  133. package/dist/types.d.ts.map +1 -1
  134. package/dist/upgrade/index.d.ts +14 -26
  135. package/dist/upgrade/index.d.ts.map +1 -1
  136. package/dist/upgrade/index.js +28 -123
  137. package/dist/upgrade/index.js.map +1 -1
  138. package/dist/upgrade/remove-binary.d.ts +19 -19
  139. package/dist/upgrade/remove-binary.d.ts.map +1 -1
  140. package/dist/upgrade/remove-binary.js +20 -21
  141. package/dist/upgrade/remove-binary.js.map +1 -1
  142. package/dist/upgrade/update-check.d.ts +4 -3
  143. package/dist/upgrade/update-check.d.ts.map +1 -1
  144. package/dist/upgrade/update-check.js +4 -3
  145. package/dist/upgrade/update-check.js.map +1 -1
  146. package/package.json +12 -8
  147. package/dist/telemetry/index.d.ts +0 -143
  148. package/dist/telemetry/index.d.ts.map +0 -1
  149. package/dist/telemetry/index.js +0 -541
  150. package/dist/telemetry/index.js.map +0 -1
@@ -38,7 +38,7 @@ var __importStar = (this && this.__importStar) || (function () {
38
38
  };
39
39
  })();
40
40
  Object.defineProperty(exports, "__esModule", { value: true });
41
- exports.IMPLEMENTATION_ENTRY_NAME_RE = exports.STOP_WORDS = exports.QUERY_SOURCE_FILE_EXT = void 0;
41
+ exports.IMPLEMENTATION_ENTRY_NAME_RE = exports.GENERIC_VERB_ANCHOR_NOISE = exports.STOP_WORDS = exports.FRAMEWORK_UI_DECORATOR_NAMES = exports.QUERY_SOURCE_FILE_EXT = void 0;
42
42
  exports.normalizeNameToken = normalizeNameToken;
43
43
  exports.extractFileBasenamesFromQuery = extractFileBasenamesFromQuery;
44
44
  exports.extractPathSegmentsFromQuery = extractPathSegmentsFromQuery;
@@ -47,11 +47,20 @@ exports.extractKitModuleNamesFromQuery = extractKitModuleNamesFromQuery;
47
47
  exports.extractKitSubmoduleNamesFromQuery = extractKitSubmoduleNamesFromQuery;
48
48
  exports.queryAsKitModuleCapabilitySurvey = queryAsKitModuleCapabilitySurvey;
49
49
  exports.isMemberLikeIdentifier = isMemberLikeIdentifier;
50
+ exports.queryAsCoNamedTypeMethodInteraction = queryAsCoNamedTypeMethodInteraction;
50
51
  exports.queryHasNamedMemberFocus = queryHasNamedMemberFocus;
51
52
  exports.queryAsNamedComponentAction = queryAsNamedComponentAction;
52
53
  exports.queryAsOutOfRepoSdkCatalog = queryAsOutOfRepoSdkCatalog;
54
+ exports.queryShouldDeferToBuiltinTools = queryShouldDeferToBuiltinTools;
55
+ exports.homegraphDeferGuidance = homegraphDeferGuidance;
53
56
  exports.queryShouldPreferExploreOverSearch = queryShouldPreferExploreOverSearch;
57
+ exports.shouldBuildHoverHandlerSurvey = shouldBuildHoverHandlerSurvey;
58
+ exports.isFrameworkUiDecoratorName = isFrameworkUiDecoratorName;
59
+ exports.queryLooksLikeUiComponentType = queryLooksLikeUiComponentType;
60
+ exports.queryAsFocusedUiCluster = queryAsFocusedUiCluster;
61
+ exports.queryAsComponentSurfaceSurvey = queryAsComponentSurfaceSurvey;
54
62
  exports.shouldBuildKitModuleUsageSurvey = shouldBuildKitModuleUsageSurvey;
63
+ exports.queryAsksKitInstallDeps = queryAsksKitInstallDeps;
55
64
  exports.extractMemberAccessFromQuery = extractMemberAccessFromQuery;
56
65
  exports.extractImportSearchTerms = extractImportSearchTerms;
57
66
  exports.fileMatchesQueryBasename = fileMatchesQueryBasename;
@@ -62,6 +71,22 @@ exports.queryNamesConfigFile = queryNamesConfigFile;
62
71
  exports.hasSymbolFilterInQuery = hasSymbolFilterInQuery;
63
72
  exports.hasImportInventoryFilter = hasImportInventoryFilter;
64
73
  exports.queryAsApiUsageSurvey = queryAsApiUsageSurvey;
74
+ exports.extractFieldLikeSymbolsFromQuery = extractFieldLikeSymbolsFromQuery;
75
+ exports.queryAsDeclarationSiteSurvey = queryAsDeclarationSiteSurvey;
76
+ exports.queryAsInRepoSystemCapabilityHowto = queryAsInRepoSystemCapabilityHowto;
77
+ exports.queryAsReturnValueConsumerSurvey = queryAsReturnValueConsumerSurvey;
78
+ exports.queryAsConstantUsageSurvey = queryAsConstantUsageSurvey;
79
+ exports.queryAsFieldUsageSurvey = queryAsFieldUsageSurvey;
80
+ exports.queryAsNativeRenderThreadSurvey = queryAsNativeRenderThreadSurvey;
81
+ exports.queryAsDtsWrapSurvey = queryAsDtsWrapSurvey;
82
+ exports.queryAsAssignedFlagImpactSurvey = queryAsAssignedFlagImpactSurvey;
83
+ exports.queryAsNamedControlStateSyncSurvey = queryAsNamedControlStateSyncSurvey;
84
+ exports.extractListedTypeMethodsFromQuery = extractListedTypeMethodsFromQuery;
85
+ exports.queryAsModuleExportSurvey = queryAsModuleExportSurvey;
86
+ exports.queryAsModuleDependencySurvey = queryAsModuleDependencySurvey;
87
+ exports.queryLooksLikeLiteralOrCopyHunt = queryLooksLikeLiteralOrCopyHunt;
88
+ exports.queryAsTypeLifecycleSurvey = queryAsTypeLifecycleSurvey;
89
+ exports.shouldPreferMemberUsageInventory = shouldPreferMemberUsageInventory;
65
90
  exports.extractApiUsageTokens = extractApiUsageTokens;
66
91
  exports.shouldBuildApiUsageSurvey = shouldBuildApiUsageSurvey;
67
92
  exports.queryAsCallerOrMethodSurvey = queryAsCallerOrMethodSurvey;
@@ -80,6 +105,7 @@ exports.shouldFocusOnQueryNamedDefs = shouldFocusOnQueryNamedDefs;
80
105
  exports.queryAsInterpretationSurvey = queryAsInterpretationSurvey;
81
106
  exports.queryAsTestOnlyInterpretation = queryAsTestOnlyInterpretation;
82
107
  exports.extractMechanismEntrySeeds = extractMechanismEntrySeeds;
108
+ exports.queryAsDomainMechanismBag = queryAsDomainMechanismBag;
83
109
  exports.shouldTryLightMechanismExplore = shouldTryLightMechanismExplore;
84
110
  exports.shouldTryFastInventoryExplore = shouldTryFastInventoryExplore;
85
111
  exports.shouldBuildInheritanceSurvey = shouldBuildInheritanceSurvey;
@@ -178,7 +204,9 @@ function extractKitModuleNamesFromQuery(query) {
178
204
  }
179
205
  return [...names];
180
206
  }
181
- /** Submodule tokens from queries like `util模块` / `util 模块`. */
207
+ /** Submodule / named-export tokens from kit usage questions.
208
+ * Covers `util模块`, `util 模块`, and `@kit.Foo的bar` / `@kit.Foo bar` (export focus).
209
+ */
182
210
  function extractKitSubmoduleNamesFromQuery(query) {
183
211
  const names = new Set();
184
212
  for (const m of query.matchAll(/([a-z][a-z0-9_]{1,24})模块/gi)) {
@@ -189,6 +217,15 @@ function extractKitSubmoduleNamesFromQuery(query) {
189
217
  if (m[1] && m[1].length >= 2)
190
218
  names.add(m[1]);
191
219
  }
220
+ // `@kit.ArkTS的taskpool` / `@kit.ArkTS的 foo` — named export, not a catalog.
221
+ for (const m of query.matchAll(/@kit\.[A-Za-z][A-Za-z0-9]*的\s*([a-z][a-zA-Z0-9_]{2,24})\b/gi)) {
222
+ if (m[1])
223
+ names.add(m[1]);
224
+ }
225
+ for (const m of query.matchAll(/@kit\.[A-Za-z][A-Za-z0-9]*[\s.]+([a-z][a-zA-Z0-9_]{2,24})\b/gi)) {
226
+ if (m[1] && !exports.STOP_WORDS.has(m[1].toLowerCase()))
227
+ names.add(m[1]);
228
+ }
192
229
  return [...names];
193
230
  }
194
231
  /**
@@ -224,6 +261,29 @@ function isMemberLikeIdentifier(token) {
224
261
  // camelCase property/method: first lower, later upper
225
262
  return /^[a-z][a-zA-Z0-9]*[A-Z]/.test(token);
226
263
  }
264
+ /**
265
+ * Owning Type + co-named PascalCase methods (CanPlace/Place ↔ BinaryGrid).
266
+ * Not Type.member dotted form — agents name methods as sibling Types.
267
+ * Must stay on compact explore — 「是怎么」alone would light-mechanism dump.
268
+ */
269
+ function queryAsCoNamedTypeMethodInteraction(query) {
270
+ // "who calls Type's Set/Test/Fill" is a caller inventory, not body interaction.
271
+ if (queryAsCallerOrMethodSurvey(query))
272
+ return false;
273
+ const types = extractTypeNamesFromQuery(query).filter((t) => !isFrameworkUiDecoratorName(t));
274
+ if (types.length < 2 || types.length > 4)
275
+ return false;
276
+ const owners = types.filter((t) => t.length >= 8
277
+ || /(?:Grid|Manager|Service|Controller|Render|Plugin|Layout|Handler|Provider|Store)/i.test(t));
278
+ const methods = types.filter((t) => !owners.includes(t));
279
+ if (owners.length < 1 || methods.length < 1)
280
+ return false;
281
+ if (/交互|是怎么|如何.*(?:用|调|连|互)|how\s+(?:do|does|are).{0,48}(?:interact|relate|use|call)|与.+交互|和.+交互/i.test(query)) {
282
+ return true;
283
+ }
284
+ // Method-looking PascalCase co-named with an owner — even without 交互.
285
+ return methods.some((m) => /^(?:Can|Is|Has|Get|Set|Place|Fill|Test|Put|Add|Remove|Update|Draw|Check|Find)/.test(m));
286
+ }
227
287
  /**
228
288
  * Named Type + member (dot form, or co-named Type + member-like token).
229
289
  * Structural — no product/question nouns.
@@ -231,6 +291,8 @@ function isMemberLikeIdentifier(token) {
231
291
  function queryHasNamedMemberFocus(query) {
232
292
  if (extractMemberAccessFromQuery(query).length > 0)
233
293
  return true;
294
+ if (queryAsCoNamedTypeMethodInteraction(query))
295
+ return true;
234
296
  const types = extractTypeNamesFromQuery(query);
235
297
  if (types.length === 0 || types.length > 3)
236
298
  return false;
@@ -248,10 +310,10 @@ function queryAsNamedComponentAction(query) {
248
310
  if (!looksUi)
249
311
  return false;
250
312
  const deps = extractDependencySymbolsFromQuery(query);
251
- if (deps.some((d) => /^on[A-Z]/.test(d) || /^(click|tap|press)$/i.test(d)))
313
+ if (deps.some((d) => /^on[A-Z]/.test(d) || /^(click|tap|press|hover)$/i.test(d)))
252
314
  return true;
253
315
  // Optional thin intent — only with a UI-looking Type already present.
254
- return /点击|单击|按下|按钮|onClick|onTap|会发生什么|what\s+happens|when\s+(?:clicked|pressed)/i.test(query);
316
+ return /点击|单击|按下|按钮|悬停|hover|onClick|onTap|onHover|会发生什么|what\s+happens|when\s+(?:clicked|pressed|hovered)/i.test(query);
255
317
  }
256
318
  /**
257
319
  * Out-of-repo SDK catalog (kept for survey omit logic — not as a refuse list).
@@ -264,6 +326,97 @@ function queryAsOutOfRepoSdkCatalog(query) {
264
326
  return false;
265
327
  return /@kit\.|\b[A-Z][A-Za-z0-9]*Kit\b/i.test(query);
266
328
  }
329
+ function queryShouldDeferToBuiltinTools(query) {
330
+ const q = query.trim();
331
+ if (!q)
332
+ return null;
333
+ // Pre-existing catalog classifiers (@kit / *Kit + "what features/APIs").
334
+ // Checked before named-member heuristics — `@kit.X` can look like Type.member.
335
+ if (queryAsOutOfRepoSdkCatalog(q) || queryAsKitModuleCapabilitySurvey(q)) {
336
+ return 'sdk-catalog';
337
+ }
338
+ const hasType = extractTypeNamesFromQuery(q).length > 0;
339
+ const hasFile = extractFileBasenamesFromQuery(q).length > 0;
340
+ const hasMember = extractMemberAccessFromQuery(q).length > 0;
341
+ const hasKit = /@kit\.|@ohos\./i.test(q);
342
+ const hasCaps = /\b[A-Z][A-Z0-9]*(?:_[A-Z0-9]+)+\b/.test(q);
343
+ const hasField = extractFieldLikeSymbolsFromQuery(q).length > 0;
344
+ const hasPath = extractPathSegmentsFromQuery(q).length > 0;
345
+ // Literal / layout / "import text from graphics package" hunts → Grep.
346
+ // No PascalCase Type — light-mechanism must not claim these.
347
+ if (!hasType
348
+ && !hasFile
349
+ && !hasMember
350
+ && !hasKit
351
+ && !hasCaps
352
+ && !hasField
353
+ && !hasPath
354
+ && queryLooksLikeLiteralOrCopyHunt(q)) {
355
+ return 'concept-or-existence';
356
+ }
357
+ // Structural / in-repo usage surveys must still run.
358
+ if (queryAsMechanismSurvey(q) || queryAsCrossModuleFlowSurvey(q))
359
+ return null;
360
+ if (shouldBuildKitModuleUsageSurvey(q) || shouldBuildApiUsageSurvey(q))
361
+ return null;
362
+ if (shouldBuildCallerInventory(q) || shouldTryLightMechanismExplore(q))
363
+ return null;
364
+ if (queryAsNamedComponentAction(q) || queryHasNamedMemberFocus(q))
365
+ return null;
366
+ if (queryAsInterpretationSurvey(q) || queryAsLocalSymbolDetail(q))
367
+ return null;
368
+ if (queryHasFocusedNamedAnchors(q))
369
+ return null;
370
+ if (queryAsDeclarationSiteSurvey(q)
371
+ || queryAsConstantUsageSurvey(q)
372
+ || queryAsFieldUsageSurvey(q)
373
+ || queryAsModuleExportSurvey(q)
374
+ || queryAsModuleDependencySurvey(q)) {
375
+ return null;
376
+ }
377
+ // "Which files relate to <topic>?" with no Type/file anchor → Glob/Grep.
378
+ if (/哪些文件|有哪些.*文件|相关的?\s*文件|涉及.*文件|which\s+files?|files?\s+(?:are\s+)?related/i.test(q)
379
+ && !hasType
380
+ && !hasFile) {
381
+ return 'file-listing';
382
+ }
383
+ // Existence / concept-compare with no graph anchor → Grep/docs.
384
+ if (/有使用|是否使用|有没有使用|使用了.*吗|是否存在|有没有完整|有什么不同|区别|difference|vs\.?|versus|对比|does\s+(?:this\s+)?(?:project|repo)\s+have|is\s+there\s+(?:a\s+)?complete/i.test(q)
385
+ && !hasType
386
+ && !hasFile
387
+ && !hasMember
388
+ && !hasKit) {
389
+ return 'concept-or-existence';
390
+ }
391
+ // Hover / 悬停 without a named Type → inventory of hover handlers (not Skip).
392
+ if (shouldBuildHoverHandlerSurvey(q))
393
+ return null;
394
+ // Behavioral / interaction outcome with zero graph anchors → Grep (no symbol to seed).
395
+ // Do NOT include hover/悬停 here — those have a graph inventory path above.
396
+ if (!hasType
397
+ && !hasFile
398
+ && !hasMember
399
+ && !hasKit
400
+ && /会有什么|会发生什么|什么反应|what\s+happens|what\s+(?:is|are)\s+the\s+(?:reaction|response)|when\s+(?:the\s+)?user\b/i.test(q)) {
401
+ return 'concept-or-existence';
402
+ }
403
+ return null;
404
+ }
405
+ /** Short success-shaped reply when explore/search should not run. */
406
+ function homegraphDeferGuidance(kind, query) {
407
+ const q = query.trim().slice(0, 120);
408
+ const reasons = {
409
+ 'sdk-catalog': 'Official SDK / @kit feature catalogs are outside this repo index — use SDK docs.',
410
+ 'file-listing': 'Topic → file lists need Grep/Glob (keyword paths), not the symbol graph.',
411
+ 'concept-or-existence': 'Concept compare / UI-behavior / existence checks without a named in-repo Type/file/@kit target — Grep (and docs) beat HomeGraph.',
412
+ };
413
+ return [
414
+ 'Skip HomeGraph for this question shape — do **not** retry any `homegraph_*` tool for it.',
415
+ reasons[kind],
416
+ `Query: "${q}${query.trim().length > 120 ? '…' : ''}"`,
417
+ 'Next: Grep / Glob / Read / SDK docs. Call HomeGraph only for in-repo wiring, callers/callees, or named-symbol flows.',
418
+ ].join('\n');
419
+ }
267
420
  /**
268
421
  * Question already names files/symbols/@kit — prefer explore over FTS search.
269
422
  */
@@ -271,17 +424,95 @@ function queryShouldPreferExploreOverSearch(query) {
271
424
  const q = query.trim();
272
425
  if (!q)
273
426
  return false;
427
+ if (queryLooksLikeLiteralOrCopyHunt(query))
428
+ return false;
274
429
  if (/^[A-Za-z_][\w]*$/.test(q))
275
430
  return true;
276
431
  if (extractFileBasenamesFromQuery(query).length > 0
277
432
  || extractTypeNamesFromQuery(query).length > 0
278
433
  || extractMemberAccessFromQuery(query).length > 0
279
434
  || extractImportSearchTerms(query).length > 0
280
- || extractKitModuleNamesFromQuery(query).length > 0) {
435
+ || extractKitModuleNamesFromQuery(query).length > 0
436
+ || extractFieldLikeSymbolsFromQuery(query).length > 0
437
+ || queryAsConstantUsageSurvey(query)
438
+ || queryAsDeclarationSiteSurvey(query)
439
+ || queryAsFieldUsageSurvey(query)
440
+ || queryAsModuleExportSurvey(query)
441
+ || queryAsModuleDependencySurvey(query)
442
+ || queryAsDtsWrapSurvey(query)
443
+ || shouldBuildApiUsageSurvey(query)
444
+ || queryAsAssignedFlagImpactSurvey(query)
445
+ || queryAsNativeRenderThreadSurvey(query)
446
+ || queryAsInRepoSystemCapabilityHowto(query)
447
+ || shouldBuildHoverHandlerSurvey(query)
448
+ || queryAsDataSourceSurvey(query)
449
+ || queryAsksKitInstallDeps(query)
450
+ || queryAsTypeLifecycleSurvey(query)
451
+ || shouldBuildCallerInventory(query)) {
281
452
  return true;
282
453
  }
283
454
  return extractDependencySymbolsFromQuery(query).some((d) => isDistinctiveIdentifier(d));
284
455
  }
456
+ /**
457
+ * In-repo pointer/hover handler inventory — no named Type yet, but the graph
458
+ * still has onHover / Hover* symbols. Soft-skip→Grep lost accuracy; inventory wins.
459
+ */
460
+ function shouldBuildHoverHandlerSurvey(query) {
461
+ if (extractTypeNamesFromQuery(query).length > 0)
462
+ return false;
463
+ if (extractMemberAccessFromQuery(query).length > 0)
464
+ return false;
465
+ if (queryAsNamedComponentAction(query))
466
+ return false;
467
+ return /悬停|\bhover\b|onHover|hoverEffect|HoverAnimation|HoverConstants/i.test(query);
468
+ }
469
+ /**
470
+ * Framework decorator / ambient UI names — agents paste `@CustomDialog` etc.
471
+ * Keep real *Page/*Dialog types; drop these when counting UI surfaces / anchors.
472
+ */
473
+ exports.FRAMEWORK_UI_DECORATOR_NAMES = new Set([
474
+ 'customdialog', 'component', 'entry', 'builder', 'preview', 'observed', 'observable',
475
+ 'state', 'prop', 'link', 'objectlink', 'provide', 'consume', 'watch', 'builderparam',
476
+ ]);
477
+ function isFrameworkUiDecoratorName(name) {
478
+ return exports.FRAMEWORK_UI_DECORATOR_NAMES.has(name.toLowerCase());
479
+ }
480
+ /** PascalCase names that look like UI surfaces (Page / View / … / ThemeHome). */
481
+ function queryLooksLikeUiComponentType(name) {
482
+ // `@CustomDialog` / bare `Component` match the suffix regex but are not app Types.
483
+ if (isFrameworkUiDecoratorName(name))
484
+ return false;
485
+ return /(Button|Component|View|Page|Dialog|Bar|Item|Panel|Menu|Chip|Card|Home)$/i.test(name);
486
+ }
487
+ /**
488
+ * 2–3 concrete UI Types named together (Page+Dialog, View+Panel, …).
489
+ * Must stay on compact explore — full findRelevantContext is a 15–30k / multi-minute
490
+ * dump for what is a local composition / resource question.
491
+ */
492
+ function queryAsFocusedUiCluster(query) {
493
+ if (queryAsMechanismSurvey(query) || queryAsCrossModuleFlowSurvey(query))
494
+ return false;
495
+ const types = extractTypeNamesFromQuery(query).filter((t) => queryLooksLikeUiComponentType(t) && !isFrameworkUiDecoratorName(t));
496
+ return types.length >= 2 && types.length <= 3;
497
+ }
498
+ /**
499
+ * Named Page/Component overview: which UI children / where does it navigate /
500
+ * how does Page embed Dialog. Needs component bodies, not full-graph explore.
501
+ */
502
+ function queryAsComponentSurfaceSurvey(query) {
503
+ if (queryAsFocusedUiCluster(query))
504
+ return true;
505
+ const types = extractTypeNamesFromQuery(query).filter((t) => !isFrameworkUiDecoratorName(t));
506
+ if (types.length < 1 || types.length > 3)
507
+ return false;
508
+ if (!types.some(queryLooksLikeUiComponentType))
509
+ return false;
510
+ if (/使用了哪些|哪些UI|哪些组件|UI组件|跳转到|可以跳转|内嵌|嵌入|preview|加载|page\s+navigation|which\s+(?:ui\s+)?components|navigat|embed/i.test(query)) {
511
+ return true;
512
+ }
513
+ // Bare / near-bare `ThemeHome` / `FooPage` — agents search("ThemeHome") for overview.
514
+ return queryIsTypeNameFocus(query);
515
+ }
285
516
  /**
286
517
  * In-repo kit/import **usage** inventory (call sites / which files import).
287
518
  */
@@ -292,11 +523,23 @@ function shouldBuildKitModuleUsageSurvey(query) {
292
523
  && extractImportSearchTerms(query).length === 0) {
293
524
  return false;
294
525
  }
295
- return /依赖|引用|哪里用|哪些代码|哪里调用|哪些.*(?:依赖|引用|使用)|import\s+sites?|usages?\b|\buses\b|call\s*sites?/i.test(query);
526
+ return (/依赖|引用|哪里用|哪些代码|哪里调用|哪些.*(?:依赖|引用|使用)|import\s+sites?|usages?\b|\buses\b|call\s*sites?/i.test(query)
527
+ // "调用 Kit 需要额外安装哪些依赖 / how to install deps for Kit"
528
+ || queryAsksKitInstallDeps(query));
529
+ }
530
+ /** "需要额外安装哪些依赖" — kit survey must include oh-package, not only imports. */
531
+ function queryAsksKitInstallDeps(query) {
532
+ return (/额外安装|还需.*依赖|需要.*依赖|引入.*依赖|其他参数和依赖|other\s+(?:params?.{0,24})?deps?/i.test(query)
533
+ || /install(?:ing)?\s+(?:extra\s+)?deps?|which\s+deps?|need(?:s)?\s+(?:other\s+)?deps?/i.test(query));
296
534
  }
297
535
  /**
298
536
  * Member-access patterns from a query — `obj.method`, `Type.method`, or `.member`.
537
+ * Skips `@kit.X` / `@ohos.X` module paths (those are imports, not Type.member).
299
538
  */
539
+ /** Shared-object / binary suffixes — not Type.member (libfoo.so → member "so"). */
540
+ const FILE_EXT_MEMBER_NOISE = new Set([
541
+ 'so', 'dll', 'dylib', 'a', 'lib', 'exe', 'apk', 'hap', 'bin', 'wasm', 'o', 'obj',
542
+ ]);
300
543
  function extractMemberAccessFromQuery(query) {
301
544
  const results = [];
302
545
  const seen = new Set();
@@ -304,12 +547,21 @@ function extractMemberAccessFromQuery(query) {
304
547
  const dotted = m[1];
305
548
  if (!dotted || seen.has(dotted))
306
549
  continue;
550
+ const idx = m.index ?? 0;
551
+ // `@kit.ArkTS` / `@ohos.foo` — module specifier, not receiver.member
552
+ if (idx > 0 && query[idx - 1] === '@')
553
+ continue;
554
+ if (/^(?:kit|ohos)\./i.test(dotted))
555
+ continue;
307
556
  const parts = dotted.split('.');
308
557
  if (parts.length < 2)
309
558
  continue;
310
559
  const member = parts[parts.length - 1];
311
560
  if (member.length < 2)
312
561
  continue;
562
+ // `libeffectrender.so` / `foo.dll` — not a member access.
563
+ if (FILE_EXT_MEMBER_NOISE.has(member.toLowerCase()))
564
+ continue;
313
565
  seen.add(dotted);
314
566
  results.push({
315
567
  receiver: parts.slice(0, -1).join('.'),
@@ -319,12 +571,29 @@ function extractMemberAccessFromQuery(query) {
319
571
  }
320
572
  for (const m of query.matchAll(/\.([A-Za-z_][\w]*)\b/g)) {
321
573
  const member = m[1];
574
+ const idx = m.index ?? 0;
575
+ // Skip the `.X` inside `@kit.X` / `@ohos.X`
576
+ const before = query.slice(0, idx);
577
+ if (/@(?:kit|ohos)$/i.test(before))
578
+ continue;
579
+ if (FILE_EXT_MEMBER_NOISE.has(member.toLowerCase()))
580
+ continue;
322
581
  const dotted = `.${member}`;
323
582
  if (seen.has(dotted))
324
583
  continue;
325
584
  seen.add(dotted);
326
585
  results.push({ member, dotted });
327
586
  }
587
+ // C++ / ArkTS qualified `Type::method` — same shape as dotted member access.
588
+ for (const m of query.matchAll(/\b([A-Za-z_][\w]*)::([A-Za-z_][\w]*)\b/g)) {
589
+ const receiver = m[1];
590
+ const member = m[2];
591
+ const dotted = `${receiver}::${member}`;
592
+ if (seen.has(dotted) || member.length < 2)
593
+ continue;
594
+ seen.add(dotted);
595
+ results.push({ receiver, member, dotted });
596
+ }
328
597
  return results;
329
598
  }
330
599
  /** Terms for targeted import-node FTS (kit modules and their @kit.* paths). */
@@ -436,22 +705,49 @@ exports.STOP_WORDS = new Set([
436
705
  * Lowercase API / import symbol names in a dependency question (taskpool, formInfo).
437
706
  * Used to filter import sites to the symbol actually asked about, not every @kit.ArkTS import.
438
707
  */
708
+ /**
709
+ * Generic verbs agents put in rewritten how-implemented bags ("xml parse").
710
+ * Not useful as dependency/import inventory filters or local-detail anchors —
711
+ * they match every `parse`/`load` method and drown the distinctive token (xml).
712
+ */
713
+ exports.GENERIC_VERB_ANCHOR_NOISE = new Set([
714
+ 'parse', 'parsing', 'load', 'save', 'read', 'write', 'update', 'delete', 'create',
715
+ 'init', 'handle', 'process', 'run', 'start', 'stop', 'open', 'close', 'convert',
716
+ 'get', 'set', 'add', 'remove', 'list', 'show', 'find',
717
+ // Lifetime / allocator verbs — agents ask "where new/delete" and these must not
718
+ // become explore seeds (AnimateEx.new / RdbStoreHelper.delete).
719
+ 'new', 'pointer', 'alloc', 'allocate', 'dealloc', 'deallocate', 'free', 'release',
720
+ // Inventory / NL fillers agents leave in rewritten bags (not real symbols).
721
+ 'usage', 'usages', 'use', 'uses', 'using', 'api', 'apis', 'endpoint', 'endpoints',
722
+ 'project', 'codebase', 'repo', 'documentation', 'document', 'docs', '说明',
723
+ // Shared-lib / GL thread NL leftovers (not symbols).
724
+ 'context', 'opengl', 'gles', 'glesv3', 'egl',
725
+ ]);
439
726
  function extractDependencySymbolsFromQuery(query) {
440
727
  const symbols = new Set();
441
728
  for (const ma of extractMemberAccessFromQuery(query)) {
442
729
  if (ma.member.length >= 3 && !exports.STOP_WORDS.has(ma.member.toLowerCase())) {
443
- symbols.add(ma.member);
730
+ if (!exports.GENERIC_VERB_ANCHOR_NOISE.has(ma.member.toLowerCase()))
731
+ symbols.add(ma.member);
444
732
  }
445
733
  }
446
- for (const m of query.matchAll(/\b([a-z][a-zA-Z0-9]{3,})\b/g)) {
734
+ for (const f of extractFieldLikeSymbolsFromQuery(query))
735
+ symbols.add(f);
736
+ // ≥3-char lowercase tokens (includes xml); drop generic verbs.
737
+ for (const m of query.matchAll(/\b([a-z][a-zA-Z0-9]{2,})\b/g)) {
447
738
  const sym = m[1];
448
- if (!exports.STOP_WORDS.has(sym.toLowerCase()))
449
- symbols.add(sym);
739
+ if (exports.STOP_WORDS.has(sym.toLowerCase()))
740
+ continue;
741
+ if (exports.GENERIC_VERB_ANCHOR_NOISE.has(sym.toLowerCase()))
742
+ continue;
743
+ symbols.add(sym);
450
744
  }
451
745
  // Lowercase API tokens (statfs, napi_wrap) when the query is clearly about code.
452
746
  if (queryMentionsCodeContext(query)) {
453
747
  for (const m of query.matchAll(/\b([a-z][a-z0-9_]{4,})\b/g)) {
454
748
  const sym = m[1];
749
+ if (exports.GENERIC_VERB_ANCHOR_NOISE.has(sym))
750
+ continue;
455
751
  if (!exports.STOP_WORDS.has(sym) && isDistinctiveIdentifier(sym))
456
752
  symbols.add(sym);
457
753
  else if (!exports.STOP_WORDS.has(sym) && sym.length >= 6)
@@ -495,7 +791,252 @@ function hasImportInventoryFilter(query) {
495
791
  }
496
792
  /** Where-is-API-used / call-site intent (statfs, snake_case APIs, "哪里使用了 X"). */
497
793
  function queryAsApiUsageSurvey(query) {
498
- return (/哪里使用|何处使用|哪些地方使用|在哪里使用|什么地方使用|项目中.*使用|使用了.*API|API端点|调用.*API|使用了哪些调用|哪些调用方法|调用方法|使用了哪些方法|where\s+(?:is|are).+(?:used|called)|which\s+(?:files?|code)\s+(?:use|call)/i.test(query));
794
+ return (/哪里使用|何处使用|哪些地方使用|在哪里使用|什么地方使用|项目中.*使用|使用了.*API|API端点|调用.*API|使用了哪些调用|哪些调用方法|调用方法|使用了哪些方法|哪些组件使用|哪些.*使用了|使用了\s*\.|自定义绘制|where\s+(?:is|are).+(?:used|called)|which\s+(?:files?|code|components?)\s+(?:use|call)/i.test(query)
795
+ // Agent rewrites (Telephony API usages / call methods in the project).
796
+ || /\bAPI\s+usages?\b/i.test(query)
797
+ || /\busages?\s+(?:of|for|in)\b/i.test(query)
798
+ || /\bcall\s+methods?\b/i.test(query)
799
+ || /\b(?:used|called)\s+(?:methods?|APIs?)\b/i.test(query)
800
+ || /\bin(?:\s+the)?\s+project\b.{0,40}\b(?:API|usages?|methods?|calls?)\b/i.test(query)
801
+ || /\b(?:API|usages?|methods?|calls?)\b.{0,40}\bin(?:\s+the)?\s+project\b/i.test(query));
802
+ }
803
+ /**
804
+ * Member / field symbols agents paste for cross-file usage hunts (`m_eglMutex`,
805
+ * `drawModifier`). Distinct from PascalCase Types.
806
+ */
807
+ function extractFieldLikeSymbolsFromQuery(query) {
808
+ const out = new Set();
809
+ for (const m of query.matchAll(/\b(m_[A-Za-z][\w]{2,})\b/g)) {
810
+ if (m[1])
811
+ out.add(m[1]);
812
+ }
813
+ for (const m of query.matchAll(/\b([A-Za-z][\w]*(?:Mutex|Lock))\b/g)) {
814
+ if (m[1] && m[1].length >= 4)
815
+ out.add(m[1]);
816
+ }
817
+ return [...out];
818
+ }
819
+ /** Named Type + declaration / id / native-binding sites across files. */
820
+ function queryAsDeclarationSiteSurvey(query) {
821
+ const types = extractTypeNamesFromQuery(query).filter((t) => !isFrameworkUiDecoratorName(t));
822
+ if (types.length < 1 || types.length > 3)
823
+ return false;
824
+ return (/在哪些文件|哪些文件.*(?:声明|定义|出现)|被声明|声明了|id\s*分别|各自绑定|绑定到|which\s+files?.{0,80}(?:declar|defin|appear)|where\s+(?:is|are).{0,40}declar/i.test(query)
825
+ // Agent English rewrites: "XComponent declaration id native render binding"
826
+ || /\bdeclarations?\b/i.test(query)
827
+ || /\bdeclared\b/i.test(query)
828
+ || (/\bids?\b/i.test(query) && /\b(?:bind|binding|native|renderer|xcomponent)\b/i.test(query)));
829
+ }
830
+ /**
831
+ * How-to get a system setting/capability *as used in this repo* (language/locale/…).
832
+ * Must NOT become a light-mechanism dump of every `language` import.
833
+ */
834
+ function queryAsInRepoSystemCapabilityHowto(query) {
835
+ if (extractTypeNamesFromQuery(query).some((t) => /Manager|Service|Controller/i.test(t))) {
836
+ // Named Manager data-source / lifecycle owns those questions.
837
+ if (queryAsDataSourceSurvey(query) || queryAsTypeLifecycleSurvey(query))
838
+ return false;
839
+ }
840
+ return (/如何获取系统|怎样获取系统|怎么获取系统|获取系统当前|系统当前设置|系统.*语言|当前设置的语言/i.test(query)
841
+ || /how\s+to\s+get\s+(?:the\s+)?system\s+(?:language|locale|setting)/i.test(query)
842
+ || (/\b(?:language|locale|i18n)\b/i.test(query)
843
+ && /(?:获取|当前设置|get\s+system|system\s+(?:language|locale))/i.test(query)));
844
+ }
845
+ /**
846
+ * "Who uses the return value of Type.member / fn" — callers of the member, not a
847
+ * create→get flow dump or an import flood on leftover words like `state`.
848
+ */
849
+ function queryAsReturnValueConsumerSurvey(query) {
850
+ const hasMember = extractMemberAccessFromQuery(query).length > 0
851
+ || extractLocalDetailAnchors(query).some((n) => isMemberLikeIdentifier(n));
852
+ if (!hasMember && extractTypeNamesFromQuery(query).length === 0)
853
+ return false;
854
+ return (/返回结果被|返回值.*(?:被|由).*(?:使用|调用)|其返回.*使用|返回结果.*哪些/i.test(query)
855
+ || /who\s+uses\s+the\s+(?:return|result)|return(?:ed)?\s+(?:value|result).{0,80}(?:used|consum)/i.test(query)
856
+ || /哪些其他函数使用|被项目中哪些.{0,20}函数使用|which\s+(?:other\s+)?functions?\s+use/i.test(query));
857
+ }
858
+ /** ALL_CAPS constants — where used / which scenarios (not "what does this macro mean"). */
859
+ function queryAsConstantUsageSurvey(query) {
860
+ const caps = [...query.matchAll(/\b([A-Z][A-Z0-9]*(?:_[A-Z0-9]+)+)\b/g)].map((m) => m[1]);
861
+ // Enum-style `Type.MEMBER` / `audio.Type.RINGTONE` — ALL_CAPS member (with or without `_`).
862
+ const dottedCaps = [
863
+ ...query.matchAll(/\.[A-Z][A-Z0-9]*(?:_[A-Z0-9]+)*\b/g),
864
+ ]
865
+ .map((m) => m[0].slice(1))
866
+ .filter((s) => s === s.toUpperCase() && s.length >= 3);
867
+ const all = [...new Set([...caps, ...dottedCaps])];
868
+ if (all.length === 0)
869
+ return false;
870
+ return /用于|场景|哪里用|哪些.*用|依赖|调用|where\s+(?:is|are).+used|which\s+(?:scenarios?|functions?)|what\s+(?:are\s+)?(?:they|these)\s+for|which\s+methods?.{0,20}(?:call|depend)/i.test(query);
871
+ }
872
+ /** Field / mutex / lock — which other functions also touch it. */
873
+ function queryAsFieldUsageSurvey(query) {
874
+ if (extractFieldLikeSymbolsFromQuery(query).length === 0)
875
+ return false;
876
+ return (/哪些函数|还会用到|用到这个|哪些.*(?:用到|使用)|who\s+(?:else\s+)?(?:uses|locks)|which\s+functions?.{0,40}(?:use|lock|mutex)|\bmutex\b|这[个把]锁/i.test(query)
877
+ // Lifetime: where is m_foo new'd / delete'd / freed.
878
+ || /new\s*出来|被\s*new|delete\s*掉|被\s*delete|new\s*\/\s*delete|(?:哪里|哪些地方).{0,12}(?:new|delete)|(?:new|delete).{0,20}(?:哪里|哪些)|被释放|释放掉|allocat|deallocat|where.{0,40}(?:new|delete|allocat|free)/i.test(query));
879
+ }
880
+ /**
881
+ * Native shared lib + GL/EGL thread questions (`libfoo.so` … OpenGL … 哪个线程).
882
+ * Light-mechanism (CMake + EGLCore/PluginRender), not `.so` member survey.
883
+ */
884
+ function queryAsNativeRenderThreadSurvey(query) {
885
+ const hasLib = /\b\w+\.(?:so|dll|dylib)\b/i.test(query)
886
+ || /\blib\w+\b/i.test(query);
887
+ const hasGl = /\b(?:GLES(?:v3)?|EGL|OpenGL)\b/i.test(query);
888
+ const hasThread = /线程|thread|UI\s*线程|主线程|JS\s*主?线程/i.test(query);
889
+ return (hasLib || hasGl) && hasThread;
890
+ }
891
+ /** Named `.d.ts` wrap — what calls / imports it in-repo. */
892
+ function queryAsDtsWrapSurvey(query) {
893
+ if (!/\.d\.ts\b/i.test(query) && extractFileBasenamesFromQuery(query).length === 0) {
894
+ return false;
895
+ }
896
+ if (!/\.d\.ts\b/i.test(query))
897
+ return false;
898
+ return /封装|wrap(?:s|ped)?|调用|哪里|功能|where|called|uses?|import/i.test(query);
899
+ }
900
+ /**
901
+ * Assigned flag / boolean field impact on later call timing
902
+ * (`isChangeSurface = true` … 如何影响 … 时序).
903
+ */
904
+ function queryAsAssignedFlagImpactSurvey(query) {
905
+ const hasAssign = /=\s*(?:true|false)\b/i.test(query)
906
+ || /设置\s*\w+.*=/.test(query)
907
+ || /里设置\s+\w+/i.test(query)
908
+ || /\bsets?\s+\w+\s*=\s*(?:true|false)\b/i.test(query);
909
+ const hasImpact = /影响|时序|后续|how\s+(?:does|will).{0,40}affect|affect.{0,40}timing/i.test(query);
910
+ if (!hasAssign || !hasImpact)
911
+ return false;
912
+ return extractLocalDetailAnchors(query).length >= 1;
913
+ }
914
+ /**
915
+ * Named Toggle/control kept consistent across surfaces — which module guarantees it.
916
+ */
917
+ function queryAsNamedControlStateSyncSurvey(query) {
918
+ const types = extractTypeNamesFromQuery(query).filter((t) => !isFrameworkUiDecoratorName(t));
919
+ if (types.length < 1 || types.length > 2)
920
+ return false;
921
+ return (/状态一致|保持一致|三处|哪个模块保证|who\s+keeps|consistent\s+state|state\s+sync|guarantees?\s+it|which\s+module\s+guarantees/i.test(query)
922
+ || (/Toggle|Switch|开关/.test(query) && /一致|保证|模块|consistent|guarantees?/i.test(query)));
923
+ }
924
+ /**
925
+ * Methods listed beside a Type for caller inventories
926
+ * (`BinaryGrid … Set、Test 或 Fill` — including short verbs normally treated as noise).
927
+ */
928
+ function extractListedTypeMethodsFromQuery(query) {
929
+ if (!queryAsCallerOrMethodSurvey(query))
930
+ return [];
931
+ const types = extractTypeNamesFromQuery(query);
932
+ if (types.length === 0)
933
+ return [];
934
+ // Owning Type(s): longer CamelCase — short Set/Test/Fill are method tokens, not owners.
935
+ const owners = new Set(types.filter((t) => t.length >= 6 || /(?:Grid|Manager|Service|Controller|Render|Plugin|Layout)/i.test(t)));
936
+ if (owners.size === 0) {
937
+ // Fall back to longest type name as owner.
938
+ const longest = [...types].sort((a, b) => b.length - a.length)[0];
939
+ if (longest)
940
+ owners.add(longest);
941
+ }
942
+ const out = new Set();
943
+ // `Set、Test 或 Fill` / `Set, Test, or Fill` — list of PascalCase methods.
944
+ for (const m of query.matchAll(/\b([A-Z][A-Za-z0-9]{1,})(?:\s*[、,]\s*([A-Z][A-Za-z0-9]{1,}))+(?:\s*(?:或|or)\s*([A-Z][A-Za-z0-9]{1,}))?/g)) {
945
+ for (let i = 1; i < m.length; i++) {
946
+ const name = m[i];
947
+ if (name && !owners.has(name) && name.length >= 2)
948
+ out.add(name);
949
+ }
950
+ }
951
+ // `的 Set` / `methods Set` / `调用了 Set` (owner already required above).
952
+ for (const m of query.matchAll(/(?:的|of|模板类的|类的|methods?)\s+([A-Z][A-Za-z0-9]{1,})\b/g)) {
953
+ const name = m[1];
954
+ if (!owners.has(name) && name.length >= 2)
955
+ out.add(name);
956
+ }
957
+ return [...out].slice(0, 8);
958
+ }
959
+ /** Path module + NAPI / exported API surface (or named Type + NAPI/expose). */
960
+ function queryAsModuleExportSurvey(query) {
961
+ const napi = /NAPI|napi|暴露了哪些|暴露哪些|暴露给|expose(?:d|s)?\s+to|napi_module_register|哪些\s*API|API\s*接口|exports?\s+(?:which|what)|which\s+(?:APIs?|exports?)/i.test(query);
962
+ if (!napi)
963
+ return false;
964
+ if (extractPathSegmentsFromQuery(query).length > 0)
965
+ return true;
966
+ // "LayoutRotatePacking … NAPI expose to ArkTS" — no path segment required.
967
+ if (extractTypeNamesFromQuery(query).length > 0)
968
+ return true;
969
+ return false;
970
+ }
971
+ /** Multi-module path interdependence (A/B/C… mutual deps). */
972
+ function queryAsModuleDependencySurvey(query) {
973
+ const paths = extractPathSegmentsFromQuery(query);
974
+ const moduleTokens = query.match(/\b[A-Za-z][\w]*(?:common|service|component|constants)\b/gi) ?? [];
975
+ const circular = /循环依赖|circular\s+depend|互为依赖/i.test(query);
976
+ // "commonconstants … 循环依赖" — one module token + cycle intent is enough.
977
+ if (circular && (paths.length + moduleTokens.length >= 1 || /\b[a-z][a-z0-9_]{5,}\b/.test(query))) {
978
+ return true;
979
+ }
980
+ if (paths.length + moduleTokens.length < 2)
981
+ return false;
982
+ return /相互依赖|互相依赖|之间.*依赖|依赖关系|depend(?:ency|encies| on each other)|cross-?depend/i.test(query);
983
+ }
984
+ /**
985
+ * Single named Type + state/lifecycle/callback surface — needs the type body,
986
+ * not a thin callers stub.
987
+ */
988
+ /**
989
+ * Repo-wide literal / copy / "imported text object" hunts — Grep territory.
990
+ * Shape-only: no PascalCase Type / file / member / kit anchors.
991
+ */
992
+ function queryLooksLikeLiteralOrCopyHunt(query) {
993
+ if (extractTypeNamesFromQuery(query).length > 0)
994
+ return false;
995
+ if (extractFileBasenamesFromQuery(query).length > 0)
996
+ return false;
997
+ if (extractMemberAccessFromQuery(query).length > 0)
998
+ return false;
999
+ if (extractKitModuleNamesFromQuery(query).length > 0)
1000
+ return false;
1001
+ if (extractFieldLikeSymbolsFromQuery(query).length > 0)
1002
+ return false;
1003
+ return (/全仓|全搜|字面量|中文\s*(?:text|字符串)|绑了.*(?:text|文案)|literal\s+strings?/i.test(query)
1004
+ || /从.{0,16}包.{0,40}(?:导入|import).{0,20}\btext\b|导入的\s*text\s*对象|imported\s+text\s+objects?/i.test(query));
1005
+ }
1006
+ function queryAsTypeLifecycleSurvey(query) {
1007
+ if (queryAsCrossModuleFlowSurvey(query))
1008
+ return false;
1009
+ // "状态来源" is a data-source inventory, not a lifecycle body dump.
1010
+ if (/状态来源|status\s+source|state\s+source/i.test(query))
1011
+ return false;
1012
+ // Release ↔ destructor compare may name CB + Manager + Render.
1013
+ if (/析构|destructor/i.test(query)
1014
+ && /Release|做了哪些|有没有重复|跟.+做的事|same\s+(?:work|things?)|overlap/i.test(query)) {
1015
+ return extractLocalDetailAnchors(query).length >= 1;
1016
+ }
1017
+ const types = extractTypeNamesFromQuery(query).filter((t) => !isFrameworkUiDecoratorName(t));
1018
+ if (types.length < 1 || types.length > 2)
1019
+ return false;
1020
+ return /状态机|状态变化|状态转换|状态.*回调|回调.*状态|foreground|background|lifecycle|state\s*machine|哪些状态|state\s+change|how\s+(?:are|is)\s+.+states?\s+(?:unified|distinguished)/i.test(query);
1021
+ }
1022
+ /**
1023
+ * Member-access inventory ("which files use .drawModifier") — not Type×method
1024
+ * interaction / Export fail wiring / Release↔dtor (those need compact bodies).
1025
+ */
1026
+ function shouldPreferMemberUsageInventory(query) {
1027
+ if (queryAsCoNamedTypeMethodInteraction(query))
1028
+ return false;
1029
+ if (queryAsTypeLifecycleSurvey(query))
1030
+ return false;
1031
+ if (queryAsFieldUsageSurvey(query))
1032
+ return true;
1033
+ if (extractMemberAccessFromQuery(query).length === 0)
1034
+ return false;
1035
+ // Conditional wiring / interaction / purpose → compact, not member-file list.
1036
+ if (/失败时|还会被|是怎么|交互|析构|destructor|做了哪些|什么作用|做什么|purpose|what\s+does/i.test(query)) {
1037
+ return false;
1038
+ }
1039
+ return /哪些|哪里|where|which|使用了|谁用|call\s*sites?/i.test(query);
499
1040
  }
500
1041
  /** API / SDK tokens named in the query — lowercase deps + PascalCase modules (Telephony). */
501
1042
  function extractApiUsageTokens(query) {
@@ -505,18 +1046,46 @@ function extractApiUsageTokens(query) {
505
1046
  tokens.add(d);
506
1047
  }
507
1048
  for (const t of extractTypeNamesFromQuery(query)) {
508
- if (t.length >= 4)
1049
+ if (t.length >= 4 && !isFrameworkUiDecoratorName(t))
509
1050
  tokens.add(t);
510
1051
  }
511
- return [...tokens];
1052
+ for (const ma of extractMemberAccessFromQuery(query)) {
1053
+ if (ma.member.length >= 3)
1054
+ tokens.add(ma.member);
1055
+ }
1056
+ for (const f of extractFieldLikeSymbolsFromQuery(query))
1057
+ tokens.add(f);
1058
+ for (const m of query.matchAll(/\b([A-Z][A-Z0-9]*(?:_[A-Z0-9]+)+)\b/g)) {
1059
+ if (m[1] && m[1].length >= 6)
1060
+ tokens.add(m[1]);
1061
+ }
1062
+ // When a PascalCase SDK/module token is present (Telephony, DrawContext), drop
1063
+ // short lowercase bag noise agents add (`call`/`radio`/`usage`) — those flood
1064
+ // false-positive files and tank precision.
1065
+ const primary = [...tokens].filter((t) => (/^[A-Z]/.test(t) && t.length >= 4)
1066
+ || t.startsWith('@')
1067
+ || /^(?:statfs|napi_\w+)$/i.test(t)
1068
+ || extractFieldLikeSymbolsFromQuery(query).includes(t)
1069
+ || extractMemberAccessFromQuery(query).some((m) => m.member === t || m.dotted === t));
1070
+ if (primary.length > 0)
1071
+ return primary.slice(0, 8);
1072
+ return [...tokens].filter((t) => t.length >= 4 && !exports.GENERIC_VERB_ANCHOR_NOISE.has(t.toLowerCase())).slice(0, 8);
512
1073
  }
513
1074
  function shouldBuildApiUsageSurvey(query) {
1075
+ // Declaration-site surveys have their own section — do NOT also claim API-usage
1076
+ // (that used to fire after an empty declaration scan and dump include hits).
1077
+ if (queryAsDeclarationSiteSurvey(query))
1078
+ return false;
1079
+ if (queryAsConstantUsageSurvey(query))
1080
+ return true;
1081
+ if (queryAsFieldUsageSurvey(query))
1082
+ return true;
514
1083
  return queryAsApiUsageSurvey(query) && extractApiUsageTokens(query).length > 0;
515
1084
  }
516
1085
  /** Caller / method survey intent — not every PascalCase token. */
517
1086
  function queryAsCallerOrMethodSurvey(query) {
518
- return (/哪里调用|何处调用|谁调用|调用了|callers?\s+of|who\s+calls|called\s+by|哪些.*调用|外部.*调用|\bexternal\b/i.test(query)
519
- || /哪些方法|which\s+methods|methods?\s+(?:are\s+)?called/i.test(query)
1087
+ return (/哪里调用|何处调用|谁调用|调用了|被调用|callers?\s+of|who\s+calls|called\s+by|哪些.*调用|外部.*调用|\bexternal\b/i.test(query)
1088
+ || /哪些方法|which\s+methods|methods?\s+(?:are\s+)?called|which\s+files?.{0,20}(?:call|invoke)/i.test(query)
520
1089
  // Agents often write "Type … methods … callers" / bare "callers" without "of".
521
1090
  || /\bcallers?\b/i.test(query)
522
1091
  || /methods?.{0,40}\bcall/i.test(query));
@@ -563,6 +1132,9 @@ function shouldBuildCallerInventory(query) {
563
1132
  && /使用了哪些调用|哪些调用方法|调用方法|使用了哪些方法/.test(query)) {
564
1133
  return false;
565
1134
  }
1135
+ // Return-value consumers: list callers of the named member/fn.
1136
+ if (queryAsReturnValueConsumerSurvey(query))
1137
+ return true;
566
1138
  // Require real caller/method intent — bare PascalCase alone is NOT enough
567
1139
  // (that emptied inventory → blocked compact → 15K full explore on callbacks).
568
1140
  return queryAsCallerOrMethodSurvey(query);
@@ -570,24 +1142,55 @@ function shouldBuildCallerInventory(query) {
570
1142
  /** Symbols to list callers for — types, free functions (SortWidgets), snake_case APIs. */
571
1143
  function extractCallerSurveySymbols(query) {
572
1144
  const names = new Set(extractTypeNamesFromQuery(query));
573
- if (!queryAsCallerOrMethodSurvey(query))
574
- return [...names];
1145
+ const wantMembers = queryAsCallerOrMethodSurvey(query) || queryAsReturnValueConsumerSurvey(query);
1146
+ if (!wantMembers)
1147
+ return [...names].filter((n) => n.length >= 3);
575
1148
  for (const d of extractDependencySymbolsFromQuery(query)) {
576
1149
  if (d.length >= 4)
577
1150
  names.add(d);
578
1151
  }
579
- return [...names].filter((n) => n.length >= 3);
1152
+ for (const ma of extractMemberAccessFromQuery(query)) {
1153
+ names.add(ma.member);
1154
+ if (ma.receiver)
1155
+ names.add(ma.receiver);
1156
+ }
1157
+ for (const a of extractLocalDetailAnchors(query)) {
1158
+ if (isMemberLikeIdentifier(a) || /^(?:get|set|is|has|create|on)[A-Z]/.test(a)) {
1159
+ names.add(a);
1160
+ }
1161
+ }
1162
+ // Type + listed methods (BinaryGrid Set/Test/Fill) — keep short verbs co-named with a Type.
1163
+ for (const m of extractListedTypeMethodsFromQuery(query))
1164
+ names.add(m);
1165
+ // "通过 getConnectionState 获取…" — bare camelCase after 通过/via is the consumer target.
1166
+ for (const m of query.matchAll(/(?:通过|经由|via|using|call(?:ing)?)\s*([a-z][a-zA-Z0-9]{3,})\b/g)) {
1167
+ if (m[1] && (/^(?:get|set|is|has|create|on)[A-Z]/.test(m[1]) || isMemberLikeIdentifier(m[1]))) {
1168
+ names.add(m[1]);
1169
+ }
1170
+ }
1171
+ const all = [...names].filter((n) => n.length >= 3);
1172
+ if (!queryAsReturnValueConsumerSurvey(query))
1173
+ return all;
1174
+ // Prefer the member whose return value is consumed over a preceding create/factory.
1175
+ const preferred = all.filter((n) => /^(?:get|is|has)[A-Z]/.test(n) || /State|Result|Status|Value$/i.test(n));
1176
+ if (preferred.length === 0)
1177
+ return all;
1178
+ const rest = all.filter((n) => !preferred.includes(n));
1179
+ return [...preferred, ...rest];
580
1180
  }
581
1181
  /** Cross-module call-chain intent (A → B → engine). */
582
1182
  function queryAsCrossModuleFlowSurvey(query) {
583
1183
  const types = extractTypeNamesFromQuery(query);
584
1184
  if (types.length < 2)
585
1185
  return false;
586
- return /经过|跨模块|调用链|链路|流程|到达|落到|再到|渲染|落盘|then\s+to|across\s+modules/i.test(query);
1186
+ // Note: bare 渲染 alone is too common (flag-impact / UI surface) — require a
1187
+ // real cross-hop cue with it.
1188
+ return (/经过|跨模块|调用链|链路|流程|到达|落到|再到|落盘|then\s+to|across\s+modules/i.test(query)
1189
+ || (/渲染/.test(query) && /跨模块|经过|再到|落到|链路|call\s*chain/i.test(query)));
587
1190
  }
588
1191
  /** Data-source / upstream-service intent (来源于哪个服务 / which service). */
589
1192
  function queryAsDataSourceSurvey(query) {
590
- return /来源于|数据来源|来自哪个|哪个系统服务|来自什么服务|data\s+source|which\s+service|来源.*服务/i.test(query);
1193
+ return /来源于|数据来源|状态来源|来自哪个|哪个系统服务|来自什么服务|data\s+source|status\s+source|state\s+source|which\s+service|来源.*服务|如何统一或区分/i.test(query);
591
1194
  }
592
1195
  /**
593
1196
  * Local one/few-symbol behavior or contract — NOT multi-file mechanism / inventory.
@@ -611,6 +1214,8 @@ function extractLocalDetailAnchors(query) {
611
1214
  if (m[1] && m[1].length >= 4)
612
1215
  names.add(m[1]);
613
1216
  }
1217
+ for (const f of extractFieldLikeSymbolsFromQuery(query))
1218
+ names.add(f);
614
1219
  // Drop path/extension/verb noise that long NL explores otherwise seed on
615
1220
  // (`cpp`, `calls`, `include`, `render`…) — that ballooned neighbor fan-out.
616
1221
  const ANCHOR_NOISE = new Set([
@@ -619,6 +1224,16 @@ function extractLocalDetailAnchors(query) {
619
1224
  'calls', 'call', 'calling', 'called', 'callers', 'caller', 'include', 'includes', 'import', 'imports',
620
1225
  'render', 'frame', 'sync', 'thread', 'subthread', 'layout', 'method', 'methods',
621
1226
  'function', 'functions', 'file', 'files', 'src', 'main', 'define', 'visible',
1227
+ 'usage', 'usages', 'api', 'apis', 'project', 'codebase', 'repo',
1228
+ 'language', 'locale', 'i18n', 'radio', 'state', 'communication', 'binding',
1229
+ 'renderer', 'native', 'declaration', 'declarations', 'declared',
1230
+ // Generic UI nouns agents leave in NL (not the PascalCase Type).
1231
+ 'dialog', 'page', 'view', 'button', 'component', 'card', 'icon', 'background',
1232
+ 'animation', 'hover', 'preview', 'image', 'resource', 'network', 'application',
1233
+ 'text', 'layout', 'layouts', 'string', 'strings', 'constant', 'constants',
1234
+ // Shared-lib / GL leftovers after stripping `.so`.
1235
+ 'so', 'dll', 'dylib', 'libeffectrender', 'glesv3', 'opengl', 'context',
1236
+ ...exports.GENERIC_VERB_ANCHOR_NOISE,
622
1237
  ]);
623
1238
  const ranked = [...names].filter((n) => n.length >= 3 && !ANCHOR_NOISE.has(n.toLowerCase()));
624
1239
  // Prefer PascalCase / dotted members first — they're the real anchors.
@@ -626,7 +1241,13 @@ function extractLocalDetailAnchors(query) {
626
1241
  const score = (s) => (/^[A-Z]/.test(s) ? 2 : 0) + (s.includes('.') ? 1 : 0) + Math.min(s.length, 20) / 20;
627
1242
  return score(b) - score(a) || a.localeCompare(b);
628
1243
  });
629
- return ranked.slice(0, 4);
1244
+ // Drop framework decorator names when a concrete UI Type is also present
1245
+ // (`@CustomDialog` + WallpaperApplyDialog → keep only the Apply* types).
1246
+ const hasConcreteUi = ranked.some((n) => queryLooksLikeUiComponentType(n) && !isFrameworkUiDecoratorName(n));
1247
+ const filtered = hasConcreteUi
1248
+ ? ranked.filter((n) => !isFrameworkUiDecoratorName(n))
1249
+ : ranked;
1250
+ return filtered.slice(0, 4);
630
1251
  }
631
1252
  /**
632
1253
  * Caller survey that also asks how a co-named type becomes visible / used.
@@ -638,8 +1259,8 @@ function queryNeedsCoNamedUseBridge(query) {
638
1259
  const anchors = extractLocalDetailAnchors(query);
639
1260
  if (anchors.length < 2)
640
1261
  return false;
641
- // Narrow intent only bare "visible/include/import" matched too many NL queries.
642
- return /定义可见|确保.*可见|头文件可见|definition\s+visible|visible\s+definition|how\s+.+\s+visible/i.test(query);
1262
+ // Narrow intent — how a co-named type becomes visible / included.
1263
+ return /定义可见|确保.*可见|头文件可见|definition\s+visible|visible\s+definition|how\s+.+\s+visible|makes?\s+.+\s+visible|如何确保.*可见/i.test(query);
643
1264
  }
644
1265
  function queryAsLocalSymbolDetail(query) {
645
1266
  if (queryAsCrossModuleFlowSurvey(query))
@@ -648,7 +1269,25 @@ function queryAsLocalSymbolDetail(query) {
648
1269
  return false;
649
1270
  if (shouldBuildKitModuleUsageSurvey(query))
650
1271
  return false;
651
- if (shouldBuildApiUsageSurvey(query))
1272
+ // Cross-file API / declaration / constant / field inventories — not definition-only.
1273
+ if (shouldBuildApiUsageSurvey(query) && !queryAsTypeLifecycleSurvey(query))
1274
+ return false;
1275
+ if (queryAsModuleExportSurvey(query) || queryAsModuleDependencySurvey(query))
1276
+ return false;
1277
+ if (queryAsInRepoSystemCapabilityHowto(query))
1278
+ return false;
1279
+ if (queryAsReturnValueConsumerSurvey(query))
1280
+ return false;
1281
+ if (queryAsDeclarationSiteSurvey(query))
1282
+ return false;
1283
+ // lib*.so + GL thread → light-mechanism, not compact OpenGL/context seeds.
1284
+ if (queryAsNativeRenderThreadSurvey(query))
1285
+ return false;
1286
+ // Assigned flag impact → compact body. Named Toggle sync may have zero FTS
1287
+ // hits for the Type — inventory owns that shape instead.
1288
+ if (queryAsAssignedFlagImpactSurvey(query))
1289
+ return true;
1290
+ if (queryAsNamedControlStateSyncSurvey(query))
652
1291
  return false;
653
1292
  // Caller + co-named visibility → compact (body + callers), not inventory-only.
654
1293
  if (queryNeedsCoNamedUseBridge(query))
@@ -660,8 +1299,19 @@ function queryAsLocalSymbolDetail(query) {
660
1299
  // Named UI control + event: compact body + callees (not import inventory).
661
1300
  if (queryAsNamedComponentAction(query))
662
1301
  return true;
663
- // Type.member / Type + isFoo co-named even if the agent stripped Chinese verbs.
664
- if (queryHasNamedMemberFocus(query) && !queryAsMechanismSurvey(query))
1302
+ // Page/Component surface or 2–3 UI Type cluster body, not full-graph explore.
1303
+ if (queryAsComponentSurfaceSurvey(query) || queryAsFocusedUiCluster(query))
1304
+ return true;
1305
+ // Named Type + state/lifecycle surface — full type body.
1306
+ if (queryAsTypeLifecycleSurvey(query))
1307
+ return true;
1308
+ // Release vs destructor / multi-method compare on named anchors.
1309
+ if (extractLocalDetailAnchors(query).length >= 2
1310
+ && /析构|destructor|有没有重复|是否重复|跟.+做的事|做了哪些事情/i.test(query)) {
1311
+ return true;
1312
+ }
1313
+ // Type.member / Type + isFoo co-named — even if the agent used 怎么/如何 NL.
1314
+ if (queryHasNamedMemberFocus(query))
665
1315
  return true;
666
1316
  if (queryAsMechanismSurvey(query))
667
1317
  return false;
@@ -689,7 +1339,12 @@ function queryHasFocusedNamedAnchors(query) {
689
1339
  if (shouldTryFastInventoryExplore(query))
690
1340
  return false;
691
1341
  const anchors = extractLocalDetailAnchors(query);
692
- return anchors.length >= 1 && anchors.length <= 3;
1342
+ if (anchors.length < 1 || anchors.length > 3)
1343
+ return false;
1344
+ // Require a real code anchor (PascalCase Type / member-like), not leftover
1345
+ // English topic nouns that survived domain extraction (application, …).
1346
+ return (extractMemberAccessFromQuery(query).length > 0
1347
+ || anchors.some((a) => /^[A-Z]/.test(a) || isMemberLikeIdentifier(a)));
693
1348
  }
694
1349
  /**
695
1350
  * Prefer a tight explore budget: local-detail questions, or named-symbol
@@ -702,13 +1357,14 @@ function shouldUseCompactExploreBudget(query) {
702
1357
  // do not shrink those into a definition-only budget.
703
1358
  if (shouldTryFastInventoryExplore(query))
704
1359
  return false;
705
- if (queryAsLocalSymbolDetail(query))
1360
+ if (queryAsLocalSymbolDetail(query) || queryAsFocusedUiCluster(query) || queryAsComponentSurfaceSurvey(query)) {
706
1361
  return true;
1362
+ }
707
1363
  if (queryIsTypeNameFocus(query))
708
1364
  return false;
709
- const types = extractTypeNamesFromQuery(query);
1365
+ const types = extractTypeNamesFromQuery(query).filter((t) => !isFrameworkUiDecoratorName(t));
710
1366
  const deps = extractDependencySymbolsFromQuery(query);
711
- return types.length + deps.length >= 1 && types.length <= 2;
1367
+ return types.length + deps.length >= 1 && types.length <= 3;
712
1368
  }
713
1369
  /** Keep rendering to files that define symbols named in the query. */
714
1370
  function shouldFocusOnQueryNamedDefs(query, hasFlowPath, multiAnchorQuery) {
@@ -762,21 +1418,124 @@ function extractMechanismEntrySeeds(query) {
762
1418
  seeds.add(term);
763
1419
  }
764
1420
  }
1421
+ // Shared-lib + GLES/EGL thread — seed the usual native GL owners (not `.so` / Context).
1422
+ if (queryAsNativeRenderThreadSurvey(query)) {
1423
+ seeds.add('EGLCore');
1424
+ seeds.add('PluginRender');
1425
+ if (/\bCMakeLists\b|\.so\b|链接|link/i.test(query))
1426
+ seeds.add('CMakeLists');
1427
+ }
1428
+ // Theme pack download→parse→install — agents often omit PascalCase names.
1429
+ if (/主题|theme\s*pack|theme\s*package/i.test(query) && /安装|解析|下载|install|parse|download/i.test(query)) {
1430
+ for (const s of [
1431
+ 'ThemePackParser', 'ThemePackageInstaller', 'SkinInstaller', 'IconsInstaller', 'FontInstaller',
1432
+ ]) {
1433
+ seeds.add(s);
1434
+ }
1435
+ }
1436
+ // Notification subscribe/manage howto — same pattern (CJK domain, no Type named).
1437
+ if (/通知|notification/i.test(query) && /订阅|subscribe|管理|manage|多线程|multithread/i.test(query)) {
1438
+ for (const s of ['NotificationManager', 'NotificationSubscriber', 'AnsManager', 'SubscribeCallback']) {
1439
+ seeds.add(s);
1440
+ }
1441
+ }
1442
+ // XML parse howto — seed the real API, not bare `xml` (which floods every Xml* path).
1443
+ if (/\bxml\b|xml解析|解析xml/i.test(query) && /解析|parse|convert|实现/i.test(query)) {
1444
+ for (const s of ['convertxml', 'XmlParseUtil', 'ConvertXML', 'fastConvertToJSObject']) {
1445
+ seeds.add(s);
1446
+ }
1447
+ }
765
1448
  return [...seeds].filter((s) => s.length >= 3).slice(0, 12);
766
1449
  }
767
1450
  /**
768
1451
  * Lightweight mechanism explore — seed symbols + flow spine only, no full
769
1452
  * findRelevantContext (keeps MCP calls fast and avoids agent grep fallback).
1453
+ *
1454
+ * Domain-mechanism *bag*: agents often strip the how-phrase and keep only
1455
+ * keywords (`xml parse`, `foo convert`). Structural — distinctive ASCII token
1456
+ * plus an action verb or a CJK domain token; named Types take other paths.
770
1457
  */
1458
+ function queryAsDomainMechanismBag(query) {
1459
+ if (queryAsMechanismSurvey(query))
1460
+ return false;
1461
+ if (queryAsInterpretationSurvey(query))
1462
+ return false;
1463
+ // Named Type / Type.member / UI control → compact / inventory / click-flow.
1464
+ if (extractTypeNamesFromQuery(query).length > 0)
1465
+ return false;
1466
+ if (extractMemberAccessFromQuery(query).length > 0)
1467
+ return false;
1468
+ if (queryAsNamedComponentAction(query))
1469
+ return false;
1470
+ // Hover/悬停 bags without a how-implemented cue are UI-behavior, not mechanism.
1471
+ if (/悬停|\bhover\b|onHover/i.test(query)
1472
+ && !/\b(?:parse|implement|manage|handle|process|subscribe|convert)\b/i.test(query)) {
1473
+ return false;
1474
+ }
1475
+ const domain = extractDomainSearchTerms(query);
1476
+ const distinct = domain.filter((t) => /^[A-Za-z][A-Za-z0-9._-]{2,}$/.test(t) && !exports.GENERIC_VERB_ANCHOR_NOISE.has(t.toLowerCase()));
1477
+ if (distinct.length === 0)
1478
+ return false;
1479
+ const hasActionVerb = domain.some((t) => exports.GENERIC_VERB_ANCHOR_NOISE.has(t.toLowerCase()))
1480
+ || /\b(?:parse|implement|manage|handle|process|subscribe|convert|encode|decode)(?:s|d|ing)?\b/i.test(query);
1481
+ // Remaining CJK tokens are the topic half of a rewritten how-question bag.
1482
+ const hasCjkDomain = domain.some((t) => /[\u4e00-\u9fff]/.test(t));
1483
+ return hasActionVerb || hasCjkDomain;
1484
+ }
771
1485
  function shouldTryLightMechanismExplore(query) {
772
1486
  if (queryAsCrossModuleFlowSurvey(query))
773
1487
  return false;
774
1488
  if (queryAsInterpretationSurvey(query))
775
1489
  return false;
776
- // Named UI control + event seed the control and follow callees (click flow).
777
- if (queryAsNamedComponentAction(query)) {
778
- return extractTypeNamesFromQuery(query).length >= 1
779
- || extractMechanismEntrySeeds(query).length >= 1;
1490
+ // Literal / copy huntsGrep (never light dump).
1491
+ if (queryLooksLikeLiteralOrCopyHunt(query))
1492
+ return false;
1493
+ // System-setting howto → lean capability inventory, not every `language` import.
1494
+ if (queryAsInRepoSystemCapabilityHowto(query))
1495
+ return false;
1496
+ // Return-value consumers → callers of the member, not a seed+flow dump.
1497
+ if (queryAsReturnValueConsumerSurvey(query))
1498
+ return false;
1499
+ // Manager data/status source → inventory, not light-mechanism.
1500
+ if (queryAsDataSourceSurvey(query))
1501
+ return false;
1502
+ // Named UI control + event → compact (body + callees + usage sites), not light-mechanism.
1503
+ if (queryAsNamedComponentAction(query))
1504
+ return false;
1505
+ if (queryAsAssignedFlagImpactSurvey(query) || queryAsNamedControlStateSyncSurvey(query)) {
1506
+ return false;
1507
+ }
1508
+ // Kit in-repo usage / extra-deps → kit inventory, not a mechanism dump.
1509
+ if (shouldBuildKitModuleUsageSurvey(query))
1510
+ return false;
1511
+ // Caller + "how is Type visible" → compact (callers + include), not light.
1512
+ if (queryNeedsCoNamedUseBridge(query))
1513
+ return false;
1514
+ // Named Type.member / UI cluster / local pinpoint → compact, even if NL has 怎么/如何.
1515
+ if (queryHasNamedMemberFocus(query)
1516
+ || queryAsFocusedUiCluster(query)
1517
+ || queryAsComponentSurfaceSurvey(query)
1518
+ || queryAsLocalSymbolDetail(query)) {
1519
+ return false;
1520
+ }
1521
+ // Declaration / constant / field / module-export inventories own these shapes.
1522
+ if (queryAsDeclarationSiteSurvey(query)
1523
+ || queryAsConstantUsageSurvey(query)
1524
+ || queryAsFieldUsageSurvey(query)
1525
+ || queryAsModuleExportSurvey(query)
1526
+ || queryAsModuleDependencySurvey(query)
1527
+ || queryAsTypeLifecycleSurvey(query)
1528
+ || shouldBuildApiUsageSurvey(query)
1529
+ || queryAsDtsWrapSurvey(query)) {
1530
+ return false;
1531
+ }
1532
+ // lib*.so + GLES/EGL + which thread → CMake/EGLCore spine, not `.so` member dump.
1533
+ if (queryAsNativeRenderThreadSurvey(query))
1534
+ return true;
1535
+ if (queryAsDomainMechanismBag(query)) {
1536
+ if (queryLooksLikeLiteralOrCopyHunt(query))
1537
+ return false;
1538
+ return extractDomainSearchTerms(query).length >= 1;
780
1539
  }
781
1540
  if (!queryAsMechanismSurvey(query))
782
1541
  return false;
@@ -793,38 +1552,76 @@ function shouldTryFastInventoryExplore(query) {
793
1552
  return false;
794
1553
  if (queryAsInterpretationSurvey(query))
795
1554
  return false;
1555
+ if (queryLooksLikeLiteralOrCopyHunt(query))
1556
+ return false;
796
1557
  // Caller paths alone cannot answer "which call makes Type X visible" — compact.
797
1558
  if (queryNeedsCoNamedUseBridge(query))
798
1559
  return false;
799
- // One named symbol / UI action / Type.member inventory path is the wrong shape
800
- // (returns import sites for isExpired/onClick and agents fall back to Grep).
801
- if (queryAsLocalSymbolDetail(query) || queryHasNamedMemberFocus(query) || queryAsNamedComponentAction(query)) {
1560
+ // Type lifecycle / Release↔destructor compares need bodies, not path lists.
1561
+ if (queryAsTypeLifecycleSurvey(query))
1562
+ return false;
1563
+ // Type×method interaction (CanPlace/Place ↔ Grid) → compact, not member inventory.
1564
+ if (queryAsCoNamedTypeMethodInteraction(query))
1565
+ return false;
1566
+ // Import / kit-usage / domain / API / config / data-source listings win even if
1567
+ // a dotted `@kit.X` once looked like Type.member (defense in depth).
1568
+ // Member-pattern inventories only when the question is a usage listing.
1569
+ if (shouldBuildKitModuleUsageSurvey(query)
1570
+ || shouldBuildApiUsageSurvey(query)
1571
+ || shouldBuildDomainFileSurvey(query)
1572
+ || shouldBuildConfigSection(query)
1573
+ || queryAsDataSourceSurvey(query)
1574
+ || shouldBuildHoverHandlerSurvey(query)
1575
+ || queryAsModuleExportSurvey(query)
1576
+ || queryAsModuleDependencySurvey(query)
1577
+ || queryAsDtsWrapSurvey(query)
1578
+ || queryAsNamedControlStateSyncSurvey(query)
1579
+ || shouldPreferMemberUsageInventory(query)
1580
+ || queryAsInRepoSystemCapabilityHowto(query)
1581
+ || queryAsReturnValueConsumerSurvey(query)
1582
+ || queryAsDeclarationSiteSurvey(query)) {
1583
+ return true;
1584
+ }
1585
+ // One named symbol / UI action / Type.member / component surface — inventory is wrong.
1586
+ if (queryAsLocalSymbolDetail(query)
1587
+ || queryHasNamedMemberFocus(query)
1588
+ || queryAsNamedComponentAction(query)
1589
+ || queryAsComponentSurfaceSurvey(query)
1590
+ || queryAsFocusedUiCluster(query)) {
802
1591
  return false;
803
1592
  }
804
1593
  const types = extractTypeNamesFromQuery(query);
805
- if (types.length >= 2 && !queryAsCallerOrMethodSurvey(query))
1594
+ if (types.length >= 2 && !queryAsCallerOrMethodSurvey(query) && !queryAsInheritanceSurvey(query)) {
806
1595
  return false;
1596
+ }
807
1597
  const inventoryDeps = extractDependencySymbolsFromQuery(query).filter((d) => !isMemberLikeIdentifier(d));
808
- return (shouldBuildDomainFileSurvey(query)
809
- || shouldBuildApiUsageSurvey(query)
810
- || shouldBuildKitModuleUsageSurvey(query)
811
- || (hasImportInventoryFilter(query)
812
- && (inventoryDeps.length > 0 || extractKitModuleNamesFromQuery(query).length > 0)
813
- && inventoryDeps.length > 0)
1598
+ return ((hasImportInventoryFilter(query)
1599
+ && (inventoryDeps.length > 0 || extractKitModuleNamesFromQuery(query).length > 0)
1600
+ && inventoryDeps.length > 0)
814
1601
  || shouldBuildCallerInventory(query)
815
- || shouldBuildInheritanceSurvey(query)
816
- || shouldBuildMemberSurvey(query)
817
- || shouldBuildConfigSection(query)
818
- || queryAsDataSourceSurvey(query));
1602
+ || shouldBuildInheritanceSurvey(query));
819
1603
  }
820
1604
  function shouldBuildInheritanceSurvey(query) {
821
1605
  if (queryAsInheritanceSurvey(query) && extractTypeNamesFromQuery(query).length > 0)
822
1606
  return true;
1607
+ // UI Page/Component overview needs the body — not an empty subtype list.
1608
+ if (queryAsComponentSurfaceSurvey(query))
1609
+ return false;
823
1610
  // Bare / near-bare type queries — agent search("Foo") for subtypes/overview.
824
- return queryIsTypeNameFocus(query);
1611
+ if (queryIsTypeNameFocus(query)) {
1612
+ const types = extractTypeNamesFromQuery(query);
1613
+ if (types.some(queryLooksLikeUiComponentType))
1614
+ return false;
1615
+ return true;
1616
+ }
1617
+ return false;
825
1618
  }
826
1619
  function shouldBuildMemberSurvey(query) {
827
- return extractMemberAccessFromQuery(query).length > 0;
1620
+ const members = extractMemberAccessFromQuery(query);
1621
+ if (members.length > 0)
1622
+ return true;
1623
+ // Field/mutex co-use without a leading dot still needs the text-usage inventory.
1624
+ return queryAsFieldUsageSurvey(query);
828
1625
  }
829
1626
  function shouldBuildConfigSection(query) {
830
1627
  return queryNamesConfigFile(query);
@@ -851,28 +1648,28 @@ function extractDomainSearchTerms(query) {
851
1648
  terms.delete(kit);
852
1649
  terms.delete(kit.charAt(0).toLowerCase() + kit.slice(1));
853
1650
  }
1651
+ // Always keep ASCII code tokens (xml, parse, subscribe…) — mechanism NL often
1652
+ // mixes them with Chinese and used to drop them unless a @kit/path was present.
1653
+ for (const m of query.matchAll(/\b([A-Za-z][A-Za-z0-9]{2,})\b/g)) {
1654
+ const w = m[1];
1655
+ if (!exports.STOP_WORDS.has(w.toLowerCase()))
1656
+ terms.add(w);
1657
+ }
854
1658
  const stripped = query
855
1659
  .replace(/[@#][\w.]+/g, ' ')
856
- .replace(/哪些文件|有哪些|什么|如何|怎样|怎么|项目中|项目里|项目|涉及|相关的?|文件|模块|功能|吗|?|\?|有使用|是否|有没有|使用|不同|区别|对比|这个|那个|是怎样|是如何|怎么|如何|它与|它和/g, ' ')
1660
+ .replace(/哪些文件|有哪些|什么|如何|怎样|怎么|项目中|项目里|项目|涉及|相关的?|文件|模块|功能|吗|?|\?|有使用|是否|有没有|使用|不同|区别|对比|这个|那个|是怎样|是如何|怎么|如何|它与|它和|实现|的/g, ' ')
857
1661
  .replace(/\s+/g, ' ')
858
1662
  .trim();
859
- for (const m of stripped.matchAll(/[\u4e00-\u9fff]{2,12}/g)) {
1663
+ for (const m of stripped.matchAll(/[\u4e00-\u9fff]{2,8}/g)) {
860
1664
  let t = m[0].replace(/^与/, '');
861
1665
  if (t.length >= 2)
862
1666
  terms.add(t);
863
1667
  }
864
- if (queryMentionsCodeContext(query)) {
865
- for (const m of query.matchAll(/\b([a-z][a-zA-Z0-9]{4,})\b/g)) {
866
- const w = m[1];
867
- if (!exports.STOP_WORDS.has(w.toLowerCase()))
868
- terms.add(w);
869
- }
870
- }
871
1668
  return [...terms].filter((t) => t.length >= 2).slice(0, 12);
872
1669
  }
873
1670
  /** How/implementation/mechanism intent — needs entry symbols + flow, not flat file list. */
874
1671
  function queryAsMechanismSurvey(query) {
875
- return /如何(?:实现|获取|做|处理|管理)|怎样(?:实现|获取|做)|怎么(?:实现|获取|做)|是如何|是怎么|how\s+(?:is|does|are|to\s+(?:get|implement|work))|机制|流程|架构|多线程|多进程|multithread|multi-thread/i.test(query);
1672
+ return /如何(?:实现|获取|做|处理|管理)|怎样(?:实现|获取|做)|怎么(?:实现|获取|做)|是如何|是怎么|how\s+(?:is|does|are|to\s+(?:get|implement|work))|机制|流程|架构|多线程|多进程|multithread|multi-thread|会走到哪些|步骤中会|解析和安装|下载完|下载后/i.test(query);
876
1673
  }
877
1674
  /**
878
1675
  * Broad domain survey — related files, usage existence, or concept comparison.
@@ -881,6 +1678,8 @@ function queryAsMechanismSurvey(query) {
881
1678
  function queryAsDomainFileSurvey(query) {
882
1679
  if (queryAsKitModuleCapabilitySurvey(query))
883
1680
  return false;
1681
+ if (queryLooksLikeLiteralOrCopyHunt(query))
1682
+ return false;
884
1683
  if (queryAsCallerOrMethodSurvey(query))
885
1684
  return false;
886
1685
  if (shouldBuildApiUsageSurvey(query))
@@ -893,6 +1692,16 @@ function queryAsDomainFileSurvey(query) {
893
1692
  return false;
894
1693
  if (queryAsMechanismSurvey(query))
895
1694
  return false;
1695
+ // Module dep → dedicated cycle survey. Module NAPI export → dedicated export
1696
+ // survey (must NOT become a 50-file domain dump of every "API"/"NAPI" token).
1697
+ if (queryAsModuleDependencySurvey(query))
1698
+ return true;
1699
+ if (queryAsModuleExportSurvey(query) || queryAsDtsWrapSurvey(query))
1700
+ return false;
1701
+ // Step / "which code runs" flow questions are mechanism — not domain file lists.
1702
+ if (/会走到|步骤中|解析和安装|下载完|下载后|call\s*chain|which\s+code\s+(?:runs|paths?)/i.test(query)) {
1703
+ return false;
1704
+ }
896
1705
  const listing = /哪些文件|有哪些.*文件|相关的?\s*文件|涉及.*文件|which\s+files?|files?\s+(?:are\s+)?related/i.test(query);
897
1706
  const usage = /有使用|是否使用|有没有使用|使用了.*吗|is\s+.+\s+used/i.test(query);
898
1707
  const compare = /有什么不同|区别|difference|vs\.?|versus|对比/i.test(query);