@blxzer/cursor-trellis 0.1.0 → 0.1.1

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 (279) hide show
  1. package/README.md +124 -5
  2. package/README.zh-CN.md +170 -0
  3. package/dist/cli/index.d.ts.map +1 -1
  4. package/dist/cli/index.js +1 -33
  5. package/dist/cli/index.js.map +1 -1
  6. package/dist/commands/init.d.ts +0 -13
  7. package/dist/commands/init.d.ts.map +1 -1
  8. package/dist/commands/init.js +7 -14
  9. package/dist/commands/init.js.map +1 -1
  10. package/dist/commands/uninstall.d.ts.map +1 -1
  11. package/dist/commands/uninstall.js +3 -32
  12. package/dist/commands/uninstall.js.map +1 -1
  13. package/dist/commands/update.d.ts.map +1 -1
  14. package/dist/commands/update.js +7 -84
  15. package/dist/commands/update.js.map +1 -1
  16. package/dist/configurators/index.d.ts +7 -8
  17. package/dist/configurators/index.d.ts.map +1 -1
  18. package/dist/configurators/index.js +11 -236
  19. package/dist/configurators/index.js.map +1 -1
  20. package/dist/configurators/workflow.d.ts.map +1 -1
  21. package/dist/configurators/workflow.js +5 -1
  22. package/dist/configurators/workflow.js.map +1 -1
  23. package/dist/templates/common/bundled-skills/smart-search-cli/SKILL.md +3 -3
  24. package/dist/templates/common/bundled-skills/trellis-meta/SKILL.md +4 -2
  25. package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-agents.md +8 -15
  26. package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-hooks.md +7 -7
  27. package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-skills-or-commands.md +18 -21
  28. package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/overview.md +2 -2
  29. package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/generated-files.md +10 -7
  30. package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/overview.md +4 -4
  31. package/dist/templates/common/bundled-skills/trellis-meta/references/platform-files/agents.md +20 -31
  32. package/dist/templates/common/bundled-skills/trellis-meta/references/platform-files/hooks-and-settings.md +20 -31
  33. package/dist/templates/common/bundled-skills/trellis-meta/references/platform-files/overview.md +24 -40
  34. package/dist/templates/common/bundled-skills/trellis-meta/references/platform-files/platform-map.md +28 -59
  35. package/dist/templates/common/bundled-skills/trellis-meta/references/platform-files/skills-and-commands.md +12 -28
  36. package/dist/templates/common/bundled-skills/trellis-skill-creator/references/trellis-skill-locations.md +2 -2
  37. package/dist/templates/cursor/hooks.json +6 -0
  38. package/dist/templates/cursor/rules/retrieval-routing.mdc +57 -0
  39. package/dist/templates/extract.d.ts +0 -14
  40. package/dist/templates/extract.d.ts.map +1 -1
  41. package/dist/templates/extract.js +0 -34
  42. package/dist/templates/extract.js.map +1 -1
  43. package/dist/templates/markdown/agents.md +2 -6
  44. package/dist/templates/markdown/index.d.ts +1 -0
  45. package/dist/templates/markdown/index.d.ts.map +1 -1
  46. package/dist/templates/markdown/index.js +1 -0
  47. package/dist/templates/markdown/index.js.map +1 -1
  48. package/dist/templates/markdown/prompts/run-semantic-slice-12.md.txt +41 -0
  49. package/dist/templates/markdown/spec/guides/code-reuse-thinking-guide.md.txt +14 -14
  50. package/dist/templates/markdown/spec/guides/cross-layer-thinking-guide.md.txt +8 -20
  51. package/dist/templates/markdown/spec/guides/cross-platform-thinking-guide.md.txt +15 -14
  52. package/dist/templates/markdown/spec/guides/cursor-context-injection-guide.md.txt +2 -1
  53. package/dist/templates/markdown/spec/guides/cursor-semantic-compliance.md.txt +49 -0
  54. package/dist/templates/markdown/spec/guides/cursor-subagent-policy.md.txt +2 -2
  55. package/dist/templates/markdown/spec/guides/retrieval-daily-guide.md.txt +68 -20
  56. package/dist/templates/shared-hooks/index.d.ts +1 -1
  57. package/dist/templates/shared-hooks/index.d.ts.map +1 -1
  58. package/dist/templates/shared-hooks/index.js +1 -0
  59. package/dist/templates/shared-hooks/index.js.map +1 -1
  60. package/dist/templates/shared-hooks/inject-retrieval-plan.py +163 -0
  61. package/dist/templates/shared-hooks/research-end-retrieval-pack.py +2 -11
  62. package/dist/templates/trellis/index.d.ts +11 -0
  63. package/dist/templates/trellis/index.d.ts.map +1 -1
  64. package/dist/templates/trellis/index.js +22 -0
  65. package/dist/templates/trellis/index.js.map +1 -1
  66. package/dist/templates/trellis/scripts/aggregate_retrieval_telemetry.py +342 -0
  67. package/dist/templates/trellis/scripts/batch_plan_envelope.py +105 -0
  68. package/dist/templates/trellis/scripts/codegraph_session_smoke.py +76 -0
  69. package/dist/templates/trellis/scripts/common/active_task.py +116 -147
  70. package/dist/templates/trellis/scripts/common/cli_adapter.py +34 -710
  71. package/dist/templates/trellis/scripts/common/codebase_retrieval_router.py +49 -106
  72. package/dist/templates/trellis/scripts/common/git_context.py +17 -2
  73. package/dist/templates/trellis/scripts/common/project_file_stats.py +91 -0
  74. package/dist/templates/trellis/scripts/common/retrieval_adapter_metadata.py +9 -11
  75. package/dist/templates/trellis/scripts/common/retrieval_agent_instructions.py +313 -0
  76. package/dist/templates/trellis/scripts/common/retrieval_pack.py +0 -6
  77. package/dist/templates/trellis/scripts/common/retrieval_pack_context.py +0 -4
  78. package/dist/templates/trellis/scripts/common/retrieval_plan_gate.py +76 -0
  79. package/dist/templates/trellis/scripts/common/retrieval_result_ranking.py +253 -0
  80. package/dist/templates/trellis/scripts/common/retrieval_tool_classification.py +157 -0
  81. package/dist/templates/trellis/scripts/common/semantic_plan_gate.py +52 -0
  82. package/dist/templates/trellis/scripts/common/workflow_phase.py +3 -24
  83. package/dist/templates/trellis/scripts/rank_retrieval_candidates.py +80 -0
  84. package/dist/templates/trellis/scripts/route_codebase_retrieval.py +38 -11
  85. package/dist/templates/trellis/scripts/score_evidence.py +112 -0
  86. package/dist/templates/trellis/workflow.md +861 -960
  87. package/dist/types/ai-tools.d.ts +13 -16
  88. package/dist/types/ai-tools.d.ts.map +1 -1
  89. package/dist/types/ai-tools.js +5 -238
  90. package/dist/types/ai-tools.js.map +1 -1
  91. package/dist/utils/codebase-retrieval-router.d.ts +1 -8
  92. package/dist/utils/codebase-retrieval-router.d.ts.map +1 -1
  93. package/dist/utils/codebase-retrieval-router.js +39 -180
  94. package/dist/utils/codebase-retrieval-router.js.map +1 -1
  95. package/dist/utils/project-capabilities.d.ts.map +1 -1
  96. package/dist/utils/project-capabilities.js +10 -24
  97. package/dist/utils/project-capabilities.js.map +1 -1
  98. package/dist/utils/retrieval-agent-instructions.d.ts +14 -0
  99. package/dist/utils/retrieval-agent-instructions.d.ts.map +1 -0
  100. package/dist/utils/retrieval-agent-instructions.js +221 -0
  101. package/dist/utils/retrieval-agent-instructions.js.map +1 -0
  102. package/dist/utils/retrieval-execution-telemetry.d.ts +43 -85
  103. package/dist/utils/retrieval-execution-telemetry.d.ts.map +1 -1
  104. package/dist/utils/retrieval-execution-telemetry.js +289 -22
  105. package/dist/utils/retrieval-execution-telemetry.js.map +1 -1
  106. package/dist/utils/retrieval-result-ranking.js +1 -1
  107. package/dist/utils/retrieval-result-ranking.js.map +1 -1
  108. package/dist/utils/retrieval-tool-classification.d.ts +32 -0
  109. package/dist/utils/retrieval-tool-classification.d.ts.map +1 -0
  110. package/dist/utils/retrieval-tool-classification.js +113 -0
  111. package/dist/utils/retrieval-tool-classification.js.map +1 -0
  112. package/dist/utils/uninstall-scrubbers.d.ts +0 -36
  113. package/dist/utils/uninstall-scrubbers.d.ts.map +1 -1
  114. package/dist/utils/uninstall-scrubbers.js +0 -184
  115. package/dist/utils/uninstall-scrubbers.js.map +1 -1
  116. package/package.json +4 -2
  117. package/dist/commands/mem.d.ts +0 -30
  118. package/dist/commands/mem.d.ts.map +0 -1
  119. package/dist/commands/mem.js +0 -424
  120. package/dist/commands/mem.js.map +0 -1
  121. package/dist/configurators/antigravity.d.ts +0 -7
  122. package/dist/configurators/antigravity.d.ts.map +0 -1
  123. package/dist/configurators/antigravity.js +0 -19
  124. package/dist/configurators/antigravity.js.map +0 -1
  125. package/dist/configurators/claude.d.ts +0 -9
  126. package/dist/configurators/claude.d.ts.map +0 -1
  127. package/dist/configurators/claude.js +0 -72
  128. package/dist/configurators/claude.js.map +0 -1
  129. package/dist/configurators/codebuddy.d.ts +0 -10
  130. package/dist/configurators/codebuddy.d.ts.map +0 -1
  131. package/dist/configurators/codebuddy.js +0 -30
  132. package/dist/configurators/codebuddy.js.map +0 -1
  133. package/dist/configurators/codex.d.ts +0 -8
  134. package/dist/configurators/codex.d.ts.map +0 -1
  135. package/dist/configurators/codex.js +0 -87
  136. package/dist/configurators/codex.js.map +0 -1
  137. package/dist/configurators/copilot.d.ts +0 -10
  138. package/dist/configurators/copilot.d.ts.map +0 -1
  139. package/dist/configurators/copilot.js +0 -51
  140. package/dist/configurators/copilot.js.map +0 -1
  141. package/dist/configurators/droid.d.ts +0 -10
  142. package/dist/configurators/droid.d.ts.map +0 -1
  143. package/dist/configurators/droid.js +0 -30
  144. package/dist/configurators/droid.js.map +0 -1
  145. package/dist/configurators/gemini.d.ts +0 -16
  146. package/dist/configurators/gemini.d.ts.map +0 -1
  147. package/dist/configurators/gemini.js +0 -38
  148. package/dist/configurators/gemini.js.map +0 -1
  149. package/dist/configurators/kilo.d.ts +0 -7
  150. package/dist/configurators/kilo.d.ts.map +0 -1
  151. package/dist/configurators/kilo.js +0 -19
  152. package/dist/configurators/kilo.js.map +0 -1
  153. package/dist/configurators/kiro.d.ts +0 -8
  154. package/dist/configurators/kiro.d.ts.map +0 -1
  155. package/dist/configurators/kiro.js +0 -24
  156. package/dist/configurators/kiro.js.map +0 -1
  157. package/dist/configurators/opencode.d.ts +0 -14
  158. package/dist/configurators/opencode.d.ts.map +0 -1
  159. package/dist/configurators/opencode.js +0 -96
  160. package/dist/configurators/opencode.js.map +0 -1
  161. package/dist/configurators/pi.d.ts +0 -3
  162. package/dist/configurators/pi.d.ts.map +0 -1
  163. package/dist/configurators/pi.js +0 -45
  164. package/dist/configurators/pi.js.map +0 -1
  165. package/dist/configurators/qoder.d.ts +0 -11
  166. package/dist/configurators/qoder.d.ts.map +0 -1
  167. package/dist/configurators/qoder.js +0 -31
  168. package/dist/configurators/qoder.js.map +0 -1
  169. package/dist/configurators/windsurf.d.ts +0 -7
  170. package/dist/configurators/windsurf.d.ts.map +0 -1
  171. package/dist/configurators/windsurf.js +0 -19
  172. package/dist/configurators/windsurf.js.map +0 -1
  173. package/dist/templates/claude/agents/trellis-check.md +0 -124
  174. package/dist/templates/claude/agents/trellis-implement.md +0 -110
  175. package/dist/templates/claude/agents/trellis-research.md +0 -137
  176. package/dist/templates/claude/index.d.ts +0 -22
  177. package/dist/templates/claude/index.d.ts.map +0 -1
  178. package/dist/templates/claude/index.js +0 -46
  179. package/dist/templates/claude/index.js.map +0 -1
  180. package/dist/templates/claude/settings.json +0 -84
  181. package/dist/templates/codebuddy/agents/trellis-check.md +0 -115
  182. package/dist/templates/codebuddy/agents/trellis-implement.md +0 -110
  183. package/dist/templates/codebuddy/agents/trellis-research.md +0 -137
  184. package/dist/templates/codebuddy/index.d.ts +0 -15
  185. package/dist/templates/codebuddy/index.d.ts.map +0 -1
  186. package/dist/templates/codebuddy/index.js +0 -15
  187. package/dist/templates/codebuddy/index.js.map +0 -1
  188. package/dist/templates/codebuddy/settings.json +0 -70
  189. package/dist/templates/codex/agents/trellis-check.toml +0 -67
  190. package/dist/templates/codex/agents/trellis-implement.toml +0 -40
  191. package/dist/templates/codex/agents/trellis-research.toml +0 -73
  192. package/dist/templates/codex/config.toml +0 -28
  193. package/dist/templates/codex/hooks/session-start.py +0 -575
  194. package/dist/templates/codex/hooks.json +0 -15
  195. package/dist/templates/codex/index.d.ts +0 -39
  196. package/dist/templates/codex/index.d.ts.map +0 -1
  197. package/dist/templates/codex/index.js +0 -85
  198. package/dist/templates/codex/index.js.map +0 -1
  199. package/dist/templates/codex/skills/before-dev/SKILL.md +0 -40
  200. package/dist/templates/codex/skills/brainstorm/SKILL.md +0 -112
  201. package/dist/templates/codex/skills/break-loop/SKILL.md +0 -130
  202. package/dist/templates/codex/skills/check/SKILL.md +0 -98
  203. package/dist/templates/codex/skills/check-cross-layer/SKILL.md +0 -158
  204. package/dist/templates/codex/skills/create-command/SKILL.md +0 -101
  205. package/dist/templates/codex/skills/finish-work/SKILL.md +0 -90
  206. package/dist/templates/codex/skills/improve-ut/SKILL.md +0 -69
  207. package/dist/templates/codex/skills/integrate-skill/SKILL.md +0 -221
  208. package/dist/templates/codex/skills/onboard/SKILL.md +0 -363
  209. package/dist/templates/codex/skills/record-session/SKILL.md +0 -67
  210. package/dist/templates/codex/skills/start/SKILL.md +0 -66
  211. package/dist/templates/codex/skills/update-spec/SKILL.md +0 -335
  212. package/dist/templates/copilot/hooks/session-start.py +0 -557
  213. package/dist/templates/copilot/hooks.json +0 -19
  214. package/dist/templates/copilot/index.d.ts +0 -23
  215. package/dist/templates/copilot/index.d.ts.map +0 -1
  216. package/dist/templates/copilot/index.js +0 -54
  217. package/dist/templates/copilot/index.js.map +0 -1
  218. package/dist/templates/copilot/prompts/before-dev.prompt.md +0 -39
  219. package/dist/templates/copilot/prompts/brainstorm.prompt.md +0 -111
  220. package/dist/templates/copilot/prompts/break-loop.prompt.md +0 -129
  221. package/dist/templates/copilot/prompts/check-cross-layer.prompt.md +0 -157
  222. package/dist/templates/copilot/prompts/check.prompt.md +0 -97
  223. package/dist/templates/copilot/prompts/create-command.prompt.md +0 -116
  224. package/dist/templates/copilot/prompts/finish-work.prompt.md +0 -99
  225. package/dist/templates/copilot/prompts/integrate-skill.prompt.md +0 -223
  226. package/dist/templates/copilot/prompts/onboard.prompt.md +0 -362
  227. package/dist/templates/copilot/prompts/parallel.prompt.md +0 -204
  228. package/dist/templates/copilot/prompts/record-session.prompt.md +0 -66
  229. package/dist/templates/copilot/prompts/start.prompt.md +0 -63
  230. package/dist/templates/copilot/prompts/update-spec.prompt.md +0 -358
  231. package/dist/templates/droid/droids/trellis-check.md +0 -107
  232. package/dist/templates/droid/droids/trellis-implement.md +0 -102
  233. package/dist/templates/droid/droids/trellis-research.md +0 -137
  234. package/dist/templates/droid/index.d.ts +0 -15
  235. package/dist/templates/droid/index.d.ts.map +0 -1
  236. package/dist/templates/droid/index.js +0 -15
  237. package/dist/templates/droid/index.js.map +0 -1
  238. package/dist/templates/droid/settings.json +0 -70
  239. package/dist/templates/gemini/agents/trellis-check.md +0 -107
  240. package/dist/templates/gemini/agents/trellis-implement.md +0 -102
  241. package/dist/templates/gemini/agents/trellis-research.md +0 -136
  242. package/dist/templates/gemini/index.d.ts +0 -13
  243. package/dist/templates/gemini/index.d.ts.map +0 -1
  244. package/dist/templates/gemini/index.js +0 -13
  245. package/dist/templates/gemini/index.js.map +0 -1
  246. package/dist/templates/gemini/settings.json +0 -28
  247. package/dist/templates/kiro/agents/trellis-check.json +0 -26
  248. package/dist/templates/kiro/agents/trellis-implement.json +0 -26
  249. package/dist/templates/kiro/agents/trellis-research.json +0 -30
  250. package/dist/templates/kiro/index.d.ts +0 -18
  251. package/dist/templates/kiro/index.d.ts.map +0 -1
  252. package/dist/templates/kiro/index.js +0 -18
  253. package/dist/templates/kiro/index.js.map +0 -1
  254. package/dist/templates/opencode/agents/trellis-check.md +0 -122
  255. package/dist/templates/opencode/agents/trellis-implement.md +0 -118
  256. package/dist/templates/opencode/agents/trellis-research.md +0 -145
  257. package/dist/templates/opencode/lib/session-utils.js +0 -667
  258. package/dist/templates/opencode/lib/trellis-context.js +0 -336
  259. package/dist/templates/opencode/package.json +0 -5
  260. package/dist/templates/opencode/plugins/inject-subagent-context.js +0 -495
  261. package/dist/templates/opencode/plugins/inject-workflow-state.js +0 -159
  262. package/dist/templates/opencode/plugins/session-start.js +0 -101
  263. package/dist/templates/pi/agents/trellis-check.md +0 -37
  264. package/dist/templates/pi/agents/trellis-implement.md +0 -42
  265. package/dist/templates/pi/agents/trellis-research.md +0 -25
  266. package/dist/templates/pi/extensions/trellis/index.ts.txt +0 -1600
  267. package/dist/templates/pi/index.d.ts +0 -5
  268. package/dist/templates/pi/index.d.ts.map +0 -1
  269. package/dist/templates/pi/index.js +0 -12
  270. package/dist/templates/pi/index.js.map +0 -1
  271. package/dist/templates/pi/settings.json +0 -12
  272. package/dist/templates/qoder/agents/trellis-check.md +0 -108
  273. package/dist/templates/qoder/agents/trellis-implement.md +0 -103
  274. package/dist/templates/qoder/agents/trellis-research.md +0 -137
  275. package/dist/templates/qoder/index.d.ts +0 -15
  276. package/dist/templates/qoder/index.d.ts.map +0 -1
  277. package/dist/templates/qoder/index.js +0 -15
  278. package/dist/templates/qoder/index.js.map +0 -1
  279. package/dist/templates/qoder/settings.json +0 -47
