@fifine/aim-studio 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (289) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +159 -0
  3. package/bin/aim.js +3 -0
  4. package/dist/cli/index.d.ts +3 -0
  5. package/dist/cli/index.d.ts.map +1 -0
  6. package/dist/cli/index.js +89 -0
  7. package/dist/cli/index.js.map +1 -0
  8. package/dist/commands/init.d.ts +13 -0
  9. package/dist/commands/init.d.ts.map +1 -0
  10. package/dist/commands/init.js +513 -0
  11. package/dist/commands/init.js.map +1 -0
  12. package/dist/commands/update.d.ts +27 -0
  13. package/dist/commands/update.d.ts.map +1 -0
  14. package/dist/commands/update.js +1275 -0
  15. package/dist/commands/update.js.map +1 -0
  16. package/dist/configurators/claude.d.ts +32 -0
  17. package/dist/configurators/claude.d.ts.map +1 -0
  18. package/dist/configurators/claude.js +98 -0
  19. package/dist/configurators/claude.js.map +1 -0
  20. package/dist/configurators/index.d.ts +51 -0
  21. package/dist/configurators/index.d.ts.map +1 -0
  22. package/dist/configurators/index.js +113 -0
  23. package/dist/configurators/index.js.map +1 -0
  24. package/dist/configurators/shared.d.ts +12 -0
  25. package/dist/configurators/shared.d.ts.map +1 -0
  26. package/dist/configurators/shared.js +21 -0
  27. package/dist/configurators/shared.js.map +1 -0
  28. package/dist/configurators/workflow.d.ts +28 -0
  29. package/dist/configurators/workflow.d.ts.map +1 -0
  30. package/dist/configurators/workflow.js +147 -0
  31. package/dist/configurators/workflow.js.map +1 -0
  32. package/dist/constants/paths.d.ts +68 -0
  33. package/dist/constants/paths.d.ts.map +1 -0
  34. package/dist/constants/paths.js +77 -0
  35. package/dist/constants/paths.js.map +1 -0
  36. package/dist/constants/version.d.ts +9 -0
  37. package/dist/constants/version.d.ts.map +1 -0
  38. package/dist/constants/version.js +15 -0
  39. package/dist/constants/version.js.map +1 -0
  40. package/dist/index.d.ts +9 -0
  41. package/dist/index.d.ts.map +1 -0
  42. package/dist/index.js +9 -0
  43. package/dist/index.js.map +1 -0
  44. package/dist/migrations/index.d.ts +54 -0
  45. package/dist/migrations/index.d.ts.map +1 -0
  46. package/dist/migrations/index.js +160 -0
  47. package/dist/migrations/index.js.map +1 -0
  48. package/dist/migrations/manifests/0.0.1.json +9 -0
  49. package/dist/migrations/manifests/0.1.9.json +30 -0
  50. package/dist/migrations/manifests/0.2.0.json +49 -0
  51. package/dist/migrations/manifests/0.2.12.json +9 -0
  52. package/dist/migrations/manifests/0.2.13.json +9 -0
  53. package/dist/migrations/manifests/0.2.14.json +175 -0
  54. package/dist/migrations/manifests/0.2.15.json +33 -0
  55. package/dist/migrations/manifests/0.3.0-beta.0.json +278 -0
  56. package/dist/migrations/manifests/0.3.0-beta.1.json +9 -0
  57. package/dist/migrations/manifests/0.3.0-beta.10.json +9 -0
  58. package/dist/migrations/manifests/0.3.0-beta.11.json +9 -0
  59. package/dist/migrations/manifests/0.3.0-beta.12.json +9 -0
  60. package/dist/migrations/manifests/0.3.0-beta.13.json +9 -0
  61. package/dist/migrations/manifests/0.3.0-beta.14.json +9 -0
  62. package/dist/migrations/manifests/0.3.0-beta.15.json +9 -0
  63. package/dist/migrations/manifests/0.3.0-beta.16.json +9 -0
  64. package/dist/migrations/manifests/0.3.0-beta.2.json +9 -0
  65. package/dist/migrations/manifests/0.3.0-beta.3.json +9 -0
  66. package/dist/migrations/manifests/0.3.0-beta.4.json +9 -0
  67. package/dist/migrations/manifests/0.3.0-beta.5.json +9 -0
  68. package/dist/migrations/manifests/0.3.0-beta.6.json +9 -0
  69. package/dist/migrations/manifests/0.3.0-beta.7.json +11 -0
  70. package/dist/migrations/manifests/0.3.0-beta.8.json +9 -0
  71. package/dist/migrations/manifests/0.3.0-beta.9.json +9 -0
  72. package/dist/migrations/manifests/0.3.0-rc.0.json +9 -0
  73. package/dist/migrations/manifests/0.3.0-rc.1.json +9 -0
  74. package/dist/migrations/manifests/0.3.0-rc.2.json +9 -0
  75. package/dist/templates/CLAUDE.md +71 -0
  76. package/dist/templates/aim/gitignore.txt +29 -0
  77. package/dist/templates/aim/index.d.ts +49 -0
  78. package/dist/templates/aim/index.d.ts.map +1 -0
  79. package/dist/templates/aim/index.js +92 -0
  80. package/dist/templates/aim/index.js.map +1 -0
  81. package/dist/templates/aim/scripts/__init__.py +5 -0
  82. package/dist/templates/aim/scripts/add_session.py +392 -0
  83. package/dist/templates/aim/scripts/common/__init__.py +80 -0
  84. package/dist/templates/aim/scripts/common/cli_adapter.py +435 -0
  85. package/dist/templates/aim/scripts/common/developer.py +190 -0
  86. package/dist/templates/aim/scripts/common/git_context.py +383 -0
  87. package/dist/templates/aim/scripts/common/paths.py +347 -0
  88. package/dist/templates/aim/scripts/common/phase.py +253 -0
  89. package/dist/templates/aim/scripts/common/registry.py +366 -0
  90. package/dist/templates/aim/scripts/common/task_queue.py +255 -0
  91. package/dist/templates/aim/scripts/common/task_utils.py +178 -0
  92. package/dist/templates/aim/scripts/common/worktree.py +219 -0
  93. package/dist/templates/aim/scripts/create_bootstrap.py +290 -0
  94. package/dist/templates/aim/scripts/get_context.py +16 -0
  95. package/dist/templates/aim/scripts/get_developer.py +26 -0
  96. package/dist/templates/aim/scripts/init_developer.py +51 -0
  97. package/dist/templates/aim/scripts/multi_agent/__init__.py +5 -0
  98. package/dist/templates/aim/scripts/multi_agent/cleanup.py +403 -0
  99. package/dist/templates/aim/scripts/multi_agent/create_pr.py +329 -0
  100. package/dist/templates/aim/scripts/multi_agent/plan.py +233 -0
  101. package/dist/templates/aim/scripts/multi_agent/start.py +461 -0
  102. package/dist/templates/aim/scripts/multi_agent/status.py +817 -0
  103. package/dist/templates/aim/scripts/task.py +1068 -0
  104. package/dist/templates/aim/scripts-shell-archive/add-session.sh +384 -0
  105. package/dist/templates/aim/scripts-shell-archive/common/developer.sh +129 -0
  106. package/dist/templates/aim/scripts-shell-archive/common/git-context.sh +263 -0
  107. package/dist/templates/aim/scripts-shell-archive/common/paths.sh +208 -0
  108. package/dist/templates/aim/scripts-shell-archive/common/phase.sh +150 -0
  109. package/dist/templates/aim/scripts-shell-archive/common/registry.sh +247 -0
  110. package/dist/templates/aim/scripts-shell-archive/common/task-queue.sh +142 -0
  111. package/dist/templates/aim/scripts-shell-archive/common/task-utils.sh +151 -0
  112. package/dist/templates/aim/scripts-shell-archive/common/worktree.sh +128 -0
  113. package/dist/templates/aim/scripts-shell-archive/create-bootstrap.sh +299 -0
  114. package/dist/templates/aim/scripts-shell-archive/get-context.sh +7 -0
  115. package/dist/templates/aim/scripts-shell-archive/get-developer.sh +15 -0
  116. package/dist/templates/aim/scripts-shell-archive/init-developer.sh +34 -0
  117. package/dist/templates/aim/scripts-shell-archive/multi-agent/cleanup.sh +396 -0
  118. package/dist/templates/aim/scripts-shell-archive/multi-agent/create-pr.sh +241 -0
  119. package/dist/templates/aim/scripts-shell-archive/multi-agent/plan.sh +207 -0
  120. package/dist/templates/aim/scripts-shell-archive/multi-agent/start.sh +317 -0
  121. package/dist/templates/aim/scripts-shell-archive/multi-agent/status.sh +828 -0
  122. package/dist/templates/aim/scripts-shell-archive/task.sh +1204 -0
  123. package/dist/templates/aim/tasks/.gitkeep +0 -0
  124. package/dist/templates/aim/workflow.md +258 -0
  125. package/dist/templates/aim/worktree.yaml +47 -0
  126. package/dist/templates/claude/agents/check.md +122 -0
  127. package/dist/templates/claude/agents/debug.md +106 -0
  128. package/dist/templates/claude/agents/dispatch.md +230 -0
  129. package/dist/templates/claude/agents/implement.md +96 -0
  130. package/dist/templates/claude/agents/plan.md +396 -0
  131. package/dist/templates/claude/agents/research.md +120 -0
  132. package/dist/templates/claude/agents/story.md +53 -0
  133. package/dist/templates/claude/commands/aim/before-backend-dev.md +13 -0
  134. package/dist/templates/claude/commands/aim/before-frontend-dev.md +13 -0
  135. package/dist/templates/claude/commands/aim/break-loop.md +153 -0
  136. package/dist/templates/claude/commands/aim/check-backend.md +13 -0
  137. package/dist/templates/claude/commands/aim/check-cross-layer.md +153 -0
  138. package/dist/templates/claude/commands/aim/check-frontend.md +13 -0
  139. package/dist/templates/claude/commands/aim/check-story.md +59 -0
  140. package/dist/templates/claude/commands/aim/create-command.md +154 -0
  141. package/dist/templates/claude/commands/aim/export.md +187 -0
  142. package/dist/templates/claude/commands/aim/finish-work.md +104 -0
  143. package/dist/templates/claude/commands/aim/integrate-skill.md +219 -0
  144. package/dist/templates/claude/commands/aim/onboard.md +358 -0
  145. package/dist/templates/claude/commands/aim/parallel.md +217 -0
  146. package/dist/templates/claude/commands/aim/portrait.md +170 -0
  147. package/dist/templates/claude/commands/aim/record-session.md +92 -0
  148. package/dist/templates/claude/commands/aim/start.md +112 -0
  149. package/dist/templates/claude/commands/aim/story.md +140 -0
  150. package/dist/templates/claude/commands/aim/update-spec.md +285 -0
  151. package/dist/templates/claude/commands/aim/visualize.md +182 -0
  152. package/dist/templates/claude/commands/trellis/before-backend-dev.md +13 -0
  153. package/dist/templates/claude/commands/trellis/before-frontend-dev.md +13 -0
  154. package/dist/templates/claude/commands/trellis/break-loop.md +125 -0
  155. package/dist/templates/claude/commands/trellis/check-backend.md +13 -0
  156. package/dist/templates/claude/commands/trellis/check-cross-layer.md +153 -0
  157. package/dist/templates/claude/commands/trellis/check-frontend.md +13 -0
  158. package/dist/templates/claude/commands/trellis/create-command.md +154 -0
  159. package/dist/templates/claude/commands/trellis/finish-work.md +129 -0
  160. package/dist/templates/claude/commands/trellis/integrate-skill.md +219 -0
  161. package/dist/templates/claude/commands/trellis/onboard.md +358 -0
  162. package/dist/templates/claude/commands/trellis/parallel.md +193 -0
  163. package/dist/templates/claude/commands/trellis/record-session.md +62 -0
  164. package/dist/templates/claude/commands/trellis/start.md +280 -0
  165. package/dist/templates/claude/commands/trellis/update-spec.md +285 -0
  166. package/dist/templates/claude/hooks/inject-subagent-context.py +772 -0
  167. package/dist/templates/claude/hooks/ralph-loop.py +388 -0
  168. package/dist/templates/claude/hooks/session-start.py +142 -0
  169. package/dist/templates/claude/index.d.ts +54 -0
  170. package/dist/templates/claude/index.d.ts.map +1 -0
  171. package/dist/templates/claude/index.js +85 -0
  172. package/dist/templates/claude/index.js.map +1 -0
  173. package/dist/templates/claude/settings.json +41 -0
  174. package/dist/templates/extract.d.ts +68 -0
  175. package/dist/templates/extract.d.ts.map +1 -0
  176. package/dist/templates/extract.js +128 -0
  177. package/dist/templates/extract.js.map +1 -0
  178. package/dist/templates/markdown/agents.md +25 -0
  179. package/dist/templates/markdown/gitignore.txt +12 -0
  180. package/dist/templates/markdown/index.d.ts +32 -0
  181. package/dist/templates/markdown/index.d.ts.map +1 -0
  182. package/dist/templates/markdown/index.js +58 -0
  183. package/dist/templates/markdown/index.js.map +1 -0
  184. package/dist/templates/markdown/spec/backend/database-guidelines.md.txt +51 -0
  185. package/dist/templates/markdown/spec/backend/directory-structure.md.txt +54 -0
  186. package/dist/templates/markdown/spec/backend/error-handling.md.txt +51 -0
  187. package/dist/templates/markdown/spec/backend/index.md +40 -0
  188. package/dist/templates/markdown/spec/backend/index.md.txt +38 -0
  189. package/dist/templates/markdown/spec/backend/logging-guidelines.md.txt +51 -0
  190. package/dist/templates/markdown/spec/backend/quality-guidelines.md.txt +51 -0
  191. package/dist/templates/markdown/spec/backend/script-conventions.md +467 -0
  192. package/dist/templates/markdown/spec/frontend/component-guidelines.md.txt +59 -0
  193. package/dist/templates/markdown/spec/frontend/directory-structure.md.txt +54 -0
  194. package/dist/templates/markdown/spec/frontend/hook-guidelines.md.txt +51 -0
  195. package/dist/templates/markdown/spec/frontend/index.md.txt +39 -0
  196. package/dist/templates/markdown/spec/frontend/quality-guidelines.md.txt +51 -0
  197. package/dist/templates/markdown/spec/frontend/state-management.md.txt +51 -0
  198. package/dist/templates/markdown/spec/frontend/type-safety.md.txt +51 -0
  199. package/dist/templates/markdown/spec/guides/code-reuse-thinking-guide.md +118 -0
  200. package/dist/templates/markdown/spec/guides/code-reuse-thinking-guide.md.txt +92 -0
  201. package/dist/templates/markdown/spec/guides/cross-layer-thinking-guide.md.txt +94 -0
  202. package/dist/templates/markdown/spec/guides/cross-platform-thinking-guide.md +394 -0
  203. package/dist/templates/markdown/spec/guides/cross-platform-thinking-guide.md.txt +319 -0
  204. package/dist/templates/markdown/spec/guides/index.md.txt +89 -0
  205. package/dist/templates/markdown/spec/story/character.md.txt +95 -0
  206. package/dist/templates/markdown/spec/story/index.md.txt +31 -0
  207. package/dist/templates/markdown/spec/story/script.md.txt +313 -0
  208. package/dist/templates/markdown/spec/story/world.md.txt +92 -0
  209. package/dist/templates/markdown/workspace-index.md +123 -0
  210. package/dist/templates/markdown/worktree.yaml.txt +58 -0
  211. package/dist/templates/trellis/gitignore.txt +29 -0
  212. package/dist/templates/trellis/index.d.ts +49 -0
  213. package/dist/templates/trellis/index.d.ts.map +1 -0
  214. package/dist/templates/trellis/index.js +92 -0
  215. package/dist/templates/trellis/index.js.map +1 -0
  216. package/dist/templates/trellis/scripts/__init__.py +5 -0
  217. package/dist/templates/trellis/scripts/add_session.py +392 -0
  218. package/dist/templates/trellis/scripts/common/__init__.py +80 -0
  219. package/dist/templates/trellis/scripts/common/cli_adapter.py +435 -0
  220. package/dist/templates/trellis/scripts/common/developer.py +190 -0
  221. package/dist/templates/trellis/scripts/common/git_context.py +383 -0
  222. package/dist/templates/trellis/scripts/common/paths.py +347 -0
  223. package/dist/templates/trellis/scripts/common/phase.py +253 -0
  224. package/dist/templates/trellis/scripts/common/registry.py +366 -0
  225. package/dist/templates/trellis/scripts/common/task_queue.py +255 -0
  226. package/dist/templates/trellis/scripts/common/task_utils.py +178 -0
  227. package/dist/templates/trellis/scripts/common/worktree.py +219 -0
  228. package/dist/templates/trellis/scripts/create_bootstrap.py +290 -0
  229. package/dist/templates/trellis/scripts/get_context.py +16 -0
  230. package/dist/templates/trellis/scripts/get_developer.py +26 -0
  231. package/dist/templates/trellis/scripts/init_developer.py +51 -0
  232. package/dist/templates/trellis/scripts/multi_agent/__init__.py +5 -0
  233. package/dist/templates/trellis/scripts/multi_agent/cleanup.py +403 -0
  234. package/dist/templates/trellis/scripts/multi_agent/create_pr.py +329 -0
  235. package/dist/templates/trellis/scripts/multi_agent/plan.py +233 -0
  236. package/dist/templates/trellis/scripts/multi_agent/start.py +461 -0
  237. package/dist/templates/trellis/scripts/multi_agent/status.py +817 -0
  238. package/dist/templates/trellis/scripts/task.py +1056 -0
  239. package/dist/templates/trellis/scripts-shell-archive/add-session.sh +384 -0
  240. package/dist/templates/trellis/scripts-shell-archive/common/developer.sh +129 -0
  241. package/dist/templates/trellis/scripts-shell-archive/common/git-context.sh +263 -0
  242. package/dist/templates/trellis/scripts-shell-archive/common/paths.sh +208 -0
  243. package/dist/templates/trellis/scripts-shell-archive/common/phase.sh +150 -0
  244. package/dist/templates/trellis/scripts-shell-archive/common/registry.sh +247 -0
  245. package/dist/templates/trellis/scripts-shell-archive/common/task-queue.sh +142 -0
  246. package/dist/templates/trellis/scripts-shell-archive/common/task-utils.sh +151 -0
  247. package/dist/templates/trellis/scripts-shell-archive/common/worktree.sh +128 -0
  248. package/dist/templates/trellis/scripts-shell-archive/create-bootstrap.sh +299 -0
  249. package/dist/templates/trellis/scripts-shell-archive/get-context.sh +7 -0
  250. package/dist/templates/trellis/scripts-shell-archive/get-developer.sh +15 -0
  251. package/dist/templates/trellis/scripts-shell-archive/init-developer.sh +34 -0
  252. package/dist/templates/trellis/scripts-shell-archive/multi-agent/cleanup.sh +396 -0
  253. package/dist/templates/trellis/scripts-shell-archive/multi-agent/create-pr.sh +241 -0
  254. package/dist/templates/trellis/scripts-shell-archive/multi-agent/plan.sh +207 -0
  255. package/dist/templates/trellis/scripts-shell-archive/multi-agent/start.sh +317 -0
  256. package/dist/templates/trellis/scripts-shell-archive/multi-agent/status.sh +828 -0
  257. package/dist/templates/trellis/scripts-shell-archive/task.sh +1204 -0
  258. package/dist/templates/trellis/tasks/.gitkeep +0 -0
  259. package/dist/templates/trellis/workflow.md +416 -0
  260. package/dist/templates/trellis/worktree.yaml +47 -0
  261. package/dist/types/ai-tools.d.ts +48 -0
  262. package/dist/types/ai-tools.d.ts.map +1 -0
  263. package/dist/types/ai-tools.js +32 -0
  264. package/dist/types/ai-tools.js.map +1 -0
  265. package/dist/types/migration.d.ts +86 -0
  266. package/dist/types/migration.d.ts.map +1 -0
  267. package/dist/types/migration.js +8 -0
  268. package/dist/types/migration.js.map +1 -0
  269. package/dist/utils/compare-versions.d.ts +12 -0
  270. package/dist/utils/compare-versions.d.ts.map +1 -0
  271. package/dist/utils/compare-versions.js +76 -0
  272. package/dist/utils/compare-versions.js.map +1 -0
  273. package/dist/utils/file-writer.d.ts +23 -0
  274. package/dist/utils/file-writer.d.ts.map +1 -0
  275. package/dist/utils/file-writer.js +140 -0
  276. package/dist/utils/file-writer.js.map +1 -0
  277. package/dist/utils/project-detector.d.ts +16 -0
  278. package/dist/utils/project-detector.d.ts.map +1 -0
  279. package/dist/utils/project-detector.js +188 -0
  280. package/dist/utils/project-detector.js.map +1 -0
  281. package/dist/utils/template-fetcher.d.ts +51 -0
  282. package/dist/utils/template-fetcher.d.ts.map +1 -0
  283. package/dist/utils/template-fetcher.js +174 -0
  284. package/dist/utils/template-fetcher.js.map +1 -0
  285. package/dist/utils/template-hash.d.ts +78 -0
  286. package/dist/utils/template-hash.d.ts.map +1 -0
  287. package/dist/utils/template-hash.js +239 -0
  288. package/dist/utils/template-hash.js.map +1 -0
  289. package/package.json +87 -0
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Get current developer name.
4
+
5
+ This is a wrapper that uses common/paths.py
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import sys
11
+
12
+ from common.paths import get_developer
13
+
14
+
15
+ def main() -> None:
16
+ """CLI entry point."""
17
+ developer = get_developer()
18
+ if developer:
19
+ print(developer)
20
+ else:
21
+ print("Developer not initialized", file=sys.stderr)
22
+ sys.exit(1)
23
+
24
+
25
+ if __name__ == "__main__":
26
+ main()
@@ -0,0 +1,51 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Initialize developer for workflow.
4
+
5
+ Usage:
6
+ python3 init_developer.py <developer-name>
7
+
8
+ This creates:
9
+ - .trellis/.developer file with developer info
10
+ - .trellis/workspace/<name>/ directory structure
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ import sys
16
+
17
+ from common.paths import (
18
+ DIR_WORKFLOW,
19
+ FILE_DEVELOPER,
20
+ get_developer,
21
+ )
22
+ from common.developer import init_developer
23
+
24
+
25
+ def main() -> None:
26
+ """CLI entry point."""
27
+ if len(sys.argv) < 2:
28
+ print(f"Usage: {sys.argv[0]} <developer-name>")
29
+ print()
30
+ print("Example:")
31
+ print(f" {sys.argv[0]} john")
32
+ sys.exit(1)
33
+
34
+ name = sys.argv[1]
35
+
36
+ # Check if already initialized
37
+ existing = get_developer()
38
+ if existing:
39
+ print(f"Developer already initialized: {existing}")
40
+ print()
41
+ print(f"To reinitialize, remove {DIR_WORKFLOW}/{FILE_DEVELOPER} first")
42
+ sys.exit(0)
43
+
44
+ if init_developer(name):
45
+ sys.exit(0)
46
+ else:
47
+ sys.exit(1)
48
+
49
+
50
+ if __name__ == "__main__":
51
+ main()
@@ -0,0 +1,5 @@
1
+ """
2
+ Multi-Agent Pipeline Scripts.
3
+
4
+ This module provides orchestration for multi-agent workflows.
5
+ """
@@ -0,0 +1,403 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Multi-Agent Pipeline: Cleanup Worktree.
4
+
5
+ Usage:
6
+ python3 cleanup.py <branch-name> Remove specific worktree
7
+ python3 cleanup.py --list List all worktrees
8
+ python3 cleanup.py --merged Remove merged worktrees
9
+ python3 cleanup.py --all Remove all worktrees (with confirmation)
10
+
11
+ Options:
12
+ -y, --yes Skip confirmation prompts
13
+ --keep-branch Don't delete the git branch
14
+
15
+ This script:
16
+ 1. Archives task directory to archive/{YYYY-MM}/
17
+ 2. Removes agent from registry
18
+ 3. Removes git worktree
19
+ 4. Optionally deletes git branch
20
+ """
21
+
22
+ from __future__ import annotations
23
+
24
+ import argparse
25
+ import shutil
26
+ import subprocess
27
+ import sys
28
+ from pathlib import Path
29
+
30
+ # Add parent directory to path for imports
31
+ sys.path.insert(0, str(Path(__file__).parent.parent))
32
+
33
+ from common.git_context import _run_git_command
34
+ from common.paths import get_repo_root
35
+ from common.registry import (
36
+ registry_get_file,
37
+ registry_get_task_dir,
38
+ registry_remove_by_id,
39
+ registry_remove_by_worktree,
40
+ registry_search_agent,
41
+ )
42
+ from common.task_utils import (
43
+ archive_task_complete,
44
+ is_safe_task_path,
45
+ )
46
+
47
+ # =============================================================================
48
+ # Colors
49
+ # =============================================================================
50
+
51
+
52
+ class Colors:
53
+ RED = "\033[0;31m"
54
+ GREEN = "\033[0;32m"
55
+ YELLOW = "\033[1;33m"
56
+ BLUE = "\033[0;34m"
57
+ NC = "\033[0m"
58
+
59
+
60
+ def log_info(msg: str) -> None:
61
+ print(f"{Colors.BLUE}[INFO]{Colors.NC} {msg}")
62
+
63
+
64
+ def log_success(msg: str) -> None:
65
+ print(f"{Colors.GREEN}[SUCCESS]{Colors.NC} {msg}")
66
+
67
+
68
+ def log_warn(msg: str) -> None:
69
+ print(f"{Colors.YELLOW}[WARN]{Colors.NC} {msg}")
70
+
71
+
72
+ def log_error(msg: str) -> None:
73
+ print(f"{Colors.RED}[ERROR]{Colors.NC} {msg}")
74
+
75
+
76
+ # =============================================================================
77
+ # Helper Functions
78
+ # =============================================================================
79
+
80
+
81
+ def confirm(prompt: str, skip_confirm: bool) -> bool:
82
+ """Ask for confirmation."""
83
+ if skip_confirm:
84
+ return True
85
+
86
+ if not sys.stdin.isatty():
87
+ log_error("Non-interactive mode detected. Use -y to skip confirmation.")
88
+ return False
89
+
90
+ response = input(f"{prompt} [y/N] ")
91
+ return response.lower() in ("y", "yes")
92
+
93
+
94
+ # =============================================================================
95
+ # Commands
96
+ # =============================================================================
97
+
98
+
99
+ def cmd_list(repo_root: Path) -> int:
100
+ """List worktrees."""
101
+ print(f"{Colors.BLUE}=== Git Worktrees ==={Colors.NC}")
102
+ print()
103
+
104
+ subprocess.run(["git", "worktree", "list"], cwd=repo_root)
105
+ print()
106
+
107
+ # Show registry info
108
+ registry_file = registry_get_file(repo_root)
109
+ if registry_file and registry_file.is_file():
110
+ print(f"{Colors.BLUE}=== Registered Agents ==={Colors.NC}")
111
+ print()
112
+
113
+ import json
114
+
115
+ data = json.loads(registry_file.read_text(encoding="utf-8"))
116
+ agents = data.get("agents", [])
117
+
118
+ if agents:
119
+ for agent in agents:
120
+ print(
121
+ f" {agent.get('id', '?')}: PID={agent.get('pid', '?')} [{agent.get('worktree_path', '?')}]"
122
+ )
123
+ else:
124
+ print(" (none)")
125
+ print()
126
+
127
+ return 0
128
+
129
+
130
+ def archive_task(worktree_path: str, repo_root: Path) -> None:
131
+ """Archive task directory."""
132
+ task_dir = registry_get_task_dir(worktree_path, repo_root)
133
+
134
+ if not task_dir or not is_safe_task_path(task_dir, repo_root):
135
+ return
136
+
137
+ task_dir_abs = repo_root / task_dir
138
+ if not task_dir_abs.is_dir():
139
+ return
140
+
141
+ result = archive_task_complete(task_dir_abs, repo_root)
142
+ if "archived_to" in result:
143
+ dest = Path(result["archived_to"])
144
+ log_success(f"Archived task: {dest.name} -> archive/{dest.parent.name}/")
145
+
146
+
147
+ def cleanup_registry_only(search: str, repo_root: Path, skip_confirm: bool) -> int:
148
+ """Cleanup from registry only (no worktree)."""
149
+ agent_info = registry_search_agent(search, repo_root)
150
+
151
+ if not agent_info:
152
+ log_error(f"No agent found in registry matching: {search}")
153
+ return 1
154
+
155
+ agent_id = agent_info.get("id", "?")
156
+ task_dir = agent_info.get("task_dir", "?")
157
+
158
+ print()
159
+ print(f"{Colors.BLUE}=== Cleanup Agent (no worktree) ==={Colors.NC}")
160
+ print(f" Agent ID: {agent_id}")
161
+ print(f" Task Dir: {task_dir}")
162
+ print()
163
+
164
+ if not confirm("Archive task and remove from registry?", skip_confirm):
165
+ log_info("Aborted")
166
+ return 0
167
+
168
+ # Archive task directory if exists
169
+ if task_dir and is_safe_task_path(task_dir, repo_root):
170
+ task_dir_abs = repo_root / task_dir
171
+ if task_dir_abs.is_dir():
172
+ result = archive_task_complete(task_dir_abs, repo_root)
173
+ if "archived_to" in result:
174
+ dest = Path(result["archived_to"])
175
+ log_success(
176
+ f"Archived task: {dest.name} -> archive/{dest.parent.name}/"
177
+ )
178
+ else:
179
+ log_warn("Invalid task_dir in registry, skipping archive")
180
+
181
+ # Remove from registry
182
+ registry_remove_by_id(agent_id, repo_root)
183
+ log_success(f"Removed from registry: {agent_id}")
184
+
185
+ log_success("Cleanup complete")
186
+ return 0
187
+
188
+
189
+ def cleanup_worktree(
190
+ branch: str, repo_root: Path, skip_confirm: bool, keep_branch: bool
191
+ ) -> int:
192
+ """Cleanup single worktree."""
193
+ # Find worktree path for branch
194
+ _, worktree_list, _ = _run_git_command(
195
+ ["worktree", "list", "--porcelain"], cwd=repo_root
196
+ )
197
+
198
+ worktree_path = None
199
+ current_worktree = None
200
+
201
+ for line in worktree_list.splitlines():
202
+ if line.startswith("worktree "):
203
+ current_worktree = line[9:] # Remove "worktree " prefix
204
+ elif line.startswith("branch refs/heads/"):
205
+ current_branch = line[18:] # Remove "branch refs/heads/" prefix
206
+ if current_branch == branch:
207
+ worktree_path = current_worktree
208
+ break
209
+
210
+ if not worktree_path:
211
+ # No worktree found, try to cleanup from registry only
212
+ log_warn(f"No worktree found for: {branch}")
213
+ log_info("Trying to cleanup from registry...")
214
+ return cleanup_registry_only(branch, repo_root, skip_confirm)
215
+
216
+ print()
217
+ print(f"{Colors.BLUE}=== Cleanup Worktree ==={Colors.NC}")
218
+ print(f" Branch: {branch}")
219
+ print(f" Worktree: {worktree_path}")
220
+ print()
221
+
222
+ if not confirm("Remove this worktree?", skip_confirm):
223
+ log_info("Aborted")
224
+ return 0
225
+
226
+ # 1. Archive task
227
+ archive_task(worktree_path, repo_root)
228
+
229
+ # 2. Remove from registry
230
+ registry_remove_by_worktree(worktree_path, repo_root)
231
+ log_info("Removed from registry")
232
+
233
+ # 3. Remove worktree
234
+ log_info("Removing worktree...")
235
+ ret, _, _ = _run_git_command(
236
+ ["worktree", "remove", worktree_path, "--force"], cwd=repo_root
237
+ )
238
+ if ret != 0:
239
+ # Try removing directory manually
240
+ try:
241
+ shutil.rmtree(worktree_path)
242
+ except Exception as e:
243
+ log_error(f"Failed to remove worktree: {e}")
244
+
245
+ log_success("Worktree removed")
246
+
247
+ # 4. Delete branch (optional)
248
+ if not keep_branch:
249
+ log_info("Deleting branch...")
250
+ ret, _, _ = _run_git_command(["branch", "-D", branch], cwd=repo_root)
251
+ if ret != 0:
252
+ log_warn("Could not delete branch (may be checked out elsewhere)")
253
+
254
+ log_success(f"Cleanup complete for: {branch}")
255
+ return 0
256
+
257
+
258
+ def cmd_merged(repo_root: Path, skip_confirm: bool, keep_branch: bool) -> int:
259
+ """Cleanup merged worktrees."""
260
+ # Get main branch
261
+ _, head_out, _ = _run_git_command(
262
+ ["symbolic-ref", "refs/remotes/origin/HEAD"], cwd=repo_root
263
+ )
264
+ main_branch = head_out.strip().replace("refs/remotes/origin/", "") or "main"
265
+
266
+ print(f"{Colors.BLUE}=== Finding Merged Worktrees ==={Colors.NC}")
267
+ print()
268
+
269
+ # Get merged branches
270
+ _, merged_out, _ = _run_git_command(
271
+ ["branch", "--merged", main_branch], cwd=repo_root
272
+ )
273
+ merged_branches = []
274
+ for line in merged_out.splitlines():
275
+ branch = line.strip().lstrip("* ")
276
+ if branch and branch != main_branch:
277
+ merged_branches.append(branch)
278
+
279
+ if not merged_branches:
280
+ log_info("No merged branches found")
281
+ return 0
282
+
283
+ # Get worktree list
284
+ _, worktree_list, _ = _run_git_command(["worktree", "list"], cwd=repo_root)
285
+
286
+ worktree_branches = []
287
+ for branch in merged_branches:
288
+ if f"[{branch}]" in worktree_list:
289
+ worktree_branches.append(branch)
290
+ print(f" - {branch}")
291
+
292
+ if not worktree_branches:
293
+ log_info("No merged worktrees found")
294
+ return 0
295
+
296
+ print()
297
+ if not confirm("Remove these merged worktrees?", skip_confirm):
298
+ log_info("Aborted")
299
+ return 0
300
+
301
+ for branch in worktree_branches:
302
+ cleanup_worktree(branch, repo_root, True, keep_branch)
303
+
304
+ return 0
305
+
306
+
307
+ def cmd_all(repo_root: Path, skip_confirm: bool, keep_branch: bool) -> int:
308
+ """Cleanup all worktrees."""
309
+ print(f"{Colors.BLUE}=== All Worktrees ==={Colors.NC}")
310
+ print()
311
+
312
+ # Get worktree list
313
+ _, worktree_list, _ = _run_git_command(
314
+ ["worktree", "list", "--porcelain"], cwd=repo_root
315
+ )
316
+
317
+ worktrees = []
318
+ main_worktree = str(repo_root.resolve())
319
+
320
+ for line in worktree_list.splitlines():
321
+ if line.startswith("worktree "):
322
+ wt = line[9:]
323
+ if wt != main_worktree:
324
+ worktrees.append(wt)
325
+
326
+ if not worktrees:
327
+ log_info("No worktrees to remove")
328
+ return 0
329
+
330
+ for wt in worktrees:
331
+ print(f" - {wt}")
332
+
333
+ print()
334
+ print(f"{Colors.RED}WARNING: This will remove ALL worktrees!{Colors.NC}")
335
+
336
+ if not confirm("Are you sure?", skip_confirm):
337
+ log_info("Aborted")
338
+ return 0
339
+
340
+ # Get branch for each worktree
341
+ for wt in worktrees:
342
+ # Find branch name from worktree list
343
+ _, wt_list, _ = _run_git_command(["worktree", "list"], cwd=repo_root)
344
+ for line in wt_list.splitlines():
345
+ if wt in line:
346
+ # Extract branch from [branch] format
347
+ import re
348
+
349
+ match = re.search(r"\[([^\]]+)\]", line)
350
+ if match:
351
+ branch = match.group(1)
352
+ cleanup_worktree(branch, repo_root, True, keep_branch)
353
+ break
354
+
355
+ return 0
356
+
357
+
358
+ # =============================================================================
359
+ # Main
360
+ # =============================================================================
361
+
362
+
363
+ def main() -> int:
364
+ """Main entry point."""
365
+ parser = argparse.ArgumentParser(
366
+ description="Multi-Agent Pipeline: Cleanup Worktree"
367
+ )
368
+ parser.add_argument("branch", nargs="?", help="Branch name to cleanup")
369
+ parser.add_argument("-y", "--yes", action="store_true", help="Skip confirmation")
370
+ parser.add_argument(
371
+ "--keep-branch", action="store_true", help="Don't delete git branch"
372
+ )
373
+ parser.add_argument("--list", action="store_true", help="List all worktrees")
374
+ parser.add_argument("--merged", action="store_true", help="Remove merged worktrees")
375
+ parser.add_argument("--all", action="store_true", help="Remove all worktrees")
376
+
377
+ args = parser.parse_args()
378
+ repo_root = get_repo_root()
379
+
380
+ if args.list:
381
+ return cmd_list(repo_root)
382
+ elif args.merged:
383
+ return cmd_merged(repo_root, args.yes, args.keep_branch)
384
+ elif args.all:
385
+ return cmd_all(repo_root, args.yes, args.keep_branch)
386
+ elif args.branch:
387
+ return cleanup_worktree(args.branch, repo_root, args.yes, args.keep_branch)
388
+ else:
389
+ print("""Usage:
390
+ python3 cleanup.py <branch-name> Remove specific worktree
391
+ python3 cleanup.py --list List all worktrees
392
+ python3 cleanup.py --merged Remove merged worktrees
393
+ python3 cleanup.py --all Remove all worktrees
394
+
395
+ Options:
396
+ -y, --yes Skip confirmation
397
+ --keep-branch Don't delete git branch
398
+ """)
399
+ return 1
400
+
401
+
402
+ if __name__ == "__main__":
403
+ sys.exit(main())