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,1118 @@
1
+ #!/bin/bash
2
+ # Task Management Script for Multi-Agent Pipeline
3
+ #
4
+ # Usage:
5
+ # ./.trellis/scripts/task.sh create "<title>" [--slug <name>] [--assignee <dev>] [--priority P0|P1|P2|P3]
6
+ # ./.trellis/scripts/task.sh init-context <dir> <type> # Initialize jsonl files
7
+ # ./.trellis/scripts/task.sh add-context <dir> <file> <path> [reason] # Add jsonl entry
8
+ # ./.trellis/scripts/task.sh validate <dir> # Validate jsonl files
9
+ # ./.trellis/scripts/task.sh list-context <dir> # List jsonl entries
10
+ # ./.trellis/scripts/task.sh start <dir> # Set as current task
11
+ # ./.trellis/scripts/task.sh finish # Clear current task
12
+ # ./.trellis/scripts/task.sh set-branch <dir> <branch> # Set git branch
13
+ # ./.trellis/scripts/task.sh set-scope <dir> <scope> # Set scope for PR title
14
+ # ./.trellis/scripts/task.sh create-pr [dir] [--dry-run] # Create PR from task
15
+ # ./.trellis/scripts/task.sh archive <task-name> # Archive completed task
16
+ # ./.trellis/scripts/task.sh list # List active tasks
17
+ # ./.trellis/scripts/task.sh list-archive [month] # List archived tasks
18
+ #
19
+ # Task Directory Structure:
20
+ # tasks/
21
+ # ├── 01-21-my-task/
22
+ # │ ├── task.json # Metadata
23
+ # │ ├── prd.md # Requirements
24
+ # │ ├── info.md # Technical design (optional)
25
+ # │ ├── implement.jsonl # Implement agent context
26
+ # │ ├── check.jsonl # Check agent context
27
+ # │ └── debug.jsonl # Debug agent context
28
+ # └── archive/
29
+ # └── 2026-01/
30
+ # └── 01-21-old-task/
31
+
32
+ set -e
33
+
34
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
35
+ source "$SCRIPT_DIR/common/paths.sh"
36
+ source "$SCRIPT_DIR/common/developer.sh"
37
+ source "$SCRIPT_DIR/common/task-queue.sh"
38
+ source "$SCRIPT_DIR/common/task-utils.sh"
39
+
40
+ # Colors
41
+ RED='\033[0;31m'
42
+ GREEN='\033[0;32m'
43
+ YELLOW='\033[1;33m'
44
+ BLUE='\033[0;34m'
45
+ CYAN='\033[0;36m'
46
+ NC='\033[0m'
47
+
48
+ REPO_ROOT=$(get_repo_root)
49
+
50
+ # =============================================================================
51
+ # Helper Functions
52
+ # =============================================================================
53
+
54
+ # Convert title to slug (only works with ASCII)
55
+ _slugify() {
56
+ local result=$(echo "$1" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g' | sed 's/--*/-/g' | sed 's/^-//' | sed 's/-$//')
57
+ echo "$result"
58
+ }
59
+
60
+ # =============================================================================
61
+ # jsonl Default Content Generators
62
+ # =============================================================================
63
+
64
+ get_implement_base() {
65
+ cat << EOF
66
+ {"file": "$DIR_WORKFLOW/workflow.md", "reason": "Project workflow and conventions"}
67
+ {"file": "$DIR_WORKFLOW/$DIR_SPEC/shared/index.md", "reason": "Shared coding standards"}
68
+ EOF
69
+ }
70
+
71
+ get_implement_backend() {
72
+ cat << EOF
73
+ {"file": "$DIR_WORKFLOW/$DIR_SPEC/backend/index.md", "reason": "Backend development guide"}
74
+ {"file": "$DIR_WORKFLOW/$DIR_SPEC/backend/api-module.md", "reason": "API module conventions"}
75
+ {"file": "$DIR_WORKFLOW/$DIR_SPEC/backend/quality.md", "reason": "Code quality requirements"}
76
+ EOF
77
+ }
78
+
79
+ get_implement_frontend() {
80
+ cat << EOF
81
+ {"file": "$DIR_WORKFLOW/$DIR_SPEC/frontend/index.md", "reason": "Frontend development guide"}
82
+ {"file": "$DIR_WORKFLOW/$DIR_SPEC/frontend/components.md", "reason": "Component conventions"}
83
+ EOF
84
+ }
85
+
86
+ get_check_context() {
87
+ local dev_type="$1"
88
+
89
+ cat << EOF
90
+ {"file": ".claude/commands/trellis/finish-work.md", "reason": "Finish work checklist"}
91
+ {"file": "$DIR_WORKFLOW/$DIR_SPEC/shared/index.md", "reason": "Shared coding standards"}
92
+ EOF
93
+
94
+ if [[ "$dev_type" == "backend" ]] || [[ "$dev_type" == "fullstack" ]]; then
95
+ echo '{"file": ".claude/commands/trellis/check-backend.md", "reason": "Backend check spec"}'
96
+ fi
97
+ if [[ "$dev_type" == "frontend" ]] || [[ "$dev_type" == "fullstack" ]]; then
98
+ echo '{"file": ".claude/commands/trellis/check-frontend.md", "reason": "Frontend check spec"}'
99
+ fi
100
+ }
101
+
102
+ get_debug_context() {
103
+ local dev_type="$1"
104
+
105
+ echo "{\"file\": \"$DIR_WORKFLOW/$DIR_SPEC/shared/index.md\", \"reason\": \"Shared coding standards\"}"
106
+
107
+ if [[ "$dev_type" == "backend" ]] || [[ "$dev_type" == "fullstack" ]]; then
108
+ echo '{"file": ".claude/commands/trellis/check-backend.md", "reason": "Backend check spec"}'
109
+ fi
110
+ if [[ "$dev_type" == "frontend" ]] || [[ "$dev_type" == "fullstack" ]]; then
111
+ echo '{"file": ".claude/commands/trellis/check-frontend.md", "reason": "Frontend check spec"}'
112
+ fi
113
+ }
114
+
115
+ # =============================================================================
116
+ # Task Operations
117
+ # =============================================================================
118
+
119
+ ensure_tasks_dir() {
120
+ local tasks_dir=$(get_tasks_dir)
121
+ local archive_dir="$tasks_dir/archive"
122
+
123
+ if [[ ! -d "$tasks_dir" ]]; then
124
+ mkdir -p "$tasks_dir"
125
+ echo -e "${GREEN}Created tasks directory: $tasks_dir${NC}" >&2
126
+ fi
127
+
128
+ if [[ ! -d "$archive_dir" ]]; then
129
+ mkdir -p "$archive_dir"
130
+ fi
131
+ }
132
+
133
+ # =============================================================================
134
+ # Command: create
135
+ # =============================================================================
136
+
137
+ cmd_create() {
138
+ local title=""
139
+ local assignee=""
140
+ local priority="P2"
141
+ local slug=""
142
+ local description=""
143
+
144
+ # Parse arguments
145
+ while [[ $# -gt 0 ]]; do
146
+ case "$1" in
147
+ --assignee|-a)
148
+ assignee="$2"
149
+ shift 2
150
+ ;;
151
+ --priority|-p)
152
+ priority="$2"
153
+ shift 2
154
+ ;;
155
+ --slug|-s)
156
+ slug="$2"
157
+ shift 2
158
+ ;;
159
+ --description|-d)
160
+ description="$2"
161
+ shift 2
162
+ ;;
163
+ -*)
164
+ echo -e "${RED}Error: Unknown option $1${NC}" >&2
165
+ exit 1
166
+ ;;
167
+ *)
168
+ if [[ -z "$title" ]]; then
169
+ title="$1"
170
+ fi
171
+ shift
172
+ ;;
173
+ esac
174
+ done
175
+
176
+ # Validate required fields
177
+ if [[ -z "$title" ]]; then
178
+ echo -e "${RED}Error: title is required${NC}" >&2
179
+ echo "Usage: $0 create <title> [--assignee <dev>] [--priority P0|P1|P2|P3] [--slug <slug>]" >&2
180
+ exit 1
181
+ fi
182
+
183
+ # Default assignee to current developer
184
+ if [[ -z "$assignee" ]]; then
185
+ assignee=$(get_developer "$REPO_ROOT")
186
+ if [[ -z "$assignee" ]]; then
187
+ echo -e "${RED}Error: No developer set. Run init-developer.sh first or use --assignee${NC}" >&2
188
+ exit 1
189
+ fi
190
+ fi
191
+
192
+ ensure_tasks_dir
193
+
194
+ # Get current developer as creator
195
+ local creator=$(get_developer "$REPO_ROOT")
196
+ if [[ -z "$creator" ]]; then
197
+ creator="$assignee"
198
+ fi
199
+
200
+ # Generate slug if not provided
201
+ if [[ -z "$slug" ]]; then
202
+ slug=$(_slugify "$title")
203
+ fi
204
+
205
+ # Validate slug
206
+ if [[ -z "$slug" ]]; then
207
+ echo -e "${RED}Error: could not generate slug from title${NC}" >&2
208
+ exit 1
209
+ fi
210
+
211
+ # Create task directory with MM-DD-slug format
212
+ local tasks_dir=$(get_tasks_dir)
213
+ local date_prefix=$(generate_task_date_prefix)
214
+ local dir_name="${date_prefix}-${slug}"
215
+ local task_dir="$tasks_dir/$dir_name"
216
+ local task_json="$task_dir/$FILE_TASK_JSON"
217
+
218
+ if [[ -d "$task_dir" ]]; then
219
+ echo -e "${YELLOW}Warning: Task directory already exists: $dir_name${NC}" >&2
220
+ else
221
+ mkdir -p "$task_dir"
222
+ fi
223
+
224
+ local today=$(date +%Y-%m-%d)
225
+
226
+ cat > "$task_json" << EOF
227
+ {
228
+ "id": "$slug",
229
+ "name": "$slug",
230
+ "title": "$title",
231
+ "description": "$description",
232
+ "status": "planning",
233
+ "dev_type": null,
234
+ "scope": null,
235
+ "priority": "$priority",
236
+ "creator": "$creator",
237
+ "assignee": "$assignee",
238
+ "createdAt": "$today",
239
+ "completedAt": null,
240
+ "branch": null,
241
+ "base_branch": null,
242
+ "worktree_path": null,
243
+ "current_phase": 0,
244
+ "next_action": [
245
+ {"phase": 1, "action": "implement"},
246
+ {"phase": 2, "action": "check"},
247
+ {"phase": 3, "action": "finish"},
248
+ {"phase": 4, "action": "create-pr"}
249
+ ],
250
+ "commit": null,
251
+ "pr_url": null,
252
+ "subtasks": [],
253
+ "relatedFiles": [],
254
+ "notes": ""
255
+ }
256
+ EOF
257
+
258
+ echo -e "${GREEN}Created task: $dir_name${NC}" >&2
259
+ echo -e "" >&2
260
+ echo -e "${BLUE}Next steps:${NC}" >&2
261
+ echo -e " 1. Create prd.md with requirements" >&2
262
+ echo -e " 2. Run: $0 init-context <dir> <dev_type>" >&2
263
+ echo -e " 3. Run: $0 start <dir>" >&2
264
+ echo "" >&2
265
+
266
+ # Output relative path for script chaining
267
+ echo "$DIR_WORKFLOW/$DIR_TASKS/$dir_name"
268
+ }
269
+
270
+ # =============================================================================
271
+ # Command: init-context
272
+ # =============================================================================
273
+
274
+ cmd_init_context() {
275
+ local target_dir="$1"
276
+ local dev_type="$2"
277
+
278
+ if [[ -z "$target_dir" ]] || [[ -z "$dev_type" ]]; then
279
+ echo -e "${RED}Error: Missing arguments${NC}"
280
+ echo "Usage: $0 init-context <task-dir> <dev_type>"
281
+ echo " dev_type: backend | frontend | fullstack | test | docs"
282
+ exit 1
283
+ fi
284
+
285
+ # Support relative paths
286
+ if [[ ! "$target_dir" = /* ]]; then
287
+ target_dir="$REPO_ROOT/$target_dir"
288
+ fi
289
+
290
+ if [[ ! -d "$target_dir" ]]; then
291
+ echo -e "${RED}Error: Directory not found: $target_dir${NC}"
292
+ exit 1
293
+ fi
294
+
295
+ echo -e "${BLUE}=== Initializing Agent Context Files ===${NC}"
296
+ echo -e "Target dir: $target_dir"
297
+ echo -e "Dev type: $dev_type"
298
+ echo ""
299
+
300
+ # implement.jsonl
301
+ echo -e "${CYAN}Creating implement.jsonl...${NC}"
302
+ local implement_file="$target_dir/implement.jsonl"
303
+ {
304
+ get_implement_base
305
+ case "$dev_type" in
306
+ backend|test) get_implement_backend ;;
307
+ frontend) get_implement_frontend ;;
308
+ fullstack)
309
+ get_implement_backend
310
+ get_implement_frontend
311
+ ;;
312
+ esac
313
+ } > "$implement_file"
314
+ echo -e " ${GREEN}✓${NC} $(wc -l < "$implement_file" | tr -d ' ') entries"
315
+
316
+ # check.jsonl
317
+ echo -e "${CYAN}Creating check.jsonl...${NC}"
318
+ local check_file="$target_dir/check.jsonl"
319
+ get_check_context "$dev_type" > "$check_file"
320
+ echo -e " ${GREEN}✓${NC} $(wc -l < "$check_file" | tr -d ' ') entries"
321
+
322
+ # debug.jsonl
323
+ echo -e "${CYAN}Creating debug.jsonl...${NC}"
324
+ local debug_file="$target_dir/debug.jsonl"
325
+ get_debug_context "$dev_type" > "$debug_file"
326
+ echo -e " ${GREEN}✓${NC} $(wc -l < "$debug_file" | tr -d ' ') entries"
327
+
328
+ echo ""
329
+ echo -e "${GREEN}✓ All context files created${NC}"
330
+ echo -e ""
331
+ echo -e "${BLUE}Next steps:${NC}"
332
+ echo -e " 1. Add task-specific specs: $0 add-context <dir> <jsonl> <path>"
333
+ echo -e " 2. Set as current: $0 start <dir>"
334
+ }
335
+
336
+ # =============================================================================
337
+ # Command: add-context
338
+ # =============================================================================
339
+
340
+ cmd_add_context() {
341
+ local target_dir="$1"
342
+ local jsonl_name="$2"
343
+ local path="$3"
344
+ local reason="${4:-Added manually}"
345
+
346
+ if [[ -z "$target_dir" ]] || [[ -z "$jsonl_name" ]] || [[ -z "$path" ]]; then
347
+ echo -e "${RED}Error: Missing arguments${NC}"
348
+ echo "Usage: $0 add-context <task-dir> <jsonl-file> <path> [reason]"
349
+ echo " jsonl-file: implement | check | debug (or full filename)"
350
+ exit 1
351
+ fi
352
+
353
+ # Support relative paths
354
+ if [[ ! "$target_dir" = /* ]]; then
355
+ target_dir="$REPO_ROOT/$target_dir"
356
+ fi
357
+
358
+ # Support shorthand
359
+ if [[ "$jsonl_name" != *.jsonl ]]; then
360
+ jsonl_name="${jsonl_name}.jsonl"
361
+ fi
362
+
363
+ local jsonl_file="$target_dir/$jsonl_name"
364
+ local full_path="$REPO_ROOT/$path"
365
+ local entry_type="file"
366
+
367
+ if [[ -d "$full_path" ]]; then
368
+ entry_type="directory"
369
+ [[ "$path" != */ ]] && path="$path/"
370
+ elif [[ ! -f "$full_path" ]]; then
371
+ echo -e "${RED}Error: Path not found: $path${NC}"
372
+ exit 1
373
+ fi
374
+
375
+ # Check if already exists
376
+ if [[ -f "$jsonl_file" ]] && grep -q "\"$path\"" "$jsonl_file" 2>/dev/null; then
377
+ echo -e "${YELLOW}Warning: Entry already exists for $path${NC}"
378
+ exit 0
379
+ fi
380
+
381
+ # Add entry
382
+ if [[ "$entry_type" == "directory" ]]; then
383
+ echo "{\"file\": \"$path\", \"type\": \"directory\", \"reason\": \"$reason\"}" >> "$jsonl_file"
384
+ else
385
+ echo "{\"file\": \"$path\", \"reason\": \"$reason\"}" >> "$jsonl_file"
386
+ fi
387
+
388
+ echo -e "${GREEN}Added $entry_type: $path${NC}"
389
+ }
390
+
391
+ # =============================================================================
392
+ # Command: validate
393
+ # =============================================================================
394
+
395
+ validate_jsonl() {
396
+ local jsonl_file="$1"
397
+ local file_name=$(basename "$jsonl_file")
398
+ local errors=0
399
+ local line_num=0
400
+
401
+ if [[ ! -f "$jsonl_file" ]]; then
402
+ echo -e " ${YELLOW}$file_name: not found (skipped)${NC}"
403
+ return 0
404
+ fi
405
+
406
+ while IFS= read -r line || [[ -n "$line" ]]; do
407
+ line_num=$((line_num + 1))
408
+ [[ -z "$line" ]] && continue
409
+
410
+ if ! echo "$line" | jq -e . > /dev/null 2>&1; then
411
+ echo -e " ${RED}$file_name:$line_num: Invalid JSON${NC}"
412
+ errors=$((errors + 1))
413
+ continue
414
+ fi
415
+
416
+ local file_path=$(echo "$line" | jq -r '.file // empty')
417
+ local entry_type=$(echo "$line" | jq -r '.type // "file"')
418
+
419
+ if [[ -z "$file_path" ]]; then
420
+ echo -e " ${RED}$file_name:$line_num: Missing 'file' field${NC}"
421
+ errors=$((errors + 1))
422
+ continue
423
+ fi
424
+
425
+ local full_path="$REPO_ROOT/$file_path"
426
+ if [[ "$entry_type" == "directory" ]]; then
427
+ if [[ ! -d "$full_path" ]]; then
428
+ echo -e " ${RED}$file_name:$line_num: Directory not found: $file_path${NC}"
429
+ errors=$((errors + 1))
430
+ fi
431
+ else
432
+ if [[ ! -f "$full_path" ]]; then
433
+ echo -e " ${RED}$file_name:$line_num: File not found: $file_path${NC}"
434
+ errors=$((errors + 1))
435
+ fi
436
+ fi
437
+ done < "$jsonl_file"
438
+
439
+ if [[ $errors -eq 0 ]]; then
440
+ echo -e " ${GREEN}$file_name: ✓ ($line_num entries)${NC}"
441
+ else
442
+ echo -e " ${RED}$file_name: ✗ ($errors errors)${NC}"
443
+ fi
444
+
445
+ return $errors
446
+ }
447
+
448
+ cmd_validate() {
449
+ local target_dir="$1"
450
+
451
+ if [[ -z "$target_dir" ]]; then
452
+ echo -e "${RED}Error: task directory required${NC}"
453
+ exit 1
454
+ fi
455
+
456
+ if [[ ! "$target_dir" = /* ]]; then
457
+ target_dir="$REPO_ROOT/$target_dir"
458
+ fi
459
+
460
+ echo -e "${BLUE}=== Validating Context Files ===${NC}"
461
+ echo -e "Target dir: $target_dir"
462
+ echo ""
463
+
464
+ local total_errors=0
465
+ for jsonl_file in "$target_dir"/{implement,check,debug}.jsonl; do
466
+ validate_jsonl "$jsonl_file"
467
+ total_errors=$((total_errors + $?))
468
+ done
469
+
470
+ echo ""
471
+ if [[ $total_errors -eq 0 ]]; then
472
+ echo -e "${GREEN}✓ All validations passed${NC}"
473
+ else
474
+ echo -e "${RED}✗ Validation failed ($total_errors errors)${NC}"
475
+ exit 1
476
+ fi
477
+ }
478
+
479
+ # =============================================================================
480
+ # Command: list-context
481
+ # =============================================================================
482
+
483
+ cmd_list_context() {
484
+ local target_dir="$1"
485
+
486
+ if [[ -z "$target_dir" ]]; then
487
+ echo -e "${RED}Error: task directory required${NC}"
488
+ exit 1
489
+ fi
490
+
491
+ if [[ ! "$target_dir" = /* ]]; then
492
+ target_dir="$REPO_ROOT/$target_dir"
493
+ fi
494
+
495
+ echo -e "${BLUE}=== Context Files ===${NC}"
496
+ echo ""
497
+
498
+ for jsonl_file in "$target_dir"/{implement,check,debug}.jsonl; do
499
+ local file_name=$(basename "$jsonl_file")
500
+ [[ ! -f "$jsonl_file" ]] && continue
501
+
502
+ echo -e "${CYAN}[$file_name]${NC}"
503
+
504
+ local count=0
505
+ while IFS= read -r line || [[ -n "$line" ]]; do
506
+ [[ -z "$line" ]] && continue
507
+
508
+ local file_path=$(echo "$line" | jq -r '.file // "?"')
509
+ local entry_type=$(echo "$line" | jq -r '.type // "file"')
510
+ local reason=$(echo "$line" | jq -r '.reason // "-"')
511
+ count=$((count + 1))
512
+
513
+ if [[ "$entry_type" == "directory" ]]; then
514
+ echo -e " ${GREEN}$count.${NC} [DIR] $file_path"
515
+ else
516
+ echo -e " ${GREEN}$count.${NC} $file_path"
517
+ fi
518
+ echo -e " ${YELLOW}→${NC} $reason"
519
+ done < "$jsonl_file"
520
+
521
+ echo ""
522
+ done
523
+ }
524
+
525
+ # =============================================================================
526
+ # Command: start / finish
527
+ # =============================================================================
528
+
529
+ cmd_start() {
530
+ local task_dir="$1"
531
+
532
+ if [[ -z "$task_dir" ]]; then
533
+ echo -e "${RED}Error: task directory required${NC}"
534
+ exit 1
535
+ fi
536
+
537
+ # Convert to relative path
538
+ if [[ "$task_dir" = /* ]]; then
539
+ task_dir="${task_dir#$REPO_ROOT/}"
540
+ fi
541
+
542
+ # Verify directory exists
543
+ if [[ ! -d "$REPO_ROOT/$task_dir" ]]; then
544
+ echo -e "${RED}Error: Task directory not found: $task_dir${NC}"
545
+ exit 1
546
+ fi
547
+
548
+ set_current_task "$task_dir"
549
+ echo -e "${GREEN}✓ Current task set to: $task_dir${NC}"
550
+ echo ""
551
+ echo -e "${BLUE}The hook will now inject context from this task's jsonl files.${NC}"
552
+ }
553
+
554
+ cmd_finish() {
555
+ local current=$(get_current_task)
556
+
557
+ if [[ -z "$current" ]]; then
558
+ echo -e "${YELLOW}No current task set${NC}"
559
+ exit 0
560
+ fi
561
+
562
+ clear_current_task
563
+ echo -e "${GREEN}✓ Cleared current task (was: $current)${NC}"
564
+ }
565
+
566
+ # =============================================================================
567
+ # Command: archive
568
+ # =============================================================================
569
+
570
+ cmd_archive() {
571
+ local task_name="$1"
572
+
573
+ if [[ -z "$task_name" ]]; then
574
+ echo -e "${RED}Error: Task name is required${NC}" >&2
575
+ echo "Usage: $0 archive <task-name>" >&2
576
+ exit 1
577
+ fi
578
+
579
+ local tasks_dir=$(get_tasks_dir)
580
+
581
+ # Find task directory using common function
582
+ local task_dir=$(find_task_by_name "$task_name" "$tasks_dir")
583
+
584
+ if [[ -z "$task_dir" ]] || [[ ! -d "$task_dir" ]]; then
585
+ echo -e "${RED}Error: Task not found: $task_name${NC}" >&2
586
+ echo "Active tasks:" >&2
587
+ cmd_list >&2
588
+ exit 1
589
+ fi
590
+
591
+ local dir_name=$(basename "$task_dir")
592
+ local task_json="$task_dir/$FILE_TASK_JSON"
593
+
594
+ # Update status before archiving
595
+ local today=$(date +%Y-%m-%d)
596
+ if [[ -f "$task_json" ]] && command -v jq &> /dev/null; then
597
+ local temp_file=$(mktemp)
598
+ jq --arg date "$today" '.status = "completed" | .completedAt = $date' "$task_json" > "$temp_file"
599
+ mv "$temp_file" "$task_json"
600
+ fi
601
+
602
+ # Clear if current task
603
+ local current=$(get_current_task)
604
+ if [[ "$current" == *"$dir_name"* ]]; then
605
+ clear_current_task
606
+ fi
607
+
608
+ # Use common archive function
609
+ local result=$(archive_task_complete "$task_dir" "$REPO_ROOT")
610
+ local archive_dest=""
611
+
612
+ echo "$result" | while IFS= read -r line; do
613
+ case "$line" in
614
+ archived_to:*)
615
+ archive_dest="${line#archived_to:}"
616
+ local year_month=$(basename "$(dirname "$archive_dest")")
617
+ echo -e "${GREEN}Archived: $dir_name -> archive/$year_month/${NC}" >&2
618
+ ;;
619
+ esac
620
+ done
621
+
622
+ # Return the archive path
623
+ local year_month=$(date +%Y-%m)
624
+ echo "$DIR_WORKFLOW/$DIR_TASKS/$DIR_ARCHIVE/$year_month/$dir_name"
625
+ }
626
+
627
+ # =============================================================================
628
+ # Command: list
629
+ # =============================================================================
630
+
631
+ cmd_list() {
632
+ local filter_mine=false
633
+ local filter_status=""
634
+
635
+ # Parse arguments
636
+ while [[ $# -gt 0 ]]; do
637
+ case "$1" in
638
+ --mine|-m)
639
+ filter_mine=true
640
+ shift
641
+ ;;
642
+ --status|-s)
643
+ filter_status="$2"
644
+ shift 2
645
+ ;;
646
+ *)
647
+ shift
648
+ ;;
649
+ esac
650
+ done
651
+
652
+ local tasks_dir=$(get_tasks_dir)
653
+ local current_task=$(get_current_task)
654
+ local developer=$(get_developer "$REPO_ROOT")
655
+
656
+ if [[ "$filter_mine" == "true" ]]; then
657
+ if [[ -z "$developer" ]]; then
658
+ echo -e "${RED}Error: No developer set. Run init-developer.sh first${NC}" >&2
659
+ exit 1
660
+ fi
661
+ echo -e "${BLUE}My tasks (assignee: $developer):${NC}"
662
+ else
663
+ echo -e "${BLUE}All active tasks:${NC}"
664
+ fi
665
+ echo ""
666
+
667
+ local count=0
668
+
669
+ for d in "$tasks_dir"/*/; do
670
+ if [[ -d "$d" ]] && [[ "$(basename "$d")" != "archive" ]]; then
671
+ local dir_name=$(basename "$d")
672
+ local task_json="$d/$FILE_TASK_JSON"
673
+ local status="unknown"
674
+ local assignee="-"
675
+ local relative_path="$DIR_WORKFLOW/$DIR_TASKS/$dir_name"
676
+
677
+ if [[ -f "$task_json" ]] && command -v jq &> /dev/null; then
678
+ status=$(jq -r '.status // "unknown"' "$task_json")
679
+ assignee=$(jq -r '.assignee // "-"' "$task_json")
680
+ fi
681
+
682
+ # Apply --mine filter
683
+ if [[ "$filter_mine" == "true" ]] && [[ "$assignee" != "$developer" ]]; then
684
+ continue
685
+ fi
686
+
687
+ # Apply --status filter
688
+ if [[ -n "$filter_status" ]] && [[ "$status" != "$filter_status" ]]; then
689
+ continue
690
+ fi
691
+
692
+ local marker=""
693
+ if [[ "$relative_path" == "$current_task" ]]; then
694
+ marker=" ${GREEN}<- current${NC}"
695
+ fi
696
+
697
+ if [[ "$filter_mine" == "true" ]]; then
698
+ echo -e " - $dir_name/ ($status)$marker"
699
+ else
700
+ echo -e " - $dir_name/ ($status) [${CYAN}$assignee${NC}]$marker"
701
+ fi
702
+ ((count++))
703
+ fi
704
+ done
705
+
706
+ if [[ $count -eq 0 ]]; then
707
+ if [[ "$filter_mine" == "true" ]]; then
708
+ echo " (no tasks assigned to you)"
709
+ else
710
+ echo " (no active tasks)"
711
+ fi
712
+ fi
713
+
714
+ echo ""
715
+ echo "Total: $count task(s)"
716
+ }
717
+
718
+ # =============================================================================
719
+ # Command: list-archive
720
+ # =============================================================================
721
+
722
+ cmd_list_archive() {
723
+ local month="$1"
724
+
725
+ local tasks_dir=$(get_tasks_dir)
726
+ local archive_dir="$tasks_dir/archive"
727
+
728
+ echo -e "${BLUE}Archived tasks:${NC}"
729
+ echo ""
730
+
731
+ if [[ -n "$month" ]]; then
732
+ local month_dir="$archive_dir/$month"
733
+ if [[ -d "$month_dir" ]]; then
734
+ echo "[$month]"
735
+ for d in "$month_dir"/*/; do
736
+ if [[ -d "$d" ]]; then
737
+ echo " - $(basename "$d")/"
738
+ fi
739
+ done
740
+ else
741
+ echo " No archives for $month"
742
+ fi
743
+ else
744
+ for month_dir in "$archive_dir"/*/; do
745
+ if [[ -d "$month_dir" ]]; then
746
+ local month_name=$(basename "$month_dir")
747
+ local count=$(find "$month_dir" -maxdepth 1 -type d ! -name "$(basename "$month_dir")" | wc -l | tr -d ' ')
748
+ echo "[$month_name] - $count task(s)"
749
+ fi
750
+ done
751
+ fi
752
+ }
753
+
754
+ # =============================================================================
755
+ # Command: set-branch
756
+ # =============================================================================
757
+
758
+ cmd_set_branch() {
759
+ local target_dir="$1"
760
+ local branch="$2"
761
+
762
+ if [[ -z "$target_dir" ]] || [[ -z "$branch" ]]; then
763
+ echo -e "${RED}Error: Missing arguments${NC}"
764
+ echo "Usage: $0 set-branch <task-dir> <branch-name>"
765
+ echo "Example: $0 set-branch <dir> task/my-task"
766
+ exit 1
767
+ fi
768
+
769
+ # Support relative paths
770
+ if [[ ! "$target_dir" = /* ]]; then
771
+ target_dir="$REPO_ROOT/$target_dir"
772
+ fi
773
+
774
+ local task_json="$target_dir/$FILE_TASK_JSON"
775
+ if [[ ! -f "$task_json" ]]; then
776
+ echo -e "${RED}Error: task.json not found at $target_dir${NC}"
777
+ exit 1
778
+ fi
779
+
780
+ # Update branch field
781
+ jq --arg branch "$branch" '.branch = $branch' "$task_json" > "${task_json}.tmp"
782
+ mv "${task_json}.tmp" "$task_json"
783
+
784
+ echo -e "${GREEN}✓ Branch set to: $branch${NC}"
785
+ echo ""
786
+ echo -e "${BLUE}Now you can start the multi-agent pipeline:${NC}"
787
+ echo " ./.trellis/scripts/multi-agent/start.sh $1"
788
+ }
789
+
790
+ # =============================================================================
791
+ # Command: set-scope
792
+ # =============================================================================
793
+
794
+ cmd_set_scope() {
795
+ local target_dir="$1"
796
+ local scope="$2"
797
+
798
+ if [[ -z "$target_dir" ]] || [[ -z "$scope" ]]; then
799
+ echo -e "${RED}Error: Missing arguments${NC}"
800
+ echo "Usage: $0 set-scope <task-dir> <scope>"
801
+ echo "Example: $0 set-scope <dir> api"
802
+ exit 1
803
+ fi
804
+
805
+ # Support relative paths
806
+ if [[ ! "$target_dir" = /* ]]; then
807
+ target_dir="$REPO_ROOT/$target_dir"
808
+ fi
809
+
810
+ local task_json="$target_dir/$FILE_TASK_JSON"
811
+ if [[ ! -f "$task_json" ]]; then
812
+ echo -e "${RED}Error: task.json not found at $target_dir${NC}"
813
+ exit 1
814
+ fi
815
+
816
+ # Update scope field
817
+ jq --arg scope "$scope" '.scope = $scope' "$task_json" > "${task_json}.tmp"
818
+ mv "${task_json}.tmp" "$task_json"
819
+
820
+ echo -e "${GREEN}✓ Scope set to: $scope${NC}"
821
+ }
822
+
823
+ # =============================================================================
824
+ # Command: create-pr
825
+ # =============================================================================
826
+
827
+ cmd_create_pr() {
828
+ local target_dir=""
829
+ local dry_run=false
830
+
831
+ # Parse arguments
832
+ while [[ $# -gt 0 ]]; do
833
+ case "$1" in
834
+ --dry-run)
835
+ dry_run=true
836
+ shift
837
+ ;;
838
+ *)
839
+ if [[ -z "$target_dir" ]]; then
840
+ target_dir="$1"
841
+ fi
842
+ shift
843
+ ;;
844
+ esac
845
+ done
846
+
847
+ # Get task directory
848
+ if [[ -z "$target_dir" ]]; then
849
+ target_dir=$(get_current_task)
850
+ if [[ -z "$target_dir" ]]; then
851
+ echo -e "${RED}Error: No task directory specified and no current task set${NC}"
852
+ echo "Usage: $0 create-pr [task-dir] [--dry-run]"
853
+ exit 1
854
+ fi
855
+ fi
856
+
857
+ # Support relative paths
858
+ if [[ ! "$target_dir" = /* ]]; then
859
+ target_dir="$REPO_ROOT/$target_dir"
860
+ fi
861
+
862
+ local task_json="$target_dir/$FILE_TASK_JSON"
863
+ if [[ ! -f "$task_json" ]]; then
864
+ echo -e "${RED}Error: task.json not found at $target_dir${NC}"
865
+ exit 1
866
+ fi
867
+
868
+ echo -e "${BLUE}=== Create PR ===${NC}"
869
+ if [[ "$dry_run" == "true" ]]; then
870
+ echo -e "${YELLOW}[DRY-RUN MODE] No actual changes will be made${NC}"
871
+ fi
872
+ echo ""
873
+
874
+ # Read task config
875
+ local task_name=$(jq -r '.name' "$task_json")
876
+ local base_branch=$(jq -r '.base_branch // "main"' "$task_json")
877
+ local scope=$(jq -r '.scope // "core"' "$task_json")
878
+ local dev_type=$(jq -r '.dev_type // "feature"' "$task_json")
879
+
880
+ # Map dev_type to commit prefix
881
+ local commit_prefix
882
+ case "$dev_type" in
883
+ feature|frontend|backend|fullstack) commit_prefix="feat" ;;
884
+ bugfix|fix) commit_prefix="fix" ;;
885
+ refactor) commit_prefix="refactor" ;;
886
+ docs) commit_prefix="docs" ;;
887
+ test) commit_prefix="test" ;;
888
+ *) commit_prefix="feat" ;;
889
+ esac
890
+
891
+ echo -e "Task: ${task_name}"
892
+ echo -e "Base branch: ${base_branch}"
893
+ echo -e "Scope: ${scope}"
894
+ echo -e "Commit prefix: ${commit_prefix}"
895
+ echo ""
896
+
897
+ # Get current branch
898
+ local current_branch=$(git branch --show-current)
899
+ echo -e "Current branch: ${current_branch}"
900
+
901
+ # Check for changes
902
+ echo -e "${YELLOW}Checking for changes...${NC}"
903
+
904
+ # Stage changes (even in dry-run to detect what would be committed)
905
+ git add -A
906
+ # Exclude workspace and temp files
907
+ git reset "$DIR_WORKFLOW/$DIR_WORKSPACE/" 2>/dev/null || true
908
+ git reset .agent-log .agent-runner.sh 2>/dev/null || true
909
+
910
+ # Check if there are staged changes
911
+ if git diff --cached --quiet 2>/dev/null; then
912
+ echo -e "${YELLOW}No staged changes to commit${NC}"
913
+
914
+ # Check for unpushed commits
915
+ local unpushed=$(git log "origin/${current_branch}..HEAD" --oneline 2>/dev/null | wc -l | tr -d ' ' || echo "0")
916
+ if [[ "$unpushed" -eq 0 ]] 2>/dev/null; then
917
+ # In dry-run, also reset the staging
918
+ if [[ "$dry_run" == "true" ]]; then
919
+ git reset HEAD >/dev/null 2>&1 || true
920
+ fi
921
+ echo -e "${RED}No changes to create PR${NC}"
922
+ exit 1
923
+ fi
924
+ echo -e "Found ${unpushed} unpushed commit(s)"
925
+ else
926
+ # Commit changes
927
+ echo -e "${YELLOW}Committing changes...${NC}"
928
+ local commit_msg="${commit_prefix}(${scope}): ${task_name}"
929
+
930
+ if [[ "$dry_run" == "true" ]]; then
931
+ echo -e "[DRY-RUN] Would commit with message: ${commit_msg}"
932
+ echo -e "[DRY-RUN] Staged files:"
933
+ git diff --cached --name-only | sed 's/^/ - /'
934
+ else
935
+ git commit -m "$commit_msg"
936
+ echo -e "${GREEN}Committed: ${commit_msg}${NC}"
937
+ fi
938
+ fi
939
+
940
+ # Push to remote
941
+ echo -e "${YELLOW}Pushing to remote...${NC}"
942
+ if [[ "$dry_run" == "true" ]]; then
943
+ echo -e "[DRY-RUN] Would push to: origin/${current_branch}"
944
+ else
945
+ git push -u origin "$current_branch"
946
+ echo -e "${GREEN}Pushed to origin/${current_branch}${NC}"
947
+ fi
948
+
949
+ # Create PR
950
+ echo -e "${YELLOW}Creating PR...${NC}"
951
+ local pr_title="${commit_prefix}(${scope}): ${task_name}"
952
+ local pr_url=""
953
+
954
+ if [[ "$dry_run" == "true" ]]; then
955
+ echo -e "[DRY-RUN] Would create PR:"
956
+ echo -e " Title: ${pr_title}"
957
+ echo -e " Base: ${base_branch}"
958
+ echo -e " Head: ${current_branch}"
959
+ if [[ -f "$target_dir/prd.md" ]]; then
960
+ echo -e " Body: (from prd.md)"
961
+ fi
962
+ pr_url="https://github.com/example/repo/pull/DRY-RUN"
963
+ else
964
+ # Check if PR already exists
965
+ local existing_pr=$(gh pr list --head "$current_branch" --base "$base_branch" --json url --jq '.[0].url' 2>/dev/null || echo "")
966
+
967
+ if [[ -n "$existing_pr" ]]; then
968
+ echo -e "${YELLOW}PR already exists: ${existing_pr}${NC}"
969
+ pr_url="$existing_pr"
970
+ else
971
+ # Read PRD as PR body
972
+ local pr_body=""
973
+ if [[ -f "$target_dir/prd.md" ]]; then
974
+ pr_body=$(cat "$target_dir/prd.md")
975
+ fi
976
+
977
+ # Create PR
978
+ pr_url=$(gh pr create \
979
+ --draft \
980
+ --base "$base_branch" \
981
+ --title "$pr_title" \
982
+ --body "$pr_body" \
983
+ 2>&1)
984
+
985
+ echo -e "${GREEN}PR created: ${pr_url}${NC}"
986
+ fi
987
+ fi
988
+
989
+ # Update task.json
990
+ echo -e "${YELLOW}Updating task status...${NC}"
991
+ if [[ "$dry_run" == "true" ]]; then
992
+ echo -e "[DRY-RUN] Would update task.json:"
993
+ echo -e " status: review"
994
+ echo -e " pr_url: ${pr_url}"
995
+ echo -e " current_phase: (set to create-pr phase)"
996
+ else
997
+ # Find the phase number for create-pr action
998
+ local create_pr_phase=$(jq -r '.next_action[] | select(.action == "create-pr") | .phase // 4' "$task_json")
999
+ jq --arg url "$pr_url" --argjson phase "$create_pr_phase" \
1000
+ '.status = "review" | .pr_url = $url | .current_phase = $phase' "$task_json" > "${task_json}.tmp"
1001
+ mv "${task_json}.tmp" "$task_json"
1002
+ echo -e "${GREEN}Task status updated to 'review', phase ${create_pr_phase}${NC}"
1003
+ fi
1004
+
1005
+ # In dry-run, reset the staging area
1006
+ if [[ "$dry_run" == "true" ]]; then
1007
+ git reset HEAD >/dev/null 2>&1 || true
1008
+ fi
1009
+
1010
+ echo ""
1011
+ echo -e "${GREEN}=== PR Created Successfully ===${NC}"
1012
+ echo -e "PR URL: ${pr_url}"
1013
+ echo -e "Target: ${base_branch}"
1014
+ echo -e "Source: ${current_branch}"
1015
+ }
1016
+
1017
+ # =============================================================================
1018
+ # Help
1019
+ # =============================================================================
1020
+
1021
+ show_usage() {
1022
+ cat << EOF
1023
+ Task Management Script for Multi-Agent Pipeline
1024
+
1025
+ Usage:
1026
+ $0 create <title> Create new task directory
1027
+ $0 init-context <dir> <dev_type> Initialize jsonl files
1028
+ $0 add-context <dir> <jsonl> <path> [reason] Add entry to jsonl
1029
+ $0 validate <dir> Validate jsonl files
1030
+ $0 list-context <dir> List jsonl entries
1031
+ $0 start <dir> Set as current task
1032
+ $0 finish Clear current task
1033
+ $0 set-branch <dir> <branch> Set git branch for multi-agent
1034
+ $0 set-scope <dir> <scope> Set scope for PR title
1035
+ $0 create-pr [dir] [--dry-run] Create PR from task
1036
+ $0 archive <task-name> Archive completed task
1037
+ $0 list [--mine] [--status <status>] List tasks
1038
+ $0 list-archive [YYYY-MM] List archived tasks
1039
+
1040
+ Arguments:
1041
+ dev_type: backend | frontend | fullstack | test | docs
1042
+
1043
+ List options:
1044
+ --mine, -m Show only tasks assigned to current developer
1045
+ --status, -s <s> Filter by status (planning, in_progress, review, completed)
1046
+
1047
+ Examples:
1048
+ $0 create "Add login feature" --slug add-login
1049
+ $0 init-context .trellis/tasks/01-21-add-login backend
1050
+ $0 add-context <dir> implement .trellis/spec/backend/auth.md "Auth guidelines"
1051
+ $0 set-branch <dir> task/add-login
1052
+ $0 start .trellis/tasks/01-21-add-login
1053
+ $0 create-pr # Uses current task
1054
+ $0 create-pr <dir> --dry-run # Preview without changes
1055
+ $0 finish
1056
+ $0 archive add-login
1057
+ $0 list # List all active tasks
1058
+ $0 list --mine # List my tasks only
1059
+ $0 list --mine --status in_progress # List my in-progress tasks
1060
+ EOF
1061
+ }
1062
+
1063
+ # =============================================================================
1064
+ # Main Entry
1065
+ # =============================================================================
1066
+
1067
+ case "${1:-}" in
1068
+ create)
1069
+ shift
1070
+ cmd_create "$@"
1071
+ ;;
1072
+ init-context)
1073
+ cmd_init_context "$2" "$3"
1074
+ ;;
1075
+ add-context)
1076
+ cmd_add_context "$2" "$3" "$4" "$5"
1077
+ ;;
1078
+ validate)
1079
+ cmd_validate "$2"
1080
+ ;;
1081
+ list-context)
1082
+ cmd_list_context "$2"
1083
+ ;;
1084
+ start)
1085
+ cmd_start "$2"
1086
+ ;;
1087
+ finish)
1088
+ cmd_finish
1089
+ ;;
1090
+ set-branch)
1091
+ cmd_set_branch "$2" "$3"
1092
+ ;;
1093
+ set-scope)
1094
+ cmd_set_scope "$2" "$3"
1095
+ ;;
1096
+ create-pr)
1097
+ # Delegate to multi-agent/create-pr.sh
1098
+ shift
1099
+ "$SCRIPT_DIR/multi-agent/create-pr.sh" "$@"
1100
+ ;;
1101
+ archive)
1102
+ cmd_archive "$2"
1103
+ ;;
1104
+ list)
1105
+ shift
1106
+ cmd_list "$@"
1107
+ ;;
1108
+ list-archive)
1109
+ cmd_list_archive "$2"
1110
+ ;;
1111
+ -h|--help|help)
1112
+ show_usage
1113
+ ;;
1114
+ *)
1115
+ show_usage
1116
+ exit 1
1117
+ ;;
1118
+ esac