@@ -0,0 +1,313 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Render codebase retrieval plan envelopes as agent-executable instructions.
4
+
5
+ Complements JSON telemetry envelopes from codebase_retrieval_router.py; does not
6
+ invoke tools. Cursor platform maps routes to native tool names (Grep, codegraph,
7
+ built-in semantic search, GO_TO_DEFINITION).
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import re
13
+ from typing import Any
14
+
15
+ _SYMBOL_CANDIDATE = re.compile(
16
+ r"\b([A-Za-z_][\w$]{2,})\b|"
17
+ r"`([^`]+)`|"
18
+ r"「([^」]+)」",
19
+ )
20
+
21
+ _SKIP_SYMBOLS = frozenset(
22
+ {
23
+ "openclaw",
24
+ "which",
25
+ "where",
26
+ "what",
27
+ "who",
28
+ "how",
29
+ "list",
30
+ "give",
31
+ "tell",
32
+ "define",
33
+ "defined",
34
+ "calls",
35
+ "call",
36
+ "invoke",
37
+ "invoked",
38
+ "file",
39
+ "files",
40
+ "module",
41
+ "modules",
42
+ "package",
43
+ "packages",
44
+ "extension",
45
+ "extensions",
46
+ "src",
47
+ "the",
48
+ "and",
49
+ "for",
50
+ "with",
51
+ "from",
52
+ "that",
53
+ "this",
54
+ "AGENTS",
55
+ "README",
56
+ "Cursor",
57
+ },
58
+ )
59
+
60
+
61
+ def _plan_dict(plan: dict[str, object] | Any) -> dict[str, object]:
62
+ if isinstance(plan, dict):
63
+ return plan
64
+ raise TypeError("plan must be a dict envelope from route_codebase_retrieval")
65
+
66
+
67
+ def _symbol_rank(candidate: str) -> int:
68
+ if re.search(r"[a-z][A-Z]|[A-Z][a-z]", candidate):
69
+ return 3
70
+ if "_" in candidate or "$" in candidate:
71
+ return 2
72
+ if len(candidate) >= 10:
73
+ return 1
74
+ return 0
75
+
76
+
77
+ def guess_symbol_from_query(query: str) -> str | None:
78
+ text = (query or "").strip()
79
+ if not text:
80
+ return None
81
+ best: tuple[int, str] | None = None
82
+ for match in _SYMBOL_CANDIDATE.finditer(text):
83
+ candidate = next(g for g in match.groups() if g)
84
+ if "/" in candidate or "." in candidate and candidate.count(".") > 2:
85
+ continue
86
+ if candidate.lower() in _SKIP_SYMBOLS:
87
+ continue
88
+ if len(candidate) < 4:
89
+ continue
90
+ rank = _symbol_rank(candidate)
91
+ if best is None or rank > best[0] or (rank == best[0] and len(candidate) > len(best[1])):
92
+ best = (rank, candidate)
93
+ return best[1] if best else None
94
+
95
+
96
+ def _intent_summary(intents: list[dict[str, object]]) -> str:
97
+ if not intents:
98
+ return "(未分类,按默认精确检索)"
99
+ parts: list[str] = []
100
+ for item in intents[:4]:
101
+ ident = str(item.get("id", ""))
102
+ conf = str(item.get("confidence", ""))
103
+ parts.append(f"{ident}({conf})" if conf else ident)
104
+ return "、".join(parts)
105
+
106
+
107
+ def _cursor_step_for_route(route: dict[str, object], *, symbol: str, query: str) -> str | None:
108
+ role = str(route.get("role", ""))
109
+ route_id = str(route.get("id", ""))
110
+
111
+ if role == "verification":
112
+ return None
113
+
114
+ if route_id == "caller-chain-ast" or (
115
+ role == "ast" and "caller" in route_id
116
+ ):
117
+ return (
118
+ f"使用 **codegraph_callers**,符号 `{symbol}`,列出调用链;"
119
+ "再补 codegraph_explore 若需跨文件流。"
120
+ )
121
+ if route_id.startswith("trap-demote") and role == "ast":
122
+ return (
123
+ f"使用 **codegraph_search** / **codegraph_explore**,消歧同名符号 `{symbol}`(跨 package trap)。"
124
+ )
125
+ if route_id.startswith("extension") and role == "ast":
126
+ return (
127
+ f"使用 **codegraph_search**,路径侧重 `extensions/`,符号 `{symbol}`。"
128
+ )
129
+ if role == "ast" or route_id in ("ast-codegraph",):
130
+ return (
131
+ f"使用 **codegraph_explore** 或 **codegraph_node**(`includeCode=true`),"
132
+ f"围绕 `{symbol}` 或问题:{query[:120]}"
133
+ )
134
+
135
+ if route_id == "platform-semantic" or (
136
+ role == "semantic" and str(route.get("sourceFamily")) == "platform-semantic"
137
+ ):
138
+ return (
139
+ "使用 Cursor **内置代码库语义搜索**(Agent 语义搜索能力,等同 @codebase 语义索引;"
140
+ "**不要**使用 fast-context MCP)。"
141
+ )
142
+ if role == "semantic":
143
+ return (
144
+ "使用 Cursor **内置代码库语义搜索**(不要用 fast-context MCP)。"
145
+ )
146
+
147
+ if route_id == "lsp-navigation" or role == "lsp":
148
+ return (
149
+ f"可选:若宿主暴露 **GO_TO_DEFINITION** / 查找引用,在已有候选上核对 `{symbol}`;"
150
+ "非 Agent 默认保证,未调用时用 **Read** + **Grep** 验证。"
151
+ )
152
+
153
+ if route_id == "policy-docs-rg" or str(route.get("sourceFamily")) == "policy-docs":
154
+ return (
155
+ "使用 **Grep** 在 `AGENTS.md`、`**/AGENTS.md`、`.trellis/spec`、`README.md` "
156
+ "中搜索策略/边界关键词。"
157
+ )
158
+
159
+ if role == "exact" or "rg" in route_id:
160
+ return (
161
+ f"使用 **Grep**(Instant Grep)搜索字面量/路径;优先与 `{symbol}` 或用户给出的路径相关模式。"
162
+ )
163
+
164
+ if route_id == "cross-cutting-discovery" or "deep" in route_id.lower():
165
+ return (
166
+ "复杂跨模块探索:可选用 **DEEP_SEARCH** 或 Explore 子任务,再用 Grep/codegraph 验证。"
167
+ )
168
+
169
+ return f"按路由 `{route_id}`({role})执行,再 Read 源码确认。"
170
+
171
+
172
+ def _generic_step_for_route(route: dict[str, object], *, symbol: str, query: str) -> str | None:
173
+ role = str(route.get("role", ""))
174
+ if role == "verification":
175
+ return None
176
+ commands = route.get("commands")
177
+ cmd_hint = ""
178
+ if isinstance(commands, list) and commands:
179
+ cmd_hint = str(commands[0])[:160]
180
+ return f"执行路由 `{route.get('id')}`({role}):{cmd_hint or query[:80]}"
181
+
182
+
183
+ def render_agent_instructions(
184
+ plan: dict[str, object],
185
+ *,
186
+ locale: str = "zh",
187
+ ) -> str:
188
+ """
189
+ Turn a route_codebase_retrieval envelope into numbered agent steps.
190
+ """
191
+ envelope = _plan_dict(plan)
192
+ query = str(envelope.get("query", ""))
193
+ intents = envelope.get("intents")
194
+ routes = envelope.get("routes")
195
+ fallback = envelope.get("fallback")
196
+ verification = envelope.get("verification")
197
+
198
+ intent_list = intents if isinstance(intents, list) else []
199
+ route_list = routes if isinstance(routes, list) else []
200
+ fallback_list = fallback if isinstance(fallback, list) else []
201
+ verification_list = verification if isinstance(verification, list) else []
202
+
203
+ symbol = guess_symbol_from_query(query) or "<symbol>"
204
+
205
+ if locale != "zh":
206
+ header = "## Codebase retrieval plan\n"
207
+ else:
208
+ header = "## 代码库检索计划\n"
209
+
210
+ lines = [
211
+ header.rstrip(),
212
+ f"问题:{query or '(空)'}",
213
+ f"意图:{_intent_summary(intent_list)}",
214
+ "",
215
+ ]
216
+
217
+ step = 0
218
+ sorted_routes = sorted(
219
+ route_list,
220
+ key=lambda r: int(r.get("order", 0)) if isinstance(r, dict) else 0,
221
+ )
222
+ for route in sorted_routes:
223
+ if not isinstance(route, dict):
224
+ continue
225
+ text = _cursor_step_for_route(route, symbol=symbol, query=query)
226
+ if not text:
227
+ continue
228
+ step += 1
229
+ lines.append(f"{step}. {text}")
230
+
231
+ if step == 0:
232
+ step += 1
233
+ lines.append(
234
+ f"{step}. 使用 **Grep** 搜索关键词,再 **Read** 打开候选文件验证。"
235
+ )
236
+
237
+ if fallback_list:
238
+ lines.append("")
239
+ lines.append("**降级**:")
240
+ for hint in fallback_list[:3]:
241
+ if not isinstance(hint, dict):
242
+ continue
243
+ when = str(hint.get("when", ""))
244
+ action = str(hint.get("action", ""))
245
+ if when and action:
246
+ lines.append(f"- 当 {when} → {action}")
247
+
248
+ if verification_list:
249
+ lines.append("")
250
+ lines.append("**验证**(下结论前):")
251
+ for ver in verification_list[:4]:
252
+ if not isinstance(ver, dict):
253
+ continue
254
+ req = str(ver.get("requirement", ""))
255
+ if req:
256
+ lines.append(f"- {req}")
257
+
258
+ lines.append("")
259
+ lines.append(
260
+ "**codegraph 独用场景**:调用链、跨包 trap、extension 符号、影响面;"
261
+ "纯字面搜索用 Grep,单点定义用 GO_TO_DEFINITION。"
262
+ )
263
+
264
+ if any(
265
+ isinstance(r, dict) and r.get("id") == "platform-semantic" for r in route_list
266
+ ):
267
+ lines.append("")
268
+ if locale != "zh":
269
+ lines.append("**Semantic compliance (Cursor):**")
270
+ lines.append(
271
+ "- When this plan lists **platform-semantic**, run **one** Cursor built-in "
272
+ "codebase / semantic search before final Top-1 (not fast-context MCP)."
273
+ )
274
+ lines.append(
275
+ "- Log the **exact tool name** from the host (e.g. codebase_search, @codebase) "
276
+ "in your run notes for semantic_exec_rate."
277
+ )
278
+ else:
279
+ lines.append("**语义合规(Cursor):**")
280
+ lines.append(
281
+ "- 本计划含 **platform-semantic** 时:定 Top-1 前至少执行 **1 次** "
282
+ "Cursor **内置代码库语义搜索**(不要用 fast-context MCP)。"
283
+ )
284
+ lines.append(
285
+ "- 在 run 记录中写下宿主返回的**真实工具名**(如 codebase_search、@codebase),"
286
+ "供 semantic_exec 统计。"
287
+ )
288
+
289
+ try:
290
+ from .semantic_plan_gate import semantic_compliance_gate_hint # noqa: PLC0415
291
+
292
+ gate_hint = semantic_compliance_gate_hint(envelope, locale=locale)
293
+ if gate_hint:
294
+ lines.append("")
295
+ lines.append(gate_hint)
296
+ except Exception:
297
+ pass
298
+
299
+ try:
300
+ from .retrieval_result_ranking import ( # noqa: PLC0415
301
+ intent_ids_from_router_envelope,
302
+ result_layer_ranking_hint,
303
+ )
304
+
305
+ intent_ids = intent_ids_from_router_envelope(envelope)
306
+ ranking_hint = result_layer_ranking_hint(intent_ids, locale=locale)
307
+ if ranking_hint:
308
+ lines.append("")
309
+ lines.append(ranking_hint.rstrip())
310
+ except Exception:
311
+ pass
312
+
313
+ return "\n".join(lines).strip() + "\n"
@@ -316,11 +316,6 @@ def parse_args(argv: list[str] | None = None) -> argparse.Namespace:
316
316
  "--root",
