@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.
- package/README.md +124 -5
- package/README.zh-CN.md +170 -0
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +1 -33
- package/dist/cli/index.js.map +1 -1
- package/dist/commands/init.d.ts +0 -13
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +7 -14
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/uninstall.d.ts.map +1 -1
- package/dist/commands/uninstall.js +3 -32
- package/dist/commands/uninstall.js.map +1 -1
- package/dist/commands/update.d.ts.map +1 -1
- package/dist/commands/update.js +7 -84
- package/dist/commands/update.js.map +1 -1
- package/dist/configurators/index.d.ts +7 -8
- package/dist/configurators/index.d.ts.map +1 -1
- package/dist/configurators/index.js +11 -236
- package/dist/configurators/index.js.map +1 -1
- package/dist/configurators/workflow.d.ts.map +1 -1
- package/dist/configurators/workflow.js +5 -1
- package/dist/configurators/workflow.js.map +1 -1
- package/dist/templates/common/bundled-skills/smart-search-cli/SKILL.md +3 -3
- package/dist/templates/common/bundled-skills/trellis-meta/SKILL.md +4 -2
- package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-agents.md +8 -15
- package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-hooks.md +7 -7
- package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-skills-or-commands.md +18 -21
- package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/overview.md +2 -2
- package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/generated-files.md +10 -7
- package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/overview.md +4 -4
- package/dist/templates/common/bundled-skills/trellis-meta/references/platform-files/agents.md +20 -31
- package/dist/templates/common/bundled-skills/trellis-meta/references/platform-files/hooks-and-settings.md +20 -31
- package/dist/templates/common/bundled-skills/trellis-meta/references/platform-files/overview.md +24 -40
- package/dist/templates/common/bundled-skills/trellis-meta/references/platform-files/platform-map.md +28 -59
- package/dist/templates/common/bundled-skills/trellis-meta/references/platform-files/skills-and-commands.md +12 -28
- package/dist/templates/common/bundled-skills/trellis-skill-creator/references/trellis-skill-locations.md +2 -2
- package/dist/templates/cursor/hooks.json +6 -0
- package/dist/templates/cursor/rules/retrieval-routing.mdc +57 -0
- package/dist/templates/extract.d.ts +0 -14
- package/dist/templates/extract.d.ts.map +1 -1
- package/dist/templates/extract.js +0 -34
- package/dist/templates/extract.js.map +1 -1
- package/dist/templates/markdown/agents.md +2 -6
- package/dist/templates/markdown/index.d.ts +1 -0
- package/dist/templates/markdown/index.d.ts.map +1 -1
- package/dist/templates/markdown/index.js +1 -0
- package/dist/templates/markdown/index.js.map +1 -1
- package/dist/templates/markdown/prompts/run-semantic-slice-12.md.txt +41 -0
- package/dist/templates/markdown/spec/guides/code-reuse-thinking-guide.md.txt +14 -14
- package/dist/templates/markdown/spec/guides/cross-layer-thinking-guide.md.txt +8 -20
- package/dist/templates/markdown/spec/guides/cross-platform-thinking-guide.md.txt +15 -14
- package/dist/templates/markdown/spec/guides/cursor-context-injection-guide.md.txt +2 -1
- package/dist/templates/markdown/spec/guides/cursor-semantic-compliance.md.txt +49 -0
- package/dist/templates/markdown/spec/guides/cursor-subagent-policy.md.txt +2 -2
- package/dist/templates/markdown/spec/guides/retrieval-daily-guide.md.txt +68 -20
- package/dist/templates/shared-hooks/index.d.ts +1 -1
- package/dist/templates/shared-hooks/index.d.ts.map +1 -1
- package/dist/templates/shared-hooks/index.js +1 -0
- package/dist/templates/shared-hooks/index.js.map +1 -1
- package/dist/templates/shared-hooks/inject-retrieval-plan.py +163 -0
- package/dist/templates/shared-hooks/research-end-retrieval-pack.py +2 -11
- package/dist/templates/trellis/index.d.ts +11 -0
- package/dist/templates/trellis/index.d.ts.map +1 -1
- package/dist/templates/trellis/index.js +22 -0
- package/dist/templates/trellis/index.js.map +1 -1
- package/dist/templates/trellis/scripts/aggregate_retrieval_telemetry.py +342 -0
- package/dist/templates/trellis/scripts/batch_plan_envelope.py +105 -0
- package/dist/templates/trellis/scripts/codegraph_session_smoke.py +76 -0
- package/dist/templates/trellis/scripts/common/active_task.py +116 -147
- package/dist/templates/trellis/scripts/common/cli_adapter.py +34 -710
- package/dist/templates/trellis/scripts/common/codebase_retrieval_router.py +49 -106
- package/dist/templates/trellis/scripts/common/git_context.py +17 -2
- package/dist/templates/trellis/scripts/common/project_file_stats.py +91 -0
- package/dist/templates/trellis/scripts/common/retrieval_adapter_metadata.py +9 -11
- package/dist/templates/trellis/scripts/common/retrieval_agent_instructions.py +313 -0
- package/dist/templates/trellis/scripts/common/retrieval_pack.py +0 -6
- package/dist/templates/trellis/scripts/common/retrieval_pack_context.py +0 -4
- package/dist/templates/trellis/scripts/common/retrieval_plan_gate.py +76 -0
- package/dist/templates/trellis/scripts/common/retrieval_result_ranking.py +253 -0
- package/dist/templates/trellis/scripts/common/retrieval_tool_classification.py +157 -0
- package/dist/templates/trellis/scripts/common/semantic_plan_gate.py +52 -0
- package/dist/templates/trellis/scripts/common/workflow_phase.py +3 -24
- package/dist/templates/trellis/scripts/rank_retrieval_candidates.py +80 -0
- package/dist/templates/trellis/scripts/route_codebase_retrieval.py +38 -11
- package/dist/templates/trellis/scripts/score_evidence.py +112 -0
- package/dist/templates/trellis/workflow.md +861 -960
- package/dist/types/ai-tools.d.ts +13 -16
- package/dist/types/ai-tools.d.ts.map +1 -1
- package/dist/types/ai-tools.js +5 -238
- package/dist/types/ai-tools.js.map +1 -1
- package/dist/utils/codebase-retrieval-router.d.ts +1 -8
- package/dist/utils/codebase-retrieval-router.d.ts.map +1 -1
- package/dist/utils/codebase-retrieval-router.js +39 -180
- package/dist/utils/codebase-retrieval-router.js.map +1 -1
- package/dist/utils/project-capabilities.d.ts.map +1 -1
- package/dist/utils/project-capabilities.js +10 -24
- package/dist/utils/project-capabilities.js.map +1 -1
- package/dist/utils/retrieval-agent-instructions.d.ts +14 -0
- package/dist/utils/retrieval-agent-instructions.d.ts.map +1 -0
- package/dist/utils/retrieval-agent-instructions.js +221 -0
- package/dist/utils/retrieval-agent-instructions.js.map +1 -0
- package/dist/utils/retrieval-execution-telemetry.d.ts +43 -85
- package/dist/utils/retrieval-execution-telemetry.d.ts.map +1 -1
- package/dist/utils/retrieval-execution-telemetry.js +289 -22
- package/dist/utils/retrieval-execution-telemetry.js.map +1 -1
- package/dist/utils/retrieval-result-ranking.js +1 -1
- package/dist/utils/retrieval-result-ranking.js.map +1 -1
- package/dist/utils/retrieval-tool-classification.d.ts +32 -0
- package/dist/utils/retrieval-tool-classification.d.ts.map +1 -0
- package/dist/utils/retrieval-tool-classification.js +113 -0
- package/dist/utils/retrieval-tool-classification.js.map +1 -0
- package/dist/utils/uninstall-scrubbers.d.ts +0 -36
- package/dist/utils/uninstall-scrubbers.d.ts.map +1 -1
- package/dist/utils/uninstall-scrubbers.js +0 -184
- package/dist/utils/uninstall-scrubbers.js.map +1 -1
- package/package.json +4 -2
- package/dist/commands/mem.d.ts +0 -30
- package/dist/commands/mem.d.ts.map +0 -1
- package/dist/commands/mem.js +0 -424
- package/dist/commands/mem.js.map +0 -1
- package/dist/configurators/antigravity.d.ts +0 -7
- package/dist/configurators/antigravity.d.ts.map +0 -1
- package/dist/configurators/antigravity.js +0 -19
- package/dist/configurators/antigravity.js.map +0 -1
- package/dist/configurators/claude.d.ts +0 -9
- package/dist/configurators/claude.d.ts.map +0 -1
- package/dist/configurators/claude.js +0 -72
- package/dist/configurators/claude.js.map +0 -1
- package/dist/configurators/codebuddy.d.ts +0 -10
- package/dist/configurators/codebuddy.d.ts.map +0 -1
- package/dist/configurators/codebuddy.js +0 -30
- package/dist/configurators/codebuddy.js.map +0 -1
- package/dist/configurators/codex.d.ts +0 -8
- package/dist/configurators/codex.d.ts.map +0 -1
- package/dist/configurators/codex.js +0 -87
- package/dist/configurators/codex.js.map +0 -1
- package/dist/configurators/copilot.d.ts +0 -10
- package/dist/configurators/copilot.d.ts.map +0 -1
- package/dist/configurators/copilot.js +0 -51
- package/dist/configurators/copilot.js.map +0 -1
- package/dist/configurators/droid.d.ts +0 -10
- package/dist/configurators/droid.d.ts.map +0 -1
- package/dist/configurators/droid.js +0 -30
- package/dist/configurators/droid.js.map +0 -1
- package/dist/configurators/gemini.d.ts +0 -16
- package/dist/configurators/gemini.d.ts.map +0 -1
- package/dist/configurators/gemini.js +0 -38
- package/dist/configurators/gemini.js.map +0 -1
- package/dist/configurators/kilo.d.ts +0 -7
- package/dist/configurators/kilo.d.ts.map +0 -1
- package/dist/configurators/kilo.js +0 -19
- package/dist/configurators/kilo.js.map +0 -1
- package/dist/configurators/kiro.d.ts +0 -8
- package/dist/configurators/kiro.d.ts.map +0 -1
- package/dist/configurators/kiro.js +0 -24
- package/dist/configurators/kiro.js.map +0 -1
- package/dist/configurators/opencode.d.ts +0 -14
- package/dist/configurators/opencode.d.ts.map +0 -1
- package/dist/configurators/opencode.js +0 -96
- package/dist/configurators/opencode.js.map +0 -1
- package/dist/configurators/pi.d.ts +0 -3
- package/dist/configurators/pi.d.ts.map +0 -1
- package/dist/configurators/pi.js +0 -45
- package/dist/configurators/pi.js.map +0 -1
- package/dist/configurators/qoder.d.ts +0 -11
- package/dist/configurators/qoder.d.ts.map +0 -1
- package/dist/configurators/qoder.js +0 -31
- package/dist/configurators/qoder.js.map +0 -1
- package/dist/configurators/windsurf.d.ts +0 -7
- package/dist/configurators/windsurf.d.ts.map +0 -1
- package/dist/configurators/windsurf.js +0 -19
- package/dist/configurators/windsurf.js.map +0 -1
- package/dist/templates/claude/agents/trellis-check.md +0 -124
- package/dist/templates/claude/agents/trellis-implement.md +0 -110
- package/dist/templates/claude/agents/trellis-research.md +0 -137
- package/dist/templates/claude/index.d.ts +0 -22
- package/dist/templates/claude/index.d.ts.map +0 -1
- package/dist/templates/claude/index.js +0 -46
- package/dist/templates/claude/index.js.map +0 -1
- package/dist/templates/claude/settings.json +0 -84
- package/dist/templates/codebuddy/agents/trellis-check.md +0 -115
- package/dist/templates/codebuddy/agents/trellis-implement.md +0 -110
- package/dist/templates/codebuddy/agents/trellis-research.md +0 -137
- package/dist/templates/codebuddy/index.d.ts +0 -15
- package/dist/templates/codebuddy/index.d.ts.map +0 -1
- package/dist/templates/codebuddy/index.js +0 -15
- package/dist/templates/codebuddy/index.js.map +0 -1
- package/dist/templates/codebuddy/settings.json +0 -70
- package/dist/templates/codex/agents/trellis-check.toml +0 -67
- package/dist/templates/codex/agents/trellis-implement.toml +0 -40
- package/dist/templates/codex/agents/trellis-research.toml +0 -73
- package/dist/templates/codex/config.toml +0 -28
- package/dist/templates/codex/hooks/session-start.py +0 -575
- package/dist/templates/codex/hooks.json +0 -15
- package/dist/templates/codex/index.d.ts +0 -39
- package/dist/templates/codex/index.d.ts.map +0 -1
- package/dist/templates/codex/index.js +0 -85
- package/dist/templates/codex/index.js.map +0 -1
- package/dist/templates/codex/skills/before-dev/SKILL.md +0 -40
- package/dist/templates/codex/skills/brainstorm/SKILL.md +0 -112
- package/dist/templates/codex/skills/break-loop/SKILL.md +0 -130
- package/dist/templates/codex/skills/check/SKILL.md +0 -98
- package/dist/templates/codex/skills/check-cross-layer/SKILL.md +0 -158
- package/dist/templates/codex/skills/create-command/SKILL.md +0 -101
- package/dist/templates/codex/skills/finish-work/SKILL.md +0 -90
- package/dist/templates/codex/skills/improve-ut/SKILL.md +0 -69
- package/dist/templates/codex/skills/integrate-skill/SKILL.md +0 -221
- package/dist/templates/codex/skills/onboard/SKILL.md +0 -363
- package/dist/templates/codex/skills/record-session/SKILL.md +0 -67
- package/dist/templates/codex/skills/start/SKILL.md +0 -66
- package/dist/templates/codex/skills/update-spec/SKILL.md +0 -335
- package/dist/templates/copilot/hooks/session-start.py +0 -557
- package/dist/templates/copilot/hooks.json +0 -19
- package/dist/templates/copilot/index.d.ts +0 -23
- package/dist/templates/copilot/index.d.ts.map +0 -1
- package/dist/templates/copilot/index.js +0 -54
- package/dist/templates/copilot/index.js.map +0 -1
- package/dist/templates/copilot/prompts/before-dev.prompt.md +0 -39
- package/dist/templates/copilot/prompts/brainstorm.prompt.md +0 -111
- package/dist/templates/copilot/prompts/break-loop.prompt.md +0 -129
- package/dist/templates/copilot/prompts/check-cross-layer.prompt.md +0 -157
- package/dist/templates/copilot/prompts/check.prompt.md +0 -97
- package/dist/templates/copilot/prompts/create-command.prompt.md +0 -116
- package/dist/templates/copilot/prompts/finish-work.prompt.md +0 -99
- package/dist/templates/copilot/prompts/integrate-skill.prompt.md +0 -223
- package/dist/templates/copilot/prompts/onboard.prompt.md +0 -362
- package/dist/templates/copilot/prompts/parallel.prompt.md +0 -204
- package/dist/templates/copilot/prompts/record-session.prompt.md +0 -66
- package/dist/templates/copilot/prompts/start.prompt.md +0 -63
- package/dist/templates/copilot/prompts/update-spec.prompt.md +0 -358
- package/dist/templates/droid/droids/trellis-check.md +0 -107
- package/dist/templates/droid/droids/trellis-implement.md +0 -102
- package/dist/templates/droid/droids/trellis-research.md +0 -137
- package/dist/templates/droid/index.d.ts +0 -15
- package/dist/templates/droid/index.d.ts.map +0 -1
- package/dist/templates/droid/index.js +0 -15
- package/dist/templates/droid/index.js.map +0 -1
- package/dist/templates/droid/settings.json +0 -70
- package/dist/templates/gemini/agents/trellis-check.md +0 -107
- package/dist/templates/gemini/agents/trellis-implement.md +0 -102
- package/dist/templates/gemini/agents/trellis-research.md +0 -136
- package/dist/templates/gemini/index.d.ts +0 -13
- package/dist/templates/gemini/index.d.ts.map +0 -1
- package/dist/templates/gemini/index.js +0 -13
- package/dist/templates/gemini/index.js.map +0 -1
- package/dist/templates/gemini/settings.json +0 -28
- package/dist/templates/kiro/agents/trellis-check.json +0 -26
- package/dist/templates/kiro/agents/trellis-implement.json +0 -26
- package/dist/templates/kiro/agents/trellis-research.json +0 -30
- package/dist/templates/kiro/index.d.ts +0 -18
- package/dist/templates/kiro/index.d.ts.map +0 -1
- package/dist/templates/kiro/index.js +0 -18
- package/dist/templates/kiro/index.js.map +0 -1
- package/dist/templates/opencode/agents/trellis-check.md +0 -122
- package/dist/templates/opencode/agents/trellis-implement.md +0 -118
- package/dist/templates/opencode/agents/trellis-research.md +0 -145
- package/dist/templates/opencode/lib/session-utils.js +0 -667
- package/dist/templates/opencode/lib/trellis-context.js +0 -336
- package/dist/templates/opencode/package.json +0 -5
- package/dist/templates/opencode/plugins/inject-subagent-context.js +0 -495
- package/dist/templates/opencode/plugins/inject-workflow-state.js +0 -159
- package/dist/templates/opencode/plugins/session-start.js +0 -101
- package/dist/templates/pi/agents/trellis-check.md +0 -37
- package/dist/templates/pi/agents/trellis-implement.md +0 -42
- package/dist/templates/pi/agents/trellis-research.md +0 -25
- package/dist/templates/pi/extensions/trellis/index.ts.txt +0 -1600
- package/dist/templates/pi/index.d.ts +0 -5
- package/dist/templates/pi/index.d.ts.map +0 -1
- package/dist/templates/pi/index.js +0 -12
- package/dist/templates/pi/index.js.map +0 -1
- package/dist/templates/pi/settings.json +0 -12
- package/dist/templates/qoder/agents/trellis-check.md +0 -108
- package/dist/templates/qoder/agents/trellis-implement.md +0 -103
- package/dist/templates/qoder/agents/trellis-research.md +0 -137
- package/dist/templates/qoder/index.d.ts +0 -15
- package/dist/templates/qoder/index.d.ts.map +0 -1
- package/dist/templates/qoder/index.js +0 -15
- package/dist/templates/qoder/index.js.map +0 -1
- 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"
|