deepspider 0.1.0

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 (261) hide show
  1. package/.claude/agents/check.md +122 -0
  2. package/.claude/agents/debug.md +106 -0
  3. package/.claude/agents/dispatch.md +214 -0
  4. package/.claude/agents/implement.md +96 -0
  5. package/.claude/agents/plan.md +396 -0
  6. package/.claude/agents/research.md +120 -0
  7. package/.claude/commands/evolve/merge.md +80 -0
  8. package/.claude/commands/trellis/before-backend-dev.md +13 -0
  9. package/.claude/commands/trellis/before-frontend-dev.md +13 -0
  10. package/.claude/commands/trellis/break-loop.md +107 -0
  11. package/.claude/commands/trellis/check-backend.md +13 -0
  12. package/.claude/commands/trellis/check-cross-layer.md +153 -0
  13. package/.claude/commands/trellis/check-frontend.md +13 -0
  14. package/.claude/commands/trellis/create-command.md +154 -0
  15. package/.claude/commands/trellis/finish-work.md +129 -0
  16. package/.claude/commands/trellis/integrate-skill.md +219 -0
  17. package/.claude/commands/trellis/onboard.md +358 -0
  18. package/.claude/commands/trellis/parallel.md +193 -0
  19. package/.claude/commands/trellis/record-session.md +62 -0
  20. package/.claude/commands/trellis/start.md +280 -0
  21. package/.claude/commands/trellis/update-spec.md +213 -0
  22. package/.claude/hooks/inject-subagent-context.py +758 -0
  23. package/.claude/hooks/ralph-loop.py +374 -0
  24. package/.claude/hooks/session-start.py +126 -0
  25. package/.claude/settings.json +41 -0
  26. package/.claude/skills/deepagents-guide/SKILL.md +428 -0
  27. package/.cursor/commands/trellis-before-backend-dev.md +13 -0
  28. package/.cursor/commands/trellis-before-frontend-dev.md +13 -0
  29. package/.cursor/commands/trellis-break-loop.md +107 -0
  30. package/.cursor/commands/trellis-check-backend.md +13 -0
  31. package/.cursor/commands/trellis-check-cross-layer.md +153 -0
  32. package/.cursor/commands/trellis-check-frontend.md +13 -0
  33. package/.cursor/commands/trellis-create-command.md +154 -0
  34. package/.cursor/commands/trellis-finish-work.md +129 -0
  35. package/.cursor/commands/trellis-integrate-skill.md +219 -0
  36. package/.cursor/commands/trellis-onboard.md +358 -0
  37. package/.cursor/commands/trellis-record-session.md +62 -0
  38. package/.cursor/commands/trellis-start.md +156 -0
  39. package/.cursor/commands/trellis-update-spec.md +213 -0
  40. package/.env.example +11 -0
  41. package/.husky/pre-commit +1 -0
  42. package/.mcp.json +8 -0
  43. package/.trellis/.template-hashes.json +65 -0
  44. package/.trellis/.version +1 -0
  45. package/.trellis/scripts/add-session.sh +384 -0
  46. package/.trellis/scripts/common/developer.sh +129 -0
  47. package/.trellis/scripts/common/git-context.sh +263 -0
  48. package/.trellis/scripts/common/paths.sh +208 -0
  49. package/.trellis/scripts/common/phase.sh +150 -0
  50. package/.trellis/scripts/common/registry.sh +247 -0
  51. package/.trellis/scripts/common/task-queue.sh +142 -0
  52. package/.trellis/scripts/common/task-utils.sh +151 -0
  53. package/.trellis/scripts/common/worktree.sh +128 -0
  54. package/.trellis/scripts/create-bootstrap.sh +299 -0
  55. package/.trellis/scripts/get-context.sh +7 -0
  56. package/.trellis/scripts/get-developer.sh +15 -0
  57. package/.trellis/scripts/init-developer.sh +34 -0
  58. package/.trellis/scripts/multi-agent/cleanup.sh +396 -0
  59. package/.trellis/scripts/multi-agent/create-pr.sh +241 -0
  60. package/.trellis/scripts/multi-agent/plan.sh +207 -0
  61. package/.trellis/scripts/multi-agent/start.sh +310 -0
  62. package/.trellis/scripts/multi-agent/status.sh +828 -0
  63. package/.trellis/scripts/task.sh +1118 -0
  64. package/.trellis/spec/backend/deepagents-guide.md +337 -0
  65. package/.trellis/spec/backend/directory-structure.md +126 -0
  66. package/.trellis/spec/backend/examples/skills/deepagents-guide/README.md +11 -0
  67. package/.trellis/spec/backend/examples/skills/deepagents-guide/agent.js.template +20 -0
  68. package/.trellis/spec/backend/examples/skills/deepagents-guide/skills-config.js.template +13 -0
  69. package/.trellis/spec/backend/examples/skills/deepagents-guide/subagent.js.template +19 -0
  70. package/.trellis/spec/backend/hook-guidelines.md +178 -0
  71. package/.trellis/spec/backend/index.md +36 -0
  72. package/.trellis/spec/backend/quality-guidelines.md +201 -0
  73. package/.trellis/spec/backend/state-management.md +76 -0
  74. package/.trellis/spec/backend/tool-guidelines.md +144 -0
  75. package/.trellis/spec/backend/type-safety.md +71 -0
  76. package/.trellis/spec/guides/code-reuse-thinking-guide.md +92 -0
  77. package/.trellis/spec/guides/cross-layer-thinking-guide.md +94 -0
  78. package/.trellis/spec/guides/index.md +79 -0
  79. package/.trellis/tasks/archive/02-02-evolving-skills/prd.md +61 -0
  80. package/.trellis/tasks/archive/02-02-evolving-skills/task.json +29 -0
  81. package/.trellis/tasks/archive/2026-02/00-bootstrap-guidelines/prd.md +86 -0
  82. package/.trellis/tasks/archive/2026-02/00-bootstrap-guidelines/task.json +27 -0
  83. package/.trellis/tasks/archive/2026-02/02-02-skills-system/check.jsonl +3 -0
  84. package/.trellis/tasks/archive/2026-02/02-02-skills-system/debug.jsonl +2 -0
  85. package/.trellis/tasks/archive/2026-02/02-02-skills-system/implement.jsonl +5 -0
  86. package/.trellis/tasks/archive/2026-02/02-02-skills-system/prd.md +33 -0
  87. package/.trellis/tasks/archive/2026-02/02-02-skills-system/task.json +41 -0
  88. package/.trellis/workflow.md +407 -0
  89. package/.trellis/workspace/index.md +123 -0
  90. package/.trellis/workspace/pony/index.md +40 -0
  91. package/.trellis/workspace/pony/journal-1.md +7 -0
  92. package/.trellis/worktree.yaml +47 -0
  93. package/AGENTS.md +18 -0
  94. package/CLAUDE.md +292 -0
  95. package/README.md +134 -0
  96. package/agents/deepspider.md +142 -0
  97. package/docs/DEBUG.md +42 -0
  98. package/docs/GUIDE.md +334 -0
  99. package/docs/PROMPT.md +60 -0
  100. package/docs/USAGE.md +226 -0
  101. package/eslint.config.js +51 -0
  102. package/package.json +78 -0
  103. package/requirements-crypto.txt +14 -0
  104. package/src/agent/index.js +97 -0
  105. package/src/agent/logger.js +164 -0
  106. package/src/agent/middleware/filterTools.js +64 -0
  107. package/src/agent/middleware/report.js +79 -0
  108. package/src/agent/prompts/system.js +315 -0
  109. package/src/agent/run.js +575 -0
  110. package/src/agent/skills/anti-detect/SKILL.md +28 -0
  111. package/src/agent/skills/anti-detect/evolved.md +12 -0
  112. package/src/agent/skills/captcha/SKILL.md +37 -0
  113. package/src/agent/skills/captcha/evolved.md +12 -0
  114. package/src/agent/skills/config.js +30 -0
  115. package/src/agent/skills/crawler/SKILL.md +9 -0
  116. package/src/agent/skills/crawler/evolved.md +16 -0
  117. package/src/agent/skills/dynamic-analysis/SKILL.md +91 -0
  118. package/src/agent/skills/dynamic-analysis/evolved.md +12 -0
  119. package/src/agent/skills/env/SKILL.md +72 -0
  120. package/src/agent/skills/env/evolved.md +12 -0
  121. package/src/agent/skills/evolve.js +79 -0
  122. package/src/agent/skills/general/SKILL.md +12 -0
  123. package/src/agent/skills/general/evolved.md +12 -0
  124. package/src/agent/skills/js2python/SKILL.md +30 -0
  125. package/src/agent/skills/js2python/evolved.md +13 -0
  126. package/src/agent/skills/report/SKILL.md +21 -0
  127. package/src/agent/skills/report/evolved.md +12 -0
  128. package/src/agent/skills/sandbox/SKILL.md +22 -0
  129. package/src/agent/skills/sandbox/evolved.md +16 -0
  130. package/src/agent/skills/static-analysis/SKILL.md +93 -0
  131. package/src/agent/skills/static-analysis/evolved.md +12 -0
  132. package/src/agent/skills/xpath/SKILL.md +119 -0
  133. package/src/agent/subagents/anti-detect.js +45 -0
  134. package/src/agent/subagents/captcha.js +51 -0
  135. package/src/agent/subagents/crawler.js +138 -0
  136. package/src/agent/subagents/dynamic.js +64 -0
  137. package/src/agent/subagents/env-agent.js +82 -0
  138. package/src/agent/subagents/index.js +37 -0
  139. package/src/agent/subagents/js2python.js +72 -0
  140. package/src/agent/subagents/sandbox.js +55 -0
  141. package/src/agent/subagents/static.js +66 -0
  142. package/src/agent/tools/analysis.js +135 -0
  143. package/src/agent/tools/analyzer.js +85 -0
  144. package/src/agent/tools/anti-detect.js +89 -0
  145. package/src/agent/tools/antidebug.js +64 -0
  146. package/src/agent/tools/async.js +43 -0
  147. package/src/agent/tools/browser.js +324 -0
  148. package/src/agent/tools/captcha.js +223 -0
  149. package/src/agent/tools/capture.js +179 -0
  150. package/src/agent/tools/correlate.js +303 -0
  151. package/src/agent/tools/crawler.js +116 -0
  152. package/src/agent/tools/cryptohook.js +80 -0
  153. package/src/agent/tools/debug.js +246 -0
  154. package/src/agent/tools/deobfuscator.js +90 -0
  155. package/src/agent/tools/env.js +83 -0
  156. package/src/agent/tools/envdump.js +92 -0
  157. package/src/agent/tools/evolve.js +164 -0
  158. package/src/agent/tools/extract.js +114 -0
  159. package/src/agent/tools/extractor.js +54 -0
  160. package/src/agent/tools/file.js +224 -0
  161. package/src/agent/tools/hook.js +84 -0
  162. package/src/agent/tools/hookManager.js +178 -0
  163. package/src/agent/tools/index.js +137 -0
  164. package/src/agent/tools/nodejs.js +101 -0
  165. package/src/agent/tools/patch.js +46 -0
  166. package/src/agent/tools/preprocess.js +71 -0
  167. package/src/agent/tools/profile.js +122 -0
  168. package/src/agent/tools/python.js +627 -0
  169. package/src/agent/tools/report.js +124 -0
  170. package/src/agent/tools/runtime.js +132 -0
  171. package/src/agent/tools/sandbox.js +79 -0
  172. package/src/agent/tools/store.js +73 -0
  173. package/src/agent/tools/trace.js +74 -0
  174. package/src/agent/tools/tracing.js +201 -0
  175. package/src/agent/tools/utils.js +51 -0
  176. package/src/agent/tools/verify.js +184 -0
  177. package/src/agent/tools/webcrack.js +109 -0
  178. package/src/analyzer/ASTAnalyzer.js +387 -0
  179. package/src/analyzer/CallStackAnalyzer.js +379 -0
  180. package/src/analyzer/Deobfuscator.js +289 -0
  181. package/src/analyzer/EncryptionAnalyzer.js +99 -0
  182. package/src/analyzer/index.js +22 -0
  183. package/src/browser/EnvBridge.js +186 -0
  184. package/src/browser/cdp.js +168 -0
  185. package/src/browser/client.js +197 -0
  186. package/src/browser/collector.js +444 -0
  187. package/src/browser/collectors/RequestCryptoLinker.js +109 -0
  188. package/src/browser/collectors/ResponseSearcher.js +107 -0
  189. package/src/browser/collectors/ScriptCollector.js +158 -0
  190. package/src/browser/collectors/index.js +26 -0
  191. package/src/browser/defaultHooks.js +932 -0
  192. package/src/browser/hooks/crypto.js +55 -0
  193. package/src/browser/hooks/index.js +64 -0
  194. package/src/browser/hooks/native.js +9 -0
  195. package/src/browser/hooks/network.js +33 -0
  196. package/src/browser/index.js +42 -0
  197. package/src/browser/interceptors/NetworkInterceptor.js +116 -0
  198. package/src/browser/interceptors/ScriptInterceptor.js +76 -0
  199. package/src/browser/interceptors/index.js +6 -0
  200. package/src/browser/ui/analysisPanel.js +1782 -0
  201. package/src/browser/ui/confirmDialog.js +158 -0
  202. package/src/browser/ui/panel.html +152 -0
  203. package/src/browser/ui/selector.js +170 -0
  204. package/src/config/index.js +5 -0
  205. package/src/config/paths.js +71 -0
  206. package/src/config/patterns/crypto.js +36 -0
  207. package/src/config/profiles/chrome.json +71 -0
  208. package/src/config/profiles/firefox.json +44 -0
  209. package/src/config/profiles/safari.json +38 -0
  210. package/src/core/EnvMonitor.js +200 -0
  211. package/src/core/PatchGenerator.js +278 -0
  212. package/src/core/Sandbox.js +181 -0
  213. package/src/env/AntiAntiDebug.js +111 -0
  214. package/src/env/AsyncHook.js +68 -0
  215. package/src/env/BrowserAPIList.js +265 -0
  216. package/src/env/CookieHook.js +48 -0
  217. package/src/env/CryptoHook.js +205 -0
  218. package/src/env/EnvCodeGenerator.js +157 -0
  219. package/src/env/EnvDumper.js +356 -0
  220. package/src/env/EnvExtractor.js +220 -0
  221. package/src/env/HookBase.js +618 -0
  222. package/src/env/NetworkHook.js +159 -0
  223. package/src/env/modules/bom/history.js +29 -0
  224. package/src/env/modules/bom/location.js +26 -0
  225. package/src/env/modules/bom/navigator.js +70 -0
  226. package/src/env/modules/bom/screen.js +26 -0
  227. package/src/env/modules/bom/storage.js +23 -0
  228. package/src/env/modules/dom/document.js +110 -0
  229. package/src/env/modules/dom/event.js +51 -0
  230. package/src/env/modules/index.js +34 -0
  231. package/src/env/modules/webapi/fetch.js +46 -0
  232. package/src/env/modules/webapi/url.js +47 -0
  233. package/src/env/modules/webapi/xhr.js +48 -0
  234. package/src/index.js +27 -0
  235. package/src/mcp/server.js +89 -0
  236. package/src/store/DataStore.js +708 -0
  237. package/src/store/Store.js +158 -0
  238. package/src/store/Validator.js +24 -0
  239. package/test/analyze.test.js +90 -0
  240. package/test/envdump.test.js +74 -0
  241. package/test/flow.test.js +90 -0
  242. package/test/hooks.test.js +138 -0
  243. package/test/plugin.test.js +35 -0
  244. package/test/refactor-full.test.js +30 -0
  245. package/test/refactor.test.js +21 -0
  246. package/test/samples/obfuscated.js +61 -0
  247. package/test/samples/original.js +66 -0
  248. package/test/samples/v10_eval_chain.js +52 -0
  249. package/test/samples/v11_bytecode_vm.js +81 -0
  250. package/test/samples/v12_polymorphic.js +69 -0
  251. package/test/samples/v1_ob_basic.js +98 -0
  252. package/test/samples/v2_ob_advanced.js +99 -0
  253. package/test/samples/v3_jjencode.js +77 -0
  254. package/test/samples/v4_aaencode.js +73 -0
  255. package/test/samples/v5_control_flow.js +86 -0
  256. package/test/samples/v6_string_encryption.js +71 -0
  257. package/test/samples/v7_jsvmp.js +83 -0
  258. package/test/samples/v8_anti_debug.js +79 -0
  259. package/test/samples/v9_proxy_trap.js +49 -0
  260. package/test/samples.test.js +96 -0
  261. package/test/webcrack.test.js +55 -0
