@mindfoldhq/trellis 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 (122) hide show
  1. package/LICENSE +110 -0
  2. package/README.md +149 -0
  3. package/bin/trellis.js +3 -0
  4. package/dist/cli/index.d.ts +2 -0
  5. package/dist/cli/index.d.ts.map +1 -0
  6. package/dist/cli/index.js +42 -0
  7. package/dist/cli/index.js.map +1 -0
  8. package/dist/commands/init.d.ts +11 -0
  9. package/dist/commands/init.d.ts.map +1 -0
  10. package/dist/commands/init.js +236 -0
  11. package/dist/commands/init.js.map +1 -0
  12. package/dist/configurators/claude.d.ts +35 -0
  13. package/dist/configurators/claude.d.ts.map +1 -0
  14. package/dist/configurators/claude.js +83 -0
  15. package/dist/configurators/claude.js.map +1 -0
  16. package/dist/configurators/cursor.d.ts +8 -0
  17. package/dist/configurators/cursor.d.ts.map +1 -0
  18. package/dist/configurators/cursor.js +22 -0
  19. package/dist/configurators/cursor.js.map +1 -0
  20. package/dist/configurators/templates.d.ts +40 -0
  21. package/dist/configurators/templates.d.ts.map +1 -0
  22. package/dist/configurators/templates.js +67 -0
  23. package/dist/configurators/templates.js.map +1 -0
  24. package/dist/configurators/workflow.d.ts +16 -0
  25. package/dist/configurators/workflow.d.ts.map +1 -0
  26. package/dist/configurators/workflow.js +169 -0
  27. package/dist/configurators/workflow.js.map +1 -0
  28. package/dist/constants/paths.d.ts +69 -0
  29. package/dist/constants/paths.d.ts.map +1 -0
  30. package/dist/constants/paths.js +80 -0
  31. package/dist/constants/paths.js.map +1 -0
  32. package/dist/index.d.ts +9 -0
  33. package/dist/index.d.ts.map +1 -0
  34. package/dist/index.js +9 -0
  35. package/dist/index.js.map +1 -0
  36. package/dist/templates/agents/check.txt +120 -0
  37. package/dist/templates/agents/debug.txt +121 -0
  38. package/dist/templates/agents/dispatch.txt +201 -0
  39. package/dist/templates/agents/implement.txt +114 -0
  40. package/dist/templates/agents/index.d.ts +35 -0
  41. package/dist/templates/agents/index.d.ts.map +1 -0
  42. package/dist/templates/agents/index.js +71 -0
  43. package/dist/templates/agents/index.js.map +1 -0
  44. package/dist/templates/agents/research.txt +258 -0
  45. package/dist/templates/commands/claude/start.md.txt +127 -0
  46. package/dist/templates/commands/common/before-backend-dev.txt +13 -0
  47. package/dist/templates/commands/common/before-frontend-dev.txt +13 -0
  48. package/dist/templates/commands/common/break-loop.txt +107 -0
  49. package/dist/templates/commands/common/check-backend.txt +13 -0
  50. package/dist/templates/commands/common/check-cross-layer.txt +153 -0
  51. package/dist/templates/commands/common/check-frontend.txt +13 -0
  52. package/dist/templates/commands/common/create-command.txt +154 -0
  53. package/dist/templates/commands/common/finish-work.txt +129 -0
  54. package/dist/templates/commands/common/integrate-skill.txt +219 -0
  55. package/dist/templates/commands/common/onboard-developer.txt +355 -0
  56. package/dist/templates/commands/common/record-agent-flow.txt +62 -0
  57. package/dist/templates/commands/cursor/start.md.txt +94 -0
  58. package/dist/templates/commands/index.d.ts +46 -0
  59. package/dist/templates/commands/index.d.ts.map +1 -0
  60. package/dist/templates/commands/index.js +151 -0
  61. package/dist/templates/commands/index.js.map +1 -0
  62. package/dist/templates/extract.d.ts +22 -0
  63. package/dist/templates/extract.d.ts.map +1 -0
  64. package/dist/templates/extract.js +34 -0
  65. package/dist/templates/extract.js.map +1 -0
  66. package/dist/templates/hooks/index.d.ts +33 -0
  67. package/dist/templates/hooks/index.d.ts.map +1 -0
  68. package/dist/templates/hooks/index.js +53 -0
  69. package/dist/templates/hooks/index.js.map +1 -0
  70. package/dist/templates/hooks/inject-subagent-context.py +620 -0
  71. package/dist/templates/hooks/settings.json +16 -0
  72. package/dist/templates/markdown/agent-traces-index.md.txt +124 -0
  73. package/dist/templates/markdown/agents.md.txt +18 -0
  74. package/dist/templates/markdown/gitignore.txt +3 -0
  75. package/dist/templates/markdown/index.d.ts +26 -0
  76. package/dist/templates/markdown/index.d.ts.map +1 -0
  77. package/dist/templates/markdown/index.js +33 -0
  78. package/dist/templates/markdown/index.js.map +1 -0
  79. package/dist/templates/markdown/init-agent.md.txt +315 -0
  80. package/dist/templates/markdown/structure/backend/database-guidelines.md.txt +51 -0
  81. package/dist/templates/markdown/structure/backend/directory-structure.md.txt +54 -0
  82. package/dist/templates/markdown/structure/backend/error-handling.md.txt +51 -0
  83. package/dist/templates/markdown/structure/backend/index.md.txt +38 -0
  84. package/dist/templates/markdown/structure/backend/logging-guidelines.md.txt +51 -0
  85. package/dist/templates/markdown/structure/backend/quality-guidelines.md.txt +51 -0
  86. package/dist/templates/markdown/structure/frontend/component-guidelines.md.txt +59 -0
  87. package/dist/templates/markdown/structure/frontend/directory-structure.md.txt +54 -0
  88. package/dist/templates/markdown/structure/frontend/hook-guidelines.md.txt +51 -0
  89. package/dist/templates/markdown/structure/frontend/index.md.txt +39 -0
  90. package/dist/templates/markdown/structure/frontend/quality-guidelines.md.txt +51 -0
  91. package/dist/templates/markdown/structure/frontend/state-management.md.txt +51 -0
  92. package/dist/templates/markdown/structure/frontend/type-safety.md.txt +51 -0
  93. package/dist/templates/markdown/structure/guides/code-reuse-thinking-guide.md.txt +92 -0
  94. package/dist/templates/markdown/structure/guides/cross-layer-thinking-guide.md.txt +94 -0
  95. package/dist/templates/markdown/structure/guides/index.md.txt +79 -0
  96. package/dist/templates/markdown/workflow.md.txt +335 -0
  97. package/dist/templates/scripts/add-session.sh.txt +384 -0
  98. package/dist/templates/scripts/common/developer.sh.txt +130 -0
  99. package/dist/templates/scripts/common/git-context.sh.txt +237 -0
  100. package/dist/templates/scripts/common/paths.sh.txt +201 -0
  101. package/dist/templates/scripts/create-bootstrap.sh.txt +298 -0
  102. package/dist/templates/scripts/feature.sh.txt +700 -0
  103. package/dist/templates/scripts/get-context.sh.txt +7 -0
  104. package/dist/templates/scripts/get-developer.sh.txt +15 -0
  105. package/dist/templates/scripts/index.d.ts +25 -0
  106. package/dist/templates/scripts/index.d.ts.map +1 -0
  107. package/dist/templates/scripts/index.js +28 -0
  108. package/dist/templates/scripts/index.js.map +1 -0
  109. package/dist/templates/scripts/init-developer.sh.txt +34 -0
  110. package/dist/types/ai-tools.d.ts +35 -0
  111. package/dist/types/ai-tools.d.ts.map +1 -0
  112. package/dist/types/ai-tools.js +31 -0
  113. package/dist/types/ai-tools.js.map +1 -0
  114. package/dist/utils/file-writer.d.ts +23 -0
  115. package/dist/utils/file-writer.d.ts.map +1 -0
  116. package/dist/utils/file-writer.js +140 -0
  117. package/dist/utils/file-writer.js.map +1 -0
  118. package/dist/utils/project-detector.d.ts +16 -0
  119. package/dist/utils/project-detector.d.ts.map +1 -0
  120. package/dist/utils/project-detector.js +186 -0
  121. package/dist/utils/project-detector.js.map +1 -0
  122. package/package.json +71 -0