317
317
  help="Repository root for read-only Smart Search manifest discovery.",
318
318
  )
319
- parser.add_argument(
320
- "--platform",
321
- default=None,
322
- help="Host platform for platform-adaptive retrieval routing (e.g. cursor, claude-code, codex).",
323
- )
324
319
  parser.add_argument(
325
320
  "--max-items",
326
321
  type=int,
@@ -385,7 +380,6 @@ def main(argv: list[str] | None = None) -> int:
385
380
  resolve_repo_root(args.root),
386
381
  explicit_router=dict_value(payload.get("routerEnvelope")) or None,
387
382
  query=string_value(payload.get("query")) or None,
388
- platform=args.platform,
389
383
  ),
390
384
  adapter_hints=normalize_dict_list(list_value(payload.get("adapterHints"))),
391
385
  )
@@ -51,7 +51,6 @@ def get_context_retrieval_pack_json(
51
51
  max_items: int | None = None,
52
52
  max_estimated_tokens: int | None = None,
53
53
  include_diagnostics: bool = False,
54
- platform: str | None = None,
55
54
  project_file_count: int | None = None,
56
55
  ) -> dict[str, object]:
57
56
  """Build a retrieval pack from session guidance and optional evidence input."""
@@ -92,7 +91,6 @@ def get_context_retrieval_pack_json(
92
91
  repo_root,
93
92
  explicit_router=dict_value(payload.get("routerEnvelope")) or None,
94
93
  query=string_value(payload.get("query")) or None,
95
- platform=platform,
96
94
  project_file_count=project_file_count,
97
95
  ),