@@ -0,0 +1,758 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Multi-Agent Pipeline Context Injection Hook
4
+
5
+ Core Design Philosophy:
6
+ - Dispatch becomes a pure dispatcher, only responsible for "calling subagents"
7
+ - Hook is responsible for injecting all context, subagent works autonomously with complete info
8
+ - Each agent has a dedicated jsonl file defining its context
9
+ - No resume needed, no segmentation, behavior controlled by code not prompt
10
+
11
+ Trigger: PreToolUse (before Task tool call)
12
+
13
+ Context Source: .trellis/.current-task points to task directory
14
+ - implement.jsonl - Implement agent dedicated context
15
+ - check.jsonl - Check agent dedicated context
16
+ - debug.jsonl - Debug agent dedicated context
17
+ - research.jsonl - Research agent dedicated context (optional, usually not needed)
18
+ - cr.jsonl - Code review dedicated context
19
+ - prd.md - Requirements document
20
+ - info.md - Technical design
21
+ - codex-review-output.txt - Code Review results
22
+ """
23
+
24
+ import json
25
+ import os
26
+ import sys
27
+ from pathlib import Path
28
+
29
+ # =============================================================================
30
+ # Path Constants (change here to rename directories)
31
+ # =============================================================================
32
+
33
+ DIR_WORKFLOW = ".trellis"
34
+ DIR_WORKSPACE = "workspace"
35
+ DIR_TASKS = "tasks"
36
+ DIR_SPEC = "spec"
37
+ FILE_CURRENT_TASK = ".current-task"
38
+ FILE_TASK_JSON = "task.json"
39
+
40
+ # Agents that don't update phase (can be called at any time)
41
+ AGENTS_NO_PHASE_UPDATE = {"debug", "research"}
42
+
43
+ # =============================================================================
44
+ # Subagent Constants (change here to rename subagent types)
45
+ # =============================================================================
46
+
47
+ AGENT_IMPLEMENT = "implement"
48
+ AGENT_CHECK = "check"
49
+ AGENT_DEBUG = "debug"
50
+ AGENT_RESEARCH = "research"
51
+
52
+ # Agents that require a task directory
53
+ AGENTS_REQUIRE_TASK = (AGENT_IMPLEMENT, AGENT_CHECK, AGENT_DEBUG)
54
+ # All supported agents
55
+ AGENTS_ALL = (AGENT_IMPLEMENT, AGENT_CHECK, AGENT_DEBUG, AGENT_RESEARCH)
56
+
57
+
58
+ def find_repo_root(start_path: str) -> str | None:
59
+ """
60
+ Find git repo root from start_path upwards
61
+
62
+ Returns:
63
+ Repo root path, or None if not found
64
+ """
65
+ current = Path(start_path).resolve()
66
+ while current != current.parent:
67
+ if (current / ".git").exists():
68
+ return str(current)
69
+ current = current.parent
70
+ return None
71
+
72
+
73
+ def get_current_task(repo_root: str) -> str | None:
74
+ """
75
+ Read current task directory path from .trellis/.current-task
76
+
77
+ Returns:
78
+ Task directory relative path (relative to repo_root)
79
+ None if not set
80
+ """
81
+ current_task_file = os.path.join(repo_root, DIR_WORKFLOW, FILE_CURRENT_TASK)
82
+ if not os.path.exists(current_task_file):
83
+ return None
84
+
85
+ try:
86
+ with open(current_task_file, "r", encoding="utf-8") as f:
87
+ content = f.read().strip()
88
+ return content if content else None
89
+ except Exception:
90
+ return None
91
+
92
+
93
+ def update_current_phase(repo_root: str, task_dir: str, subagent_type: str) -> None:
94
+ """
95
+ Update current_phase in task.json based on subagent_type.
96
+
97
+ This ensures phase tracking is always accurate, regardless of whether
98
+ dispatch agent remembers to update it.
99
+
100
+ Logic:
101
+ - Read next_action array from task.json
102
+ - Find the next phase whose action matches subagent_type
103
+ - Only move forward, never backward
104
+ - Some agents (debug, research) don't update phase
105
+ """
106
+ if subagent_type in AGENTS_NO_PHASE_UPDATE:
107
+ return
108
+
109
+ task_json_path = os.path.join(repo_root, task_dir, FILE_TASK_JSON)
110
+ if not os.path.exists(task_json_path):
111
+ return
112
+
113
+ try:
114
+ with open(task_json_path, "r", encoding="utf-8") as f:
115
+ task_data = json.load(f)
116
+
117
+ current_phase = task_data.get("current_phase", 0)
118
+ next_actions = task_data.get("next_action", [])
119
+
120
+ # Map action names to subagent types
121
+ # "implement" -> "implement", "check" -> "check", "finish" -> "check"
122
+ action_to_agent = {
123
+ "implement": "implement",
124
+ "check": "check",
125
+ "finish": "check", # finish uses check agent
126
+ }
127
+
128
+ # Find the next phase that matches this subagent_type
129
+ new_phase = None
130
+ for action in next_actions:
131
+ phase_num = action.get("phase", 0)
132
+ action_name = action.get("action", "")
133
+ expected_agent = action_to_agent.get(action_name)
134
+
135
+ # Only consider phases after current_phase
136
+ if phase_num > current_phase and expected_agent == subagent_type:
137
+ new_phase = phase_num
138
+ break
139
+
140
+ if new_phase is not None:
141
+ task_data["current_phase"] = new_phase
142
+
143
+ with open(task_json_path, "w", encoding="utf-8") as f:
144
+ json.dump(task_data, f, indent=2, ensure_ascii=False)
145
+ except Exception:
146
+ # Don't fail the hook if phase update fails
147
+ pass
148
+
149
+
150
+ def read_file_content(base_path: str, file_path: str) -> str | None:
151
+ """Read file content, return None if file doesn't exist"""
152
+ full_path = os.path.join(base_path, file_path)
153
+ if os.path.exists(full_path) and os.path.isfile(full_path):
154
+ try:
155
+ with open(full_path, "r", encoding="utf-8") as f:
156
+ return f.read()
157
+ except Exception:
158
+ return None
159
+ return None
160
+
161
+
162
+ def read_directory_contents(
163
+ base_path: str, dir_path: str, max_files: int = 20
164
+ ) -> list[tuple[str, str]]:
165
+ """
166
+ Read all .md files in a directory
167
+
168
+ Args:
169
+ base_path: Base path (usually repo_root)
170
+ dir_path: Directory relative path
171
+ max_files: Max files to read (prevent huge directories)
172
+
173
+ Returns:
174
+ [(file_path, content), ...]
175
+ """
176
+ full_path = os.path.join(base_path, dir_path)
177
+ if not os.path.exists(full_path) or not os.path.isdir(full_path):
178
+ return []
179
+
180
+ results = []
181
+ try:
182
+ # Only read .md files, sorted by filename
183
+ md_files = sorted(
184
+ [
185
+ f
186
+ for f in os.listdir(full_path)
187
+ if f.endswith(".md") and os.path.isfile(os.path.join(full_path, f))
188
+ ]
189
+ )
190
+
191
+ for filename in md_files[:max_files]:
192
+ file_full_path = os.path.join(full_path, filename)
193
+ relative_path = os.path.join(dir_path, filename)
194
+ try:
195
+ with open(file_full_path, "r", encoding="utf-8") as f:
196
+ content = f.read()
197
+ results.append((relative_path, content))
198
+ except Exception:
199
+ continue
200
+ except Exception:
201
+ pass
202
+
203
+ return results
204
+
205
+
206
+ def read_jsonl_entries(base_path: str, jsonl_path: str) -> list[tuple[str, str]]:
207
+ """
208
+ Read all file/directory contents referenced in jsonl file
209
+
210
+ Schema:
211
+ {"file": "path/to/file.md", "reason": "..."}
212
+ {"file": "path/to/dir/", "type": "directory", "reason": "..."}
213
+
214
+ Returns:
215
+ [(path, content), ...]
216
+ """
217
+ full_path = os.path.join(base_path, jsonl_path)
218
+ if not os.path.exists(full_path):
219
+ return []
220
+
221
+ results = []
222
+ try:
223
+ with open(full_path, "r", encoding="utf-8") as f:
224
+ for line in f:
225
+ line = line.strip()
226
+ if not line:
227
+ continue
228
+ try:
229
+ item = json.loads(line)
230
+ file_path = item.get("file") or item.get("path")
231
+ entry_type = item.get("type", "file")
232
+
233
+ if not file_path:
234
+ continue
235
+
236
+ if entry_type == "directory":
237
+ # Read all .md files in directory
238
+ dir_contents = read_directory_contents(base_path, file_path)
239
+ results.extend(dir_contents)
240
+ else:
241
+ # Read single file
242
+ content = read_file_content(base_path, file_path)
243
+ if content:
244
+ results.append((file_path, content))
245
+ except json.JSONDecodeError:
246
+ continue
247
+ except Exception:
248
+ pass
249
+
250
+ return results
251
+
252
+
253
+ def get_agent_context(repo_root: str, task_dir: str, agent_type: str) -> str:
254
+ """
255
+ Get complete context for specified agent
256
+
257
+ Prioritize agent-specific jsonl, fallback to spec.jsonl if not exists
258
+ """
259
+ context_parts = []
260
+
261
+ # 1. Try agent-specific jsonl
262
+ agent_jsonl = f"{task_dir}/{agent_type}.jsonl"
263
+ agent_entries = read_jsonl_entries(repo_root, agent_jsonl)
264
+
265
+ # 2. If agent-specific jsonl doesn't exist or empty, fallback to spec.jsonl
266
+ if not agent_entries:
267
+ agent_entries = read_jsonl_entries(repo_root, f"{task_dir}/spec.jsonl")
268
+
269
+ # 3. Add all files from jsonl
270
+ for file_path, content in agent_entries:
271
+ context_parts.append(f"=== {file_path} ===\n{content}")
272
+
273
+ return "\n\n".join(context_parts)
274
+
275
+
276
+ def get_implement_context(repo_root: str, task_dir: str) -> str:
277
+ """
278
+ Complete context for Implement Agent
279
+
280
+ Read order:
281
+ 1. All files in implement.jsonl (dev specs)
282
+ 2. prd.md (requirements)
283
+ 3. info.md (technical design)
284
+ """
285
+ context_parts = []
286
+
287
+ # 1. Read implement.jsonl (or fallback to spec.jsonl)
288
+ base_context = get_agent_context(repo_root, task_dir, "implement")
289
+ if base_context:
290
+ context_parts.append(base_context)
291
+
292
+ # 2. Requirements document
293
+ prd_content = read_file_content(repo_root, f"{task_dir}/prd.md")
294
+ if prd_content:
295
+ context_parts.append(f"=== {task_dir}/prd.md (Requirements) ===\n{prd_content}")
296
+
297
+ # 3. Technical design
298
+ info_content = read_file_content(repo_root, f"{task_dir}/info.md")
299
+ if info_content:
300
+ context_parts.append(
301
+ f"=== {task_dir}/info.md (Technical Design) ===\n{info_content}"
302
+ )
303
+
304
+ return "\n\n".join(context_parts)
305
+
306
+
307
+ def get_check_context(repo_root: str, task_dir: str) -> str:
308
+ """
309
+ Complete context for Check Agent
310
+
311
+ Read order:
312
+ 1. All files in check.jsonl (check specs + dev specs)
313
+ 2. prd.md (for understanding task intent)
314
+ """
315
+ context_parts = []
316
+
317
+ # 1. Read check.jsonl (or fallback to spec.jsonl + hardcoded check files)
318
+ check_entries = read_jsonl_entries(repo_root, f"{task_dir}/check.jsonl")
319
+
320
+ if check_entries:
321
+ for file_path, content in check_entries:
322
+ context_parts.append(f"=== {file_path} ===\n{content}")
323
+ else:
324
+ # Fallback: use hardcoded check files + spec.jsonl
325
+ check_files = [
326
+ (".claude/commands/trellis/finish-work.md", "Finish work checklist"),
327
+ (".claude/commands/trellis/check-cross-layer.md", "Cross-layer check spec"),
328
+ (".claude/commands/trellis/check-backend.md", "Backend check spec"),
329
+ (".claude/commands/trellis/check-frontend.md", "Frontend check spec"),
330
+ ]
331
+ for file_path, description in check_files:
332
+ content = read_file_content(repo_root, file_path)
333
+ if content:
334
+ context_parts.append(f"=== {file_path} ({description}) ===\n{content}")
335
+
336
+ # Add spec.jsonl
337
+ spec_entries = read_jsonl_entries(repo_root, f"{task_dir}/spec.jsonl")
338
+ for file_path, content in spec_entries:
339
+ context_parts.append(f"=== {file_path} (Dev spec) ===\n{content}")
340
+
341
+ # 2. Requirements document (for understanding task intent)
342
+ prd_content = read_file_content(repo_root, f"{task_dir}/prd.md")
343
+ if prd_content:
344
+ context_parts.append(
345
+ f"=== {task_dir}/prd.md (Requirements - for understanding intent) ===\n{prd_content}"
346
+ )
347
+
348
+ return "\n\n".join(context_parts)
349
+
350
+
351
+ def get_finish_context(repo_root: str, task_dir: str) -> str:
352
+ """
353
+ Complete context for Finish phase (final check before PR)
354
+
355
+ Read order:
356
+ 1. All files in finish.jsonl (if exists)
357
+ 2. Fallback to finish-work.md only (lightweight final check)
358
+ 3. prd.md (for verifying requirements are met)
359
+ """
360
+ context_parts = []
361
+
362
+ # 1. Try finish.jsonl first
363
+ finish_entries = read_jsonl_entries(repo_root, f"{task_dir}/finish.jsonl")
364
+
365
+ if finish_entries:
366
+ for file_path, content in finish_entries:
367
+ context_parts.append(f"=== {file_path} ===\n{content}")
368
+ else:
369
+ # Fallback: only finish-work.md (lightweight)
370
+ finish_work = read_file_content(
371
+ repo_root, ".claude/commands/trellis/finish-work.md"
372
+ )
373
+ if finish_work:
374
+ context_parts.append(
375
+ f"=== .claude/commands/trellis/finish-work.md (Finish checklist) ===\n{finish_work}"
376
+ )
377
+
378
+ # 2. Requirements document (for verifying requirements are met)
379
+ prd_content = read_file_content(repo_root, f"{task_dir}/prd.md")
380
+ if prd_content:
381
+ context_parts.append(
382
+ f"=== {task_dir}/prd.md (Requirements - verify all met) ===\n{prd_content}"
383
+ )
384
+
385
+ return "\n\n".join(context_parts)
386
+
387
+
388
+ def get_debug_context(repo_root: str, task_dir: str) -> str:
389
+ """
390
+ Complete context for Debug Agent
391
+
392
+ Read order:
393
+ 1. All files in debug.jsonl (specs needed for fixing)
394
+ 2. codex-review-output.txt (Codex Review results)
395
+ """
396
+ context_parts = []
397
+
398
+ # 1. Read debug.jsonl (or fallback to spec.jsonl + hardcoded check files)
399
+ debug_entries = read_jsonl_entries(repo_root, f"{task_dir}/debug.jsonl")
400
+
401
+ if debug_entries:
402
+ for file_path, content in debug_entries:
403
+ context_parts.append(f"=== {file_path} ===\n{content}")
404
+ else:
405
+ # Fallback: use spec.jsonl + hardcoded check files
406
+ spec_entries = read_jsonl_entries(repo_root, f"{task_dir}/spec.jsonl")
407
+ for file_path, content in spec_entries:
408
+ context_parts.append(f"=== {file_path} (Dev spec) ===\n{content}")
409
+
410
+ check_files = [
411
+ (".claude/commands/trellis/check-backend.md", "Backend check spec"),
412
+ (".claude/commands/trellis/check-frontend.md", "Frontend check spec"),
413
+ (".claude/commands/trellis/check-cross-layer.md", "Cross-layer check spec"),
414
+ ]
415
+ for file_path, description in check_files:
416
+ content = read_file_content(repo_root, file_path)
417
+ if content:
418
+ context_parts.append(f"=== {file_path} ({description}) ===\n{content}")
419
+
420
+ # 2. Codex review output (if exists)
421
+ codex_output = read_file_content(repo_root, f"{task_dir}/codex-review-output.txt")
422
+ if codex_output:
423
+ context_parts.append(
424
+ f"=== {task_dir}/codex-review-output.txt (Codex Review Results) ===\n{codex_output}"
425
+ )
426
+
427
+ return "\n\n".join(context_parts)
428
+
429
+
430
+ def build_implement_prompt(original_prompt: str, context: str) -> str:
431
+ """Build complete prompt for Implement"""
432
+ return f"""# Implement Agent Task
433
+
434
+ You are the Implement Agent in the Multi-Agent Pipeline.
435
+
436
+ ## Your Context
437
+
438
+ All the information you need has been prepared for you:
439
+
440
+ {context}
441
+
442
+ ---
443
+
444
+ ## Your Task
445
+
446
+ {original_prompt}
447
+
448
+ ---
449
+
450
+ ## Workflow
451
+
452
+ 1. **Understand specs** - All dev specs are injected above, understand them
453
+ 2. **Understand requirements** - Read requirements document and technical design
454
+ 3. **Implement feature** - Implement following specs and design
455
+ 4. **Self-check** - Ensure code quality against check specs
456
+
457
+ ## Important Constraints
458
+
459
+ - Do NOT execute git commit, only code modifications
460
+ - Follow all dev specs injected above
461
+ - Report list of modified/created files when done"""
462
+
463
+
464
+ def build_check_prompt(original_prompt: str, context: str) -> str:
465
+ """Build complete prompt for Check"""
466
+ return f"""# Check Agent Task
467
+
468
+ You are the Check Agent in the Multi-Agent Pipeline (code and cross-layer checker).
469
+
470
+ ## Your Context
471
+
472
+ All check specs and dev specs you need:
473
+
474
+ {context}
475
+
476
+ ---
477
+
478
+ ## Your Task
479
+
480
+ {original_prompt}
481
+
482
+ ---
483
+
484
+ ## Workflow
485
+
486
+ 1. **Get changes** - Run `git diff --name-only` and `git diff` to get code changes
487
+ 2. **Check against specs** - Check item by item against specs above
488
+ 3. **Self-fix** - Fix issues directly, don't just report
489
+ 4. **Run verification** - Run project's lint and typecheck commands
490
+
491
+ ## Important Constraints
492
+
493
+ - Fix issues yourself, don't just report
494
+ - Must execute complete checklist in check specs
495
+ - Pay special attention to impact radius analysis (L1-L5)"""
496
+
497
+
498
+ def build_finish_prompt(original_prompt: str, context: str) -> str:
499
+ """Build complete prompt for Finish (final check before PR)"""
500
+ return f"""# Finish Agent Task
501
+
502
+ You are performing the final check before creating a PR.
503
+
504
+ ## Your Context
505
+
506
+ Finish checklist and requirements:
507
+
508
+ {context}
509
+
510
+ ---
511
+
512
+ ## Your Task
513
+
514
+ {original_prompt}
515
+
516
+ ---
517
+
518
+ ## Workflow
519
+
520
+ 1. **Review changes** - Run `git diff --name-only` to see all changed files
521
+ 2. **Verify requirements** - Check each requirement in prd.md is implemented
522
+ 3. **Run final checks** - Execute finish-work.md checklist
523
+ 4. **Confirm ready** - Ensure code is ready for PR
524
+
525
+ ## Important Constraints
526
+
527
+ - This is a final verification, not a fix phase
528
+ - If critical issues found, report them clearly
529
+ - Verify all acceptance criteria in prd.md are met"""
530
+
531
+
532
+ def build_debug_prompt(original_prompt: str, context: str) -> str:
533
+ """Build complete prompt for Debug"""
534
+ return f"""# Debug Agent Task
535
+
536
+ You are the Debug Agent in the Multi-Agent Pipeline (issue fixer).
537
+
538
+ ## Your Context
539
+
540
+ Dev specs and Codex Review results:
541
+
542
+ {context}
543
+
544
+ ---
545
+
546
+ ## Your Task
547
+
548
+ {original_prompt}
549
+
550
+ ---
551
+
552
+ ## Workflow
553
+
554
+ 1. **Understand issues** - Analyze issues pointed out in Codex Review
555
+ 2. **Locate code** - Find positions that need fixing
556
+ 3. **Fix against specs** - Fix issues following dev specs
557
+ 4. **Verify fixes** - Run typecheck to ensure no new issues
558
+
559
+ ## Important Constraints
560
+
561
+ - Do NOT execute git commit, only code modifications
562
+ - Run typecheck after each fix to verify
563
+ - Report which issues were fixed and which files were modified"""
564
+
565
+
566
+ def get_research_context(repo_root: str, task_dir: str | None) -> str:
567
+ """
568
+ Context for Research Agent
569
+
570
+ Research doesn't need much preset context, only needs:
571
+ 1. Project structure overview (where spec directories are)
572
+ 2. Optional research.jsonl (if there are specific search needs)
573
+ """
574
+ context_parts = []
575
+
576
+ # 1. Project structure overview (uses constants for paths)
577
+ spec_path = f"{DIR_WORKFLOW}/{DIR_SPEC}"
578
+ project_structure = f"""## Project Spec Directory Structure
579
+
580
+ ```
581
+ {spec_path}/
582
+ ├── shared/ # Cross-project common specs (TypeScript, code quality, git)
583
+ ├── frontend/ # Frontend standards
584
+ ├── backend/ # Backend standards
585
+ └── guides/ # Thinking guides (cross-layer, code reuse, etc.)
586
+
587
+ {DIR_WORKFLOW}/big-question/ # Known issues and pitfalls
588
+ ```
589
+
590
+ ## Search Tips
591
+
592
+ - Spec files: `{spec_path}/**/*.md`
593
+ - Known issues: `{DIR_WORKFLOW}/big-question/`
594
+ - Code search: Use Glob and Grep tools
595
+ - Tech solutions: Use mcp__exa__web_search_exa or mcp__exa__get_code_context_exa"""
596
+
597
+ context_parts.append(project_structure)
598
+
599
+ # 2. If task directory exists, try reading research.jsonl (optional)
600
+ if task_dir:
601
+ research_entries = read_jsonl_entries(repo_root, f"{task_dir}/research.jsonl")
602
+ if research_entries:
603
+ context_parts.append(
604
+ "\n## Additional Search Context (from research.jsonl)\n"
605
+ )
606
+ for file_path, content in research_entries:
607
+ context_parts.append(f"=== {file_path} ===\n{content}")
608
+
609
+ return "\n\n".join(context_parts)
610
+
611
+
612
+ def build_research_prompt(original_prompt: str, context: str) -> str:
613
+ """Build complete prompt for Research"""
614
+ return f"""# Research Agent Task
615
+
616
+ You are the Research Agent in the Multi-Agent Pipeline (search researcher).
617
+
618
+ ## Core Principle
619
+
620
+ **You do one thing: find and explain information.**
621
+
622
+ You are a documenter, not a reviewer.
623
+
624
+ ## Project Info
625
+
626
+ {context}
627
+
628
+ ---
629
+
630
+ ## Your Task
631
+
632
+ {original_prompt}
633
+
634
+ ---
635
+
636
+ ## Workflow
637
+
638
+ 1. **Understand query** - Determine search type (internal/external) and scope
639
+ 2. **Plan search** - List search steps for complex queries
640
+ 3. **Execute search** - Execute multiple independent searches in parallel
641
+ 4. **Organize results** - Output structured report
642
+
643
+ ## Search Tools
644
+
645
+ | Tool | Purpose |
646
+ |------|---------|
647
+ | Glob | Search by filename pattern |
648
+ | Grep | Search by content |
649
+ | Read | Read file content |
650
+ | mcp__exa__web_search_exa | External web search |
651
+ | mcp__exa__get_code_context_exa | External code/doc search |
652
+
653
+ ## Strict Boundaries
654
+
655
+ **Only allowed**: Describe what exists, where it is, how it works
656
+
657
+ **Forbidden** (unless explicitly asked):
658
+ - Suggest improvements
659
+ - Criticize implementation
660
+ - Recommend refactoring
661
+ - Modify any files
662
+
663
+ ## Report Format
664
+
665
+ Provide structured search results including:
666
+ - List of files found (with paths)
667
+ - Code pattern analysis (if applicable)
668
+ - Related spec documents
669
+ - External references (if any)"""
670
+
671
+
672
+ def main():
673
+ try:
674
+ input_data = json.load(sys.stdin)
675
+ except json.JSONDecodeError:
676
+ sys.exit(0)
677
+
678
+ tool_name = input_data.get("tool_name", "")
679
+
680
+ if tool_name != "Task":
681
+ sys.exit(0)
682
+
683
+ tool_input = input_data.get("tool_input", {})
684
+ subagent_type = tool_input.get("subagent_type", "")
685
+ original_prompt = tool_input.get("prompt", "")
686
+ cwd = input_data.get("cwd", os.getcwd())
687
+
688
+ # Only handle subagent types we care about
689
+ if subagent_type not in AGENTS_ALL:
690
+ sys.exit(0)
691
+
692
+ # Find repo root
693
+ repo_root = find_repo_root(cwd)
694
+ if not repo_root:
695
+ sys.exit(0)
696
+
697
+ # Get current task directory (research doesn't require it)
698
+ task_dir = get_current_task(repo_root)
699
+
700
+ # implement/check/debug need task directory
701
+ if subagent_type in AGENTS_REQUIRE_TASK:
702
+ if not task_dir:
703
+ sys.exit(0)
704
+ # Check if task directory exists
705
+ task_dir_full = os.path.join(repo_root, task_dir)
706
+ if not os.path.exists(task_dir_full):
707
+ sys.exit(0)
708
+
709
+ # Update current_phase in task.json (system-level enforcement)
710
+ update_current_phase(repo_root, task_dir, subagent_type)
711
+
712
+ # Check for [finish] marker in prompt (check agent with finish context)
713
+ is_finish_phase = "[finish]" in original_prompt.lower()
714
+
715
+ # Get context and build prompt based on subagent type
716
+ if subagent_type == AGENT_IMPLEMENT:
717
+ assert task_dir is not None # validated above
718
+ context = get_implement_context(repo_root, task_dir)
719
+ new_prompt = build_implement_prompt(original_prompt, context)
720
+ elif subagent_type == AGENT_CHECK:
721
+ assert task_dir is not None # validated above
722
+ if is_finish_phase:
723
+ # Finish phase: use finish context (lighter, focused on final verification)
724
+ context = get_finish_context(repo_root, task_dir)
725
+ new_prompt = build_finish_prompt(original_prompt, context)
726
+ else:
727
+ # Regular check phase: use check context (full specs for self-fix loop)
728
+ context = get_check_context(repo_root, task_dir)
729
+ new_prompt = build_check_prompt(original_prompt, context)
730
+ elif subagent_type == AGENT_DEBUG:
731
+ assert task_dir is not None # validated above
732
+ context = get_debug_context(repo_root, task_dir)
733
+ new_prompt = build_debug_prompt(original_prompt, context)
734
+ elif subagent_type == AGENT_RESEARCH:
735
+ # Research can work without task directory
736
+ context = get_research_context(repo_root, task_dir)
737
+ new_prompt = build_research_prompt(original_prompt, context)
738
+ else:
739
+ sys.exit(0)
740
+
741
+ if not context:
742
+ sys.exit(0)
743
+
744
+ # Return updated input
745
+ output = {
746
+ "hookSpecificOutput": {
747
+ "hookEventName": "PreToolUse",
748
+ "permissionDecision": "allow",
749
+ "updatedInput": {**tool_input, "prompt": new_prompt},
750
+ }
751
+ }
752
+
753
+ print(json.dumps(output, ensure_ascii=False))
754
+ sys.exit(0)
755
+
756
+
757
+ if __name__ == "__main__":
758
+ main()