@@ -0,0 +1,620 @@
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-feature points to feature 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_PROGRESS = "agent-traces"
35
+ DIR_FEATURES = "features"
36
+ DIR_STRUCTURE = "structure"
37
+ FILE_CURRENT_FEATURE = ".current-feature"
38
+
39
+ # =============================================================================
40
+ # Subagent Constants (change here to rename subagent types)
41
+ # =============================================================================
42
+
43
+ AGENT_IMPLEMENT = "implement"
44
+ AGENT_CHECK = "check"
45
+ AGENT_DEBUG = "debug"
46
+ AGENT_RESEARCH = "research"
47
+
48
+ # Agents that require a feature directory
49
+ AGENTS_REQUIRE_FEATURE = (AGENT_IMPLEMENT, AGENT_CHECK, AGENT_DEBUG)
50
+ # All supported agents
51
+ AGENTS_ALL = (AGENT_IMPLEMENT, AGENT_CHECK, AGENT_DEBUG, AGENT_RESEARCH)
52
+
53
+
54
+ def find_repo_root(start_path: str) -> str | None:
55
+ """
56
+ Find git repo root from start_path upwards
57
+
58
+ Returns:
59
+ Repo root path, or None if not found
60
+ """
61
+ current = Path(start_path).resolve()
62
+ while current != current.parent:
63
+ if (current / ".git").exists():
64
+ return str(current)
65
+ current = current.parent
66
+ return None
67
+
68
+
69
+ def get_current_feature(repo_root: str) -> str | None:
70
+ """
71
+ Read current feature directory path from .trellis/.current-feature
72
+
73
+ Returns:
74
+ Feature directory relative path (relative to repo_root)
75
+ None if not set
76
+ """
77
+ current_feature_file = os.path.join(repo_root, DIR_WORKFLOW, FILE_CURRENT_FEATURE)
78
+ if not os.path.exists(current_feature_file):
79
+ return None
80
+
81
+ try:
82
+ with open(current_feature_file, "r", encoding="utf-8") as f:
83
+ content = f.read().strip()
84
+ return content if content else None
85
+ except Exception:
86
+ return None
87
+
88
+
89
+ def read_file_content(base_path: str, file_path: str) -> str | None:
90
+ """Read file content, return None if file doesn't exist"""
91
+ full_path = os.path.join(base_path, file_path)
92
+ if os.path.exists(full_path) and os.path.isfile(full_path):
93
+ try:
94
+ with open(full_path, "r", encoding="utf-8") as f:
95
+ return f.read()
96
+ except Exception:
97
+ return None
98
+ return None
99
+
100
+
101
+ def read_directory_contents(
102
+ base_path: str, dir_path: str, max_files: int = 20
103
+ ) -> list[tuple[str, str]]:
104
+ """
105
+ Read all .md files in a directory
106
+
107
+ Args:
108
+ base_path: Base path (usually repo_root)
109
+ dir_path: Directory relative path
110
+ max_files: Max files to read (prevent huge directories)
111
+
112
+ Returns:
113
+ [(file_path, content), ...]
114
+ """
115
+ full_path = os.path.join(base_path, dir_path)
116
+ if not os.path.exists(full_path) or not os.path.isdir(full_path):
117
+ return []
118
+
119
+ results = []
120
+ try:
121
+ # Only read .md files, sorted by filename
122
+ md_files = sorted(
123
+ [
124
+ f
125
+ for f in os.listdir(full_path)
126
+ if f.endswith(".md") and os.path.isfile(os.path.join(full_path, f))
127
+ ]
128
+ )
129
+
130
+ for filename in md_files[:max_files]:
131
+ file_full_path = os.path.join(full_path, filename)
132
+ relative_path = os.path.join(dir_path, filename)
133
+ try:
134
+ with open(file_full_path, "r", encoding="utf-8") as f:
135
+ content = f.read()
136
+ results.append((relative_path, content))
137
+ except Exception:
138
+ continue
139
+ except Exception:
140
+ pass
141
+
142
+ return results
143
+
144
+
145
+ def read_jsonl_entries(base_path: str, jsonl_path: str) -> list[tuple[str, str]]:
146
+ """
147
+ Read all file/directory contents referenced in jsonl file
148
+
149
+ Schema:
150
+ {"file": "path/to/file.md", "reason": "..."}
151
+ {"file": "path/to/dir/", "type": "directory", "reason": "..."}
152
+
153
+ Returns:
154
+ [(path, content), ...]
155
+ """
156
+ full_path = os.path.join(base_path, jsonl_path)
157
+ if not os.path.exists(full_path):
158
+ return []
159
+
160
+ results = []
161
+ try:
162
+ with open(full_path, "r", encoding="utf-8") as f:
163
+ for line in f:
164
+ line = line.strip()
165
+ if not line:
166
+ continue
167
+ try:
168
+ item = json.loads(line)
169
+ file_path = item.get("file") or item.get("path")
170
+ entry_type = item.get("type", "file")
171
+
172
+ if not file_path:
173
+ continue
174
+
175
+ if entry_type == "directory":
176
+ # Read all .md files in directory
177
+ dir_contents = read_directory_contents(base_path, file_path)
178
+ results.extend(dir_contents)
179
+ else:
180
+ # Read single file
181
+ content = read_file_content(base_path, file_path)
182
+ if content:
183
+ results.append((file_path, content))
184
+ except json.JSONDecodeError:
185
+ continue
186
+ except Exception:
187
+ pass
188
+
189
+ return results
190
+
191
+
192
+ def get_agent_context(repo_root: str, feature_dir: str, agent_type: str) -> str:
193
+ """
194
+ Get complete context for specified agent
195
+
196
+ Prioritize agent-specific jsonl, fallback to spec.jsonl if not exists
197
+ """
198
+ context_parts = []
199
+
200
+ # 1. Try agent-specific jsonl
201
+ agent_jsonl = f"{feature_dir}/{agent_type}.jsonl"
202
+ agent_entries = read_jsonl_entries(repo_root, agent_jsonl)
203
+
204
+ # 2. If agent-specific jsonl doesn't exist or empty, fallback to spec.jsonl
205
+ if not agent_entries:
206
+ agent_entries = read_jsonl_entries(repo_root, f"{feature_dir}/spec.jsonl")
207
+
208
+ # 3. Add all files from jsonl
209
+ for file_path, content in agent_entries:
210
+ context_parts.append(f"=== {file_path} ===\n{content}")
211
+
212
+ return "\n\n".join(context_parts)
213
+
214
+
215
+ def get_implement_context(repo_root: str, feature_dir: str) -> str:
216
+ """
217
+ Complete context for Implement Agent
218
+
219
+ Read order:
220
+ 1. All files in implement.jsonl (dev specs)
221
+ 2. prd.md (requirements)
222
+ 3. info.md (technical design)
223
+ """
224
+ context_parts = []
225
+
226
+ # 1. Read implement.jsonl (or fallback to spec.jsonl)
227
+ base_context = get_agent_context(repo_root, feature_dir, "implement")
228
+ if base_context:
229
+ context_parts.append(base_context)
230
+
231
+ # 2. Requirements document
232
+ prd_content = read_file_content(repo_root, f"{feature_dir}/prd.md")
233
+ if prd_content:
234
+ context_parts.append(
235
+ f"=== {feature_dir}/prd.md (Requirements) ===\n{prd_content}"
236
+ )
237
+
238
+ # 3. Technical design
239
+ info_content = read_file_content(repo_root, f"{feature_dir}/info.md")
240
+ if info_content:
241
+ context_parts.append(
242
+ f"=== {feature_dir}/info.md (Technical Design) ===\n{info_content}"
243
+ )
244
+
245
+ return "\n\n".join(context_parts)
246
+
247
+
248
+ def get_check_context(repo_root: str, feature_dir: str) -> str:
249
+ """
250
+ Complete context for Check Agent
251
+
252
+ Read order:
253
+ 1. All files in check.jsonl (check specs + dev specs)
254
+ 2. prd.md (for understanding feature intent)
255
+ """
256
+ context_parts = []
257
+
258
+ # 1. Read check.jsonl (or fallback to spec.jsonl + hardcoded check files)
259
+ check_entries = read_jsonl_entries(repo_root, f"{feature_dir}/check.jsonl")
260
+
261
+ if check_entries:
262
+ for file_path, content in check_entries:
263
+ context_parts.append(f"=== {file_path} ===\n{content}")
264
+ else:
265
+ # Fallback: use hardcoded check files + spec.jsonl
266
+ check_files = [
267
+ (".claude/commands/finish-work.md", "Finish work checklist"),
268
+ (".claude/commands/check-cross-layer.md", "Cross-layer check spec"),
269
+ (".claude/commands/check-backend.md", "Backend check spec"),
270
+ (".claude/commands/check-frontend.md", "Frontend check spec"),
271
+ ]
272
+ for file_path, description in check_files:
273
+ content = read_file_content(repo_root, file_path)
274
+ if content:
275
+ context_parts.append(f"=== {file_path} ({description}) ===\n{content}")
276
+
277
+ # Add spec.jsonl
278
+ spec_entries = read_jsonl_entries(repo_root, f"{feature_dir}/spec.jsonl")
279
+ for file_path, content in spec_entries:
280
+ context_parts.append(f"=== {file_path} (Dev spec) ===\n{content}")
281
+
282
+ # 2. Requirements document (for understanding feature intent)
283
+ prd_content = read_file_content(repo_root, f"{feature_dir}/prd.md")
284
+ if prd_content:
285
+ context_parts.append(
286
+ f"=== {feature_dir}/prd.md (Requirements - for understanding intent) ===\n{prd_content}"
287
+ )
288
+
289
+ return "\n\n".join(context_parts)
290
+
291
+
292
+ def get_debug_context(repo_root: str, feature_dir: str) -> str:
293
+ """
294
+ Complete context for Debug Agent
295
+
296
+ Read order:
297
+ 1. All files in debug.jsonl (specs needed for fixing)
298
+ 2. codex-review-output.txt (Codex Review results)
299
+ """
300
+ context_parts = []
301
+
302
+ # 1. Read debug.jsonl (or fallback to spec.jsonl + hardcoded check files)
303
+ debug_entries = read_jsonl_entries(repo_root, f"{feature_dir}/debug.jsonl")
304
+
305
+ if debug_entries:
306
+ for file_path, content in debug_entries:
307
+ context_parts.append(f"=== {file_path} ===\n{content}")
308
+ else:
309
+ # Fallback: use spec.jsonl + hardcoded check files
310
+ spec_entries = read_jsonl_entries(repo_root, f"{feature_dir}/spec.jsonl")
311
+ for file_path, content in spec_entries:
312
+ context_parts.append(f"=== {file_path} (Dev spec) ===\n{content}")
313
+
314
+ check_files = [
315
+ (".claude/commands/check-backend.md", "Backend check spec"),
316
+ (".claude/commands/check-frontend.md", "Frontend check spec"),
317
+ (".claude/commands/check-cross-layer.md", "Cross-layer check spec"),
318
+ ]
319
+ for file_path, description in check_files:
320
+ content = read_file_content(repo_root, file_path)
321
+ if content:
322
+ context_parts.append(f"=== {file_path} ({description}) ===\n{content}")
323
+
324
+ # 2. Codex review output (if exists)
325
+ codex_output = read_file_content(
326
+ repo_root, f"{feature_dir}/codex-review-output.txt"
327
+ )
328
+ if codex_output:
329
+ context_parts.append(
330
+ f"=== {feature_dir}/codex-review-output.txt (Codex Review Results) ===\n{codex_output}"
331
+ )
332
+
333
+ return "\n\n".join(context_parts)
334
+
335
+
336
+ def build_implement_prompt(original_prompt: str, context: str) -> str:
337
+ """Build complete prompt for Implement"""
338
+ return f"""# Implement Agent Task
339
+
340
+ You are the Implement Agent in the Multi-Agent Pipeline.
341
+
342
+ ## Your Context
343
+
344
+ All the information you need has been prepared for you:
345
+
346
+ {context}
347
+
348
+ ---
349
+
350
+ ## Your Task
351
+
352
+ {original_prompt}
353
+
354
+ ---
355
+
356
+ ## Workflow
357
+
358
+ 1. **Understand specs** - All dev specs are injected above, understand them
359
+ 2. **Understand requirements** - Read requirements document and technical design
360
+ 3. **Implement feature** - Implement following specs and design
361
+ 4. **Self-check** - Ensure code quality against check specs
362
+
363
+ ## Important Constraints
364
+
365
+ - Do NOT execute git commit, only code modifications
366
+ - Follow all dev specs injected above
367
+ - Report list of modified/created files when done"""
368
+
369
+
370
+ def build_check_prompt(original_prompt: str, context: str) -> str:
371
+ """Build complete prompt for Check"""
372
+ return f"""# Check Agent Task
373
+
374
+ You are the Check Agent in the Multi-Agent Pipeline (code and cross-layer checker).
375
+
376
+ ## Your Context
377
+
378
+ All check specs and dev specs you need:
379
+
380
+ {context}
381
+
382
+ ---
383
+
384
+ ## Your Task
385
+
386
+ {original_prompt}
387
+
388
+ ---
389
+
390
+ ## Workflow
391
+
392
+ 1. **Get changes** - Run `git diff --name-only` and `git diff` to get code changes
393
+ 2. **Check against specs** - Check item by item against specs above
394
+ 3. **Self-fix** - Fix issues directly, don't just report
395
+ 4. **Run verification** - Reference .husky/pre-commit for typecheck and lint
396
+
397
+ ## Important Constraints
398
+
399
+ - Fix issues yourself, don't just report
400
+ - Must execute complete checklist in finish-work.md
401
+ - Pay special attention to impact radius analysis (L1-L5)"""
402
+
403
+
404
+ def build_debug_prompt(original_prompt: str, context: str) -> str:
405
+ """Build complete prompt for Debug"""
406
+ return f"""# Debug Agent Task
407
+
408
+ You are the Debug Agent in the Multi-Agent Pipeline (issue fixer).
409
+
410
+ ## Your Context
411
+
412
+ Dev specs and Codex Review results:
413
+
414
+ {context}
415
+
416
+ ---
417
+
418
+ ## Your Task
419
+
420
+ {original_prompt}
421
+
422
+ ---
423
+
424
+ ## Workflow
425
+
426
+ 1. **Understand issues** - Analyze issues pointed out in Codex Review
427
+ 2. **Locate code** - Find positions that need fixing
428
+ 3. **Fix against specs** - Fix issues following dev specs
429
+ 4. **Verify fixes** - Run typecheck to ensure no new issues
430
+
431
+ ## Important Constraints
432
+
433
+ - Do NOT execute git commit, only code modifications
434
+ - Run typecheck after each fix to verify
435
+ - Report which issues were fixed and which files were modified"""
436
+
437
+
438
+ def get_research_context(repo_root: str, feature_dir: str | None) -> str:
439
+ """
440
+ Context for Research Agent
441
+
442
+ Research doesn't need much preset context, only needs:
443
+ 1. Project structure overview (where spec directories are)
444
+ 2. Optional research.jsonl (if there are specific search needs)
445
+ """
446
+ context_parts = []
447
+
448
+ # 1. Project structure overview (uses constants for paths)
449
+ structure_path = f"{DIR_WORKFLOW}/{DIR_STRUCTURE}"
450
+ project_structure = f"""## Project Spec Directory Structure
451
+
452
+ ```
453
+ {structure_path}/
454
+ ├── shared/ # Cross-project common specs (TypeScript, code quality, git)
455
+ ├── frontend/ # Frontend standards
456
+ ├── backend/ # Backend standards
457
+ └── guides/ # Thinking guides (cross-layer, code reuse, etc.)
458
+
459
+ {DIR_WORKFLOW}/big-question/ # Known issues and pitfalls
460
+ ```
461
+
462
+ ## Search Tips
463
+
464
+ - Spec files: `{structure_path}/**/*.md`
465
+ - Known issues: `{DIR_WORKFLOW}/big-question/`
466
+ - Code search: Use Glob and Grep tools
467
+ - Tech solutions: Use mcp__exa__web_search_exa or mcp__exa__get_code_context_exa"""
468
+
469
+ context_parts.append(project_structure)
470
+
471
+ # 2. If feature directory exists, try reading research.jsonl (optional)
472
+ if feature_dir:
473
+ research_entries = read_jsonl_entries(
474
+ repo_root, f"{feature_dir}/research.jsonl"
475
+ )
476
+ if research_entries:
477
+ context_parts.append(
478
+ "\n## Additional Search Context (from research.jsonl)\n"
479
+ )
480
+ for file_path, content in research_entries:
481
+ context_parts.append(f"=== {file_path} ===\n{content}")
482
+
483
+ return "\n\n".join(context_parts)
484
+
485
+
486
+ def build_research_prompt(original_prompt: str, context: str) -> str:
487
+ """Build complete prompt for Research"""
488
+ return f"""# Research Agent Task
489
+
490
+ You are the Research Agent in the Multi-Agent Pipeline (search researcher).
491
+
492
+ ## Core Principle
493
+
494
+ **You do one thing: find and explain information.**
495
+
496
+ You are a documenter, not a reviewer.
497
+
498
+ ## Project Info
499
+
500
+ {context}
501
+
502
+ ---
503
+
504
+ ## Your Task
505
+
506
+ {original_prompt}
507
+
508
+ ---
509
+
510
+ ## Workflow
511
+
512
+ 1. **Understand query** - Determine search type (internal/external) and scope
513
+ 2. **Plan search** - List search steps for complex queries
514
+ 3. **Execute search** - Execute multiple independent searches in parallel
515
+ 4. **Organize results** - Output structured report
516
+
517
+ ## Search Tools
518
+
519
+ | Tool | Purpose |
520
+ |------|---------|
521
+ | Glob | Search by filename pattern |
522
+ | Grep | Search by content |
523
+ | Read | Read file content |
524
+ | mcp__exa__web_search_exa | External web search |
525
+ | mcp__exa__get_code_context_exa | External code/doc search |
526
+
527
+ ## Strict Boundaries
528
+
529
+ **Only allowed**: Describe what exists, where it is, how it works
530
+
531
+ **Forbidden** (unless explicitly asked):
532
+ - Suggest improvements
533
+ - Criticize implementation
534
+ - Recommend refactoring
535
+ - Modify any files
536
+
537
+ ## Report Format
538
+
539
+ Provide structured search results including:
540
+ - List of files found (with paths)
541
+ - Code pattern analysis (if applicable)
542
+ - Related spec documents
543
+ - External references (if any)"""
544
+
545
+
546
+ def main():
547
+ try:
548
+ input_data = json.load(sys.stdin)
549
+ except json.JSONDecodeError:
550
+ sys.exit(0)
551
+
552
+ tool_name = input_data.get("tool_name", "")
553
+
554
+ if tool_name != "Task":
555
+ sys.exit(0)
556
+
557
+ tool_input = input_data.get("tool_input", {})
558
+ subagent_type = tool_input.get("subagent_type", "")
559
+ original_prompt = tool_input.get("prompt", "")
560
+ cwd = input_data.get("cwd", os.getcwd())
561
+
562
+ # Only handle subagent types we care about
563
+ if subagent_type not in AGENTS_ALL:
564
+ sys.exit(0)
565
+
566
+ # Find repo root
567
+ repo_root = find_repo_root(cwd)
568
+ if not repo_root:
569
+ sys.exit(0)
570
+
571
+ # Get current feature directory (research doesn't require it)
572
+ feature_dir = get_current_feature(repo_root)
573
+
574
+ # implement/check/debug need feature directory
575
+ if subagent_type in AGENTS_REQUIRE_FEATURE:
576
+ if not feature_dir:
577
+ sys.exit(0)
578
+ # Check if feature directory exists
579
+ feature_dir_full = os.path.join(repo_root, feature_dir)
580
+ if not os.path.exists(feature_dir_full):
581
+ sys.exit(0)
582
+
583
+ # Get context and build prompt based on subagent type
584
+ if subagent_type == AGENT_IMPLEMENT:
585
+ assert feature_dir is not None # validated above
586
+ context = get_implement_context(repo_root, feature_dir)
587
+ new_prompt = build_implement_prompt(original_prompt, context)
588
+ elif subagent_type == AGENT_CHECK:
589
+ assert feature_dir is not None # validated above
590
+ context = get_check_context(repo_root, feature_dir)
591
+ new_prompt = build_check_prompt(original_prompt, context)
592
+ elif subagent_type == AGENT_DEBUG:
593
+ assert feature_dir is not None # validated above
594
+ context = get_debug_context(repo_root, feature_dir)
595
+ new_prompt = build_debug_prompt(original_prompt, context)
596
+ elif subagent_type == AGENT_RESEARCH:
597
+ # Research can work without feature directory
598
+ context = get_research_context(repo_root, feature_dir)
599
+ new_prompt = build_research_prompt(original_prompt, context)
600
+ else:
601
+ sys.exit(0)
602
+
603
+ if not context:
604
+ sys.exit(0)
605
+
606
+ # Return updated input
607
+ output = {
608
+ "hookSpecificOutput": {
609
+ "hookEventName": "PreToolUse",
610
+ "permissionDecision": "allow",
611
+ "updatedInput": {**tool_input, "prompt": new_prompt},
612
+ }
613
+ }
614
+
615
+ print(json.dumps(output, ensure_ascii=False))
616
+ sys.exit(0)
617
+
618
+
619
+ if __name__ == "__main__":
620
+ main()
@@ -0,0 +1,16 @@
1
+ {
2
+ "hooks": {
3
+ "PreToolUse": [
4
+ {
5
+ "matcher": "Task",
6
+ "hooks": [
7
+ {
8
+ "type": "command",
9
+ "command": "python3 \"$CLAUDE_PROJECT_DIR/.claude/hooks/inject-subagent-context.py\"",
10
+ "timeout": 30
11
+ }
12
+ ]
13
+ }
14
+ ]
15
+ }
16
+ }