98
96
  adapter_hints=normalize_dict_list(list_value(payload.get("adapterHints"))),
@@ -107,7 +105,6 @@ def output_retrieval_pack_json(
107
105
  max_estimated_tokens: int | None = None,
108
106
  include_diagnostics: bool = False,
109
107
  pretty: bool = False,
110
- platform: str | None = None,
111
108
  project_file_count: int | None = None,
112
109
  ) -> None:
113
110
  """Print retrieval pack JSON to stdout."""
@@ -117,7 +114,6 @@ def output_retrieval_pack_json(
117
114
  max_items=max_items,
118
115
  max_estimated_tokens=max_estimated_tokens,
119
116
  include_diagnostics=include_diagnostics,
120
- platform=platform,
121
117
  project_file_count=project_file_count,
122
118
  )
123
119
  if pretty:
@@ -0,0 +1,76 @@
1
+ #!/usr/bin/env python3
2
+ """Gate whether to inject a per-turn codebase retrieval plan block."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import re
7
+
8
+ from .codebase_retrieval_router import (
9
+ INTENT_CALLER,
10
+ INTENT_CONCEPTUAL,
11
+ INTENT_ENV,
12
+ INTENT_EXTENSION,
13
+ INTENT_POLICY,
14
+ INTENT_TRAP,
15
+ classify_codebase_retrieval_intents,
16
+ )
17
+
18
+ _SKIP_EXACT = frozenset(
19
+ {
20
+ "继续",
21
+ "continue",
22
+ "yes",
23
+ "y",
24
+ "ok",
25
+ "好的",
26
+ "是的",
27
+ "可以",
28
+ "thanks",
29
+ "thank you",
30
+ }
31
+ )
32
+
33
+ _META_PREFIXES = (
34
+ "[triage:",
35
+ "/trellis",
36
+ "/shell",
37
+ )
38
+
39
+
40
+ def extract_user_prompt(hook_input: dict[str, object]) -> str:
41
+ """Best-effort user text from a per-turn hook stdin payload."""
42
+ for key in ("prompt", "user_message", "text", "message", "content"):
43
+ raw = hook_input.get(key)
44
+ if isinstance(raw, str) and raw.strip():
45
+ return raw.strip()
46
+ return ""
47
+
48
+
49
+ def should_inject_retrieval_plan(query: str) -> bool:
50
+ """Return True when router-backed plan injection is worth the tokens."""
51
+ text = (query or "").strip()
52
+ if not text:
53
+ return False
54
+ lowered = text.lower()
55
+ if lowered in _SKIP_EXACT:
56
+ return False
57
+ if any(lowered.startswith(prefix) for prefix in _META_PREFIXES):
58
+ return False
59
+ if len(text) < 8 and not re.search(r"[\u4e00-\u9fff]", text):
60
+ return False
61
+
62
+ intents = classify_codebase_retrieval_intents(text)
63
+ intent_ids = {str(item.get("id", "")) for item in intents}
64
+ structural = {
65
+ INTENT_CALLER,
66
+ INTENT_TRAP,
67
+ INTENT_EXTENSION,
68
+ INTENT_ENV,
69
+ INTENT_POLICY,
70
+ INTENT_CONCEPTUAL,
71
+ }
72
+ if intent_ids & structural:
73
+ return True
74
+ if len(text) >= 12:
75
+ return True
76
+ return bool(re.search(r"[\u4e00-\u9fff]{3,}", text) and len(text) >= 6)
@@ -0,0 +1,253 @@
1
+ #!/usr/bin/env python3
2
+ """Result-layer ranking for B/E/D intents (mirrors retrieval-result-ranking.ts)."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import re
7
+ from typing import Any
8
+
9
+ RankingIntent = str
10
+
11
+ INTENT_CALLER = "caller-chain"
12
+ INTENT_TRAP = "trap-package-disambiguation"
13
+ INTENT_ENV = "env-config-literal"
14
+ INTENT_EXACT = "exact-symbol-path"
15
+ INTENT_PRESERVE = "protocol-platform-preserve"
16
+
17
+
18
+ def _norm(path: str) -> str:
19
+ return path.replace("\\", "/").lower()
20
+
21
+
22
+ def _includes(intents: list[str], target: str) -> bool:
23
+ return target in intents
24
+
25
+
26
+ def _is_assembly_only(candidate: dict[str, Any]) -> bool:
27
+ path = _norm(str(candidate.get("path", "")))
28
+ role = str(candidate.get("sourceRole") or candidate.get("source_role") or "")
29
+ evidence = str(candidate.get("evidenceType") or candidate.get("evidence_type") or "")
30
+ if evidence == "assembly":
31
+ return True
32
+ if re.search(r"(?:facade|loader|barrel|runtime|registry|snapshot)", role, re.I):
33
+ return True
34
+ return bool(re.search(r"(?:facade|loader|barrel|runtime|registry|snapshot)", path, re.I))
35
+
36
+
37
+ def _is_concrete_caller(candidate: dict[str, Any]) -> bool:
38
+ evidence = str(candidate.get("evidenceType") or candidate.get("evidence_type") or "")
39
+ role = str(candidate.get("sourceRole") or candidate.get("source_role") or "")
40
+ if evidence == "caller-callsite":
41
+ return True
42
+ return bool(re.search(r"caller|callsite", role, re.I))
43
+
44
+
45
+ def _is_trap(candidate: dict[str, Any]) -> bool:
46
+ path = _norm(str(candidate.get("path", "")))
47
+ if candidate.get("trapHint") is True or candidate.get("trap_hint") is True:
48
+ return True
49
+ evidence = str(candidate.get("evidenceType") or candidate.get("evidence_type") or "")
50
+ if evidence == "trap":
51
+ return True
52
+ if re.search(r"plugin-registry-snapshot|registry-snapshot|snapshot\.ts$", path):
53
+ return True
54
+ return "/src/agents/" in path
55
+
56
+
57
+ def _is_env_priority(candidate: dict[str, Any]) -> bool:
58
+ path = _norm(str(candidate.get("path", "")))
59
+ return bool(
60
+ re.search(
61
+ r"(^|/)(scripts|e2e|bench|benches|\.github|ci|config|configs|test|tests)(/|$)",
62
+ path,
63
+ )
64
+ )
65
+
66
+
67
+ def _is_generic_env_impl(candidate: dict[str, Any]) -> bool:
68
+ path = _norm(str(candidate.get("path", "")))
69
+ return bool(re.search(r"(^|/)src/(auth|paths?|state|config)(/|\.|$)", path))
70
+
71
+
72
+ def _base_score(candidate: dict[str, Any]) -> float:
73
+ if isinstance(candidate.get("score"), (int, float)):
74
+ return float(candidate["score"])
75
+ base_rank = int(candidate.get("baseRank") or candidate.get("base_rank") or 0)
76
+ return float(1000 - base_rank)
77
+
78
+
79
+ def score_candidate(
80
+ candidate: dict[str, Any],
81
+ intents: list[str],
82
+ ) -> dict[str, Any]:
83
+ adjusted = _base_score(candidate)
84
+ reasons: list[str] = []
85
+ matched = candidate.get("matchedIntents") or candidate.get("matched_intents") or []
86
+ if not isinstance(matched, list):
87
+ matched = []
88
+
89
+ exact_preserve = (
90
+ candidate.get("exactPreserve") is True
91
+ or candidate.get("exact_preserve") is True
92
+ or INTENT_PRESERVE in matched
93
+ or INTENT_EXACT in matched
94
+ )
95
+
96
+ if exact_preserve:
97
+ adjusted += 1000
98
+ reasons.append("exact-preserve-protected")
99
+
100
+ if not exact_preserve and _includes(intents, INTENT_CALLER):
101
+ evidence = str(candidate.get("evidenceType") or candidate.get("evidence_type") or "")
102
+ if evidence == "codegraph-caller":
103
+ adjusted += 30
104
+ reasons.append("codegraph-caller-boost")
105
+ if _is_concrete_caller(candidate):
106
+ adjusted += 150
107
+ reasons.append("concrete-caller-boost")
108
+ if _is_assembly_only(candidate):
109
+ adjusted -= 120
110
+ reasons.append("assembly-only-demotion")
111
+
112
+ if not exact_preserve and _includes(intents, INTENT_TRAP):
113
+ if _is_trap(candidate) and not candidate.get("expectedHint") and not candidate.get(
114
+ "expected_hint"
115
+ ):
116
+ adjusted -= 250
117
+ reasons.append("trap-demotion")
118
+ if candidate.get("corroborated") and not _is_trap(candidate):
119
+ adjusted += 80
120
+ reasons.append("corroborated-non-trap-boost")
121
+
122
+ if not exact_preserve and _includes(intents, INTENT_ENV):
123
+ if _is_env_priority(candidate):
124
+ adjusted += 140
125
+ reasons.append("env-script-priority")
126
+ if _is_generic_env_impl(candidate):
127
+ adjusted -= 100
128
+ reasons.append("generic-env-implementation-demotion")
129
+
130
+ out = dict(candidate)
131
+ out["adjustedScore"] = adjusted
132
+ out["adjusted_score"] = adjusted
133
+ out["rankingReasons"] = reasons
134
+ out["ranking_reasons"] = reasons
135
+ return out
136
+
137
+
138
+ def rank_retrieval_result_candidates(
139
+ candidates: list[dict[str, Any]],
140
+ *,
141
+ intents: list[str],
142
+ top_k: int = 5,
143
+ expanded_pool_size: int | None = None,
144
+ caller_pool_expansion: dict[str, Any] | None = None,
145
+ ) -> dict[str, Any]:
146
+ top_k = max(1, top_k)
147
+ pool_size = expanded_pool_size if expanded_pool_size is not None else max(top_k * 3, top_k)
148
+ warnings: list[str] = []
149
+
150
+ ranked = [
151
+ score_candidate(c, intents)
152
+ for c in candidates
153
+ ]
154
+ ranked.sort(
155
+ key=lambda c: (
156
+ -float(c.get("adjustedScore") or c.get("adjusted_score") or 0),
157
+ int(c.get("baseRank") or c.get("base_rank") or 0),
158
+ ),
159
+ )
160
+
161
+ expanded_pool = ranked[:pool_size]
162
+
163
+ expansion = caller_pool_expansion or {}
164
+ if expansion.get("enabled") and _includes(intents, INTENT_CALLER):
165
+ min_concrete = int(expansion.get("minConcreteCallers") or expansion.get("min_concrete_callers") or 0)
166
+ concrete_in_pool = [c for c in expanded_pool if _is_concrete_caller(c)]
167
+ seen_paths = {_norm(str(c.get("path", ""))) for c in concrete_in_pool}
168
+
169
+ if min_concrete > 0 and len(concrete_in_pool) < min_concrete:
170
+ for candidate in ranked[pool_size:]:
171
+ if not _is_concrete_caller(candidate):
172
+ continue
173
+ key = _norm(str(candidate.get("path", "")))
174
+ if key in seen_paths:
175
+ continue
176
+ expanded_pool.append(candidate)
177
+ seen_paths.add(key)
178
+ if len([c for c in expanded_pool if _is_concrete_caller(c)]) >= min_concrete:
179
+ break
180
+
181
+ final_count = len([c for c in expanded_pool if _is_concrete_caller(c)])
182
+ if min_concrete > 0 and final_count < min_concrete:
183
+ warnings.append(
184
+ f"caller-pool-expansion: only {final_count} concrete callers found, "
185
+ f"below minConcreteCallers={min_concrete}"
186
+ )
187
+
188
+ return {
189
+ "expandedPoolSize": pool_size,
190
+ "expanded_pool_size": pool_size,
191
+ "expandedPool": expanded_pool,
192
+ "expanded_pool": expanded_pool,
193
+ "topCandidates": expanded_pool[:top_k],
194
+ "top_candidates": expanded_pool[:top_k],
195
+ "warnings": warnings,
196
+ }
197
+
198
+
199
+ def intent_ids_from_router_envelope(plan: dict[str, Any]) -> list[str]:
200
+ raw = plan.get("intents")
201
+ if not isinstance(raw, list):
202
+ return []
203
+ ids: list[str] = []
204
+ for item in raw:
205
+ if isinstance(item, dict) and item.get("id"):
206
+ ids.append(str(item["id"]))
207
+ return ids
208
+
209
+
210
+ def result_layer_ranking_hint(intents: list[str], *, locale: str = "zh") -> str:
211
+ """Short agent guidance appended to retrieval plans when B/E/D intents fire."""
212
+ lines: list[str] = []
213
+ if locale != "zh":
214
+ lines.append("**Result-layer ranking (before Top-1 / Top-5):**")
215
+ else:
216
+ lines.append("**结果层排序(定 Top-1 / Top-5 前):**")
217
+
218
+ if INTENT_CALLER in intents:
219
+ if locale != "zh":
220
+ lines.append(
221
+ "- Caller-chain: keep an expanded pool (codegraph callers + rg); "
222
+ "prefer concrete call sites over facade/barrel/runtime/registry assembly files."
223
+ )
224
+ else:
225
+ lines.append(
226
+ "- 调用链:先扩大候选池(codegraph callers + rg),"
227
+ "具体调用点优先于 facade/barrel/runtime/registry 等装配文件。"
228
+ )
229
+ if INTENT_TRAP in intents:
230
+ if locale != "zh":
231
+ lines.append(
232
+ "- Trap: demote snapshot/registry overlay and cross-package name collisions "
233
+ "unless Read confirms the asked layer."
234
+ )
235
+ else:
236
+ lines.append(
237
+ "- 干扰项:压低 snapshot/registry/overlay 与跨包同名;"
238
+ "除非 Read 确认就是所问层级。"
239
+ )
240
+ if INTENT_ENV in intents:
241
+ if locale != "zh":
242
+ lines.append(
243
+ "- Env literals: prefer scripts/e2e/bench/test harness paths over generic src/auth/paths."
244
+ )
245
+ else:
246
+ lines.append(
247
+ "- 环境变量/配置字面量:优先 scripts/e2e/bench/test 等路径,"
248
+ "低于泛化 src/auth/paths 实现文件。"
249
+ )
250
+
251
+ if len(lines) <= 1:
252
+ return ""
253
+ return "\n".join(lines) + "\n"