@blxzer/pactile 0.5.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 (903) hide show
  1. package/CHANGELOG.md +602 -0
  2. package/LICENSE +235 -0
  3. package/README.md +115 -0
  4. package/README.zh-CN.md +115 -0
  5. package/bin/compat-warning.js +11 -0
  6. package/bin/cstl.js +6 -0
  7. package/bin/pactile.js +3 -0
  8. package/bin/smart-search.js +38 -0
  9. package/dist/cli/index.d.ts +3 -0
  10. package/dist/cli/index.d.ts.map +1 -0
  11. package/dist/cli/index.js +362 -0
  12. package/dist/cli/index.js.map +1 -0
  13. package/dist/commands/capability-smoke.d.ts +24 -0
  14. package/dist/commands/capability-smoke.d.ts.map +1 -0
  15. package/dist/commands/capability-smoke.js +83 -0
  16. package/dist/commands/capability-smoke.js.map +1 -0
  17. package/dist/commands/channel/adapters/claude.d.ts +29 -0
  18. package/dist/commands/channel/adapters/claude.d.ts.map +1 -0
  19. package/dist/commands/channel/adapters/claude.js +203 -0
  20. package/dist/commands/channel/adapters/claude.js.map +1 -0
  21. package/dist/commands/channel/adapters/codex.d.ts +85 -0
  22. package/dist/commands/channel/adapters/codex.d.ts.map +1 -0
  23. package/dist/commands/channel/adapters/codex.js +505 -0
  24. package/dist/commands/channel/adapters/codex.js.map +1 -0
  25. package/dist/commands/channel/adapters/index.d.ts +84 -0
  26. package/dist/commands/channel/adapters/index.d.ts.map +1 -0
  27. package/dist/commands/channel/adapters/index.js +115 -0
  28. package/dist/commands/channel/adapters/index.js.map +1 -0
  29. package/dist/commands/channel/adapters/types.d.ts +33 -0
  30. package/dist/commands/channel/adapters/types.d.ts.map +1 -0
  31. package/dist/commands/channel/adapters/types.js +2 -0
  32. package/dist/commands/channel/adapters/types.js.map +1 -0
  33. package/dist/commands/channel/agent-loader.d.ts +32 -0
  34. package/dist/commands/channel/agent-loader.d.ts.map +1 -0
  35. package/dist/commands/channel/agent-loader.js +154 -0
  36. package/dist/commands/channel/agent-loader.js.map +1 -0
  37. package/dist/commands/channel/context-loader.d.ts +26 -0
  38. package/dist/commands/channel/context-loader.d.ts.map +1 -0
  39. package/dist/commands/channel/context-loader.js +290 -0
  40. package/dist/commands/channel/context-loader.js.map +1 -0
  41. package/dist/commands/channel/context.d.ts +16 -0
  42. package/dist/commands/channel/context.d.ts.map +1 -0
  43. package/dist/commands/channel/context.js +83 -0
  44. package/dist/commands/channel/context.js.map +1 -0
  45. package/dist/commands/channel/create.d.ts +27 -0
  46. package/dist/commands/channel/create.d.ts.map +1 -0
  47. package/dist/commands/channel/create.js +39 -0
  48. package/dist/commands/channel/create.js.map +1 -0
  49. package/dist/commands/channel/dev-parse-trace.d.ts +14 -0
  50. package/dist/commands/channel/dev-parse-trace.d.ts.map +1 -0
  51. package/dist/commands/channel/dev-parse-trace.js +70 -0
  52. package/dist/commands/channel/dev-parse-trace.js.map +1 -0
  53. package/dist/commands/channel/guard.d.ts +150 -0
  54. package/dist/commands/channel/guard.d.ts.map +1 -0
  55. package/dist/commands/channel/guard.js +475 -0
  56. package/dist/commands/channel/guard.js.map +1 -0
  57. package/dist/commands/channel/index.d.ts +3 -0
  58. package/dist/commands/channel/index.d.ts.map +1 -0
  59. package/dist/commands/channel/index.js +531 -0
  60. package/dist/commands/channel/index.js.map +1 -0
  61. package/dist/commands/channel/interrupt.d.ts +10 -0
  62. package/dist/commands/channel/interrupt.d.ts.map +1 -0
  63. package/dist/commands/channel/interrupt.js +22 -0
  64. package/dist/commands/channel/interrupt.js.map +1 -0
  65. package/dist/commands/channel/kill.d.ts +7 -0
  66. package/dist/commands/channel/kill.d.ts.map +1 -0
  67. package/dist/commands/channel/kill.js +121 -0
  68. package/dist/commands/channel/kill.js.map +1 -0
  69. package/dist/commands/channel/list.d.ts +17 -0
  70. package/dist/commands/channel/list.d.ts.map +1 -0
  71. package/dist/commands/channel/list.js +233 -0
  72. package/dist/commands/channel/list.js.map +1 -0
  73. package/dist/commands/channel/messages.d.ts +15 -0
  74. package/dist/commands/channel/messages.d.ts.map +1 -0
  75. package/dist/commands/channel/messages.js +245 -0
  76. package/dist/commands/channel/messages.js.map +1 -0
  77. package/dist/commands/channel/rm.d.ts +27 -0
  78. package/dist/commands/channel/rm.d.ts.map +1 -0
  79. package/dist/commands/channel/rm.js +216 -0
  80. package/dist/commands/channel/rm.js.map +1 -0
  81. package/dist/commands/channel/run.d.ts +30 -0
  82. package/dist/commands/channel/run.d.ts.map +1 -0
  83. package/dist/commands/channel/run.js +130 -0
  84. package/dist/commands/channel/run.js.map +1 -0
  85. package/dist/commands/channel/send.d.ts +11 -0
  86. package/dist/commands/channel/send.d.ts.map +1 -0
  87. package/dist/commands/channel/send.js +24 -0
  88. package/dist/commands/channel/send.js.map +1 -0
  89. package/dist/commands/channel/spawn.d.ts +40 -0
  90. package/dist/commands/channel/spawn.d.ts.map +1 -0
  91. package/dist/commands/channel/spawn.js +242 -0
  92. package/dist/commands/channel/spawn.js.map +1 -0
  93. package/dist/commands/channel/store/events.d.ts +39 -0
  94. package/dist/commands/channel/store/events.d.ts.map +1 -0
  95. package/dist/commands/channel/store/events.js +87 -0
  96. package/dist/commands/channel/store/events.js.map +1 -0
  97. package/dist/commands/channel/store/filter.d.ts +3 -0
  98. package/dist/commands/channel/store/filter.d.ts.map +1 -0
  99. package/dist/commands/channel/store/filter.js +2 -0
  100. package/dist/commands/channel/store/filter.js.map +1 -0
  101. package/dist/commands/channel/store/lock.d.ts +23 -0
  102. package/dist/commands/channel/store/lock.d.ts.map +1 -0
  103. package/dist/commands/channel/store/lock.js +99 -0
  104. package/dist/commands/channel/store/lock.js.map +1 -0
  105. package/dist/commands/channel/store/paths.d.ts +63 -0
  106. package/dist/commands/channel/store/paths.d.ts.map +1 -0
  107. package/dist/commands/channel/store/paths.js +247 -0
  108. package/dist/commands/channel/store/paths.js.map +1 -0
  109. package/dist/commands/channel/store/schema.d.ts +27 -0
  110. package/dist/commands/channel/store/schema.d.ts.map +1 -0
  111. package/dist/commands/channel/store/schema.js +34 -0
  112. package/dist/commands/channel/store/schema.js.map +1 -0
  113. package/dist/commands/channel/store/thread-state.d.ts +5 -0
  114. package/dist/commands/channel/store/thread-state.d.ts.map +1 -0
  115. package/dist/commands/channel/store/thread-state.js +16 -0
  116. package/dist/commands/channel/store/thread-state.js.map +1 -0
  117. package/dist/commands/channel/store/watch.d.ts +19 -0
  118. package/dist/commands/channel/store/watch.d.ts.map +1 -0
  119. package/dist/commands/channel/store/watch.js +146 -0
  120. package/dist/commands/channel/store/watch.js.map +1 -0
  121. package/dist/commands/channel/supervisor/idle.d.ts +46 -0
  122. package/dist/commands/channel/supervisor/idle.d.ts.map +1 -0
  123. package/dist/commands/channel/supervisor/idle.js +72 -0
  124. package/dist/commands/channel/supervisor/idle.js.map +1 -0
  125. package/dist/commands/channel/supervisor/inbox.d.ts +30 -0
  126. package/dist/commands/channel/supervisor/inbox.d.ts.map +1 -0
  127. package/dist/commands/channel/supervisor/inbox.js +160 -0
  128. package/dist/commands/channel/supervisor/inbox.js.map +1 -0
  129. package/dist/commands/channel/supervisor/shutdown.d.ts +68 -0
  130. package/dist/commands/channel/supervisor/shutdown.d.ts.map +1 -0
  131. package/dist/commands/channel/supervisor/shutdown.js +146 -0
  132. package/dist/commands/channel/supervisor/shutdown.js.map +1 -0
  133. package/dist/commands/channel/supervisor/stdout.d.ts +51 -0
  134. package/dist/commands/channel/supervisor/stdout.d.ts.map +1 -0
  135. package/dist/commands/channel/supervisor/stdout.js +121 -0
  136. package/dist/commands/channel/supervisor/stdout.js.map +1 -0
  137. package/dist/commands/channel/supervisor/turns.d.ts +31 -0
  138. package/dist/commands/channel/supervisor/turns.d.ts.map +1 -0
  139. package/dist/commands/channel/supervisor/turns.js +45 -0
  140. package/dist/commands/channel/supervisor/turns.js.map +1 -0
  141. package/dist/commands/channel/supervisor/warning.d.ts +48 -0
  142. package/dist/commands/channel/supervisor/warning.d.ts.map +1 -0
  143. package/dist/commands/channel/supervisor/warning.js +77 -0
  144. package/dist/commands/channel/supervisor/warning.js.map +1 -0
  145. package/dist/commands/channel/supervisor.d.ts +59 -0
  146. package/dist/commands/channel/supervisor.d.ts.map +1 -0
  147. package/dist/commands/channel/supervisor.js +345 -0
  148. package/dist/commands/channel/supervisor.js.map +1 -0
  149. package/dist/commands/channel/text-body.d.ts +13 -0
  150. package/dist/commands/channel/text-body.d.ts.map +1 -0
  151. package/dist/commands/channel/text-body.js +47 -0
  152. package/dist/commands/channel/text-body.js.map +1 -0
  153. package/dist/commands/channel/threads.d.ts +39 -0
  154. package/dist/commands/channel/threads.d.ts.map +1 -0
  155. package/dist/commands/channel/threads.js +106 -0
  156. package/dist/commands/channel/threads.js.map +1 -0
  157. package/dist/commands/channel/title.d.ts +12 -0
  158. package/dist/commands/channel/title.d.ts.map +1 -0
  159. package/dist/commands/channel/title.js +24 -0
  160. package/dist/commands/channel/title.js.map +1 -0
  161. package/dist/commands/channel/wait.d.ts +17 -0
  162. package/dist/commands/channel/wait.d.ts.map +1 -0
  163. package/dist/commands/channel/wait.js +75 -0
  164. package/dist/commands/channel/wait.js.map +1 -0
  165. package/dist/commands/detach.d.ts +7 -0
  166. package/dist/commands/detach.d.ts.map +1 -0
  167. package/dist/commands/detach.js +31 -0
  168. package/dist/commands/detach.js.map +1 -0
  169. package/dist/commands/init.d.ts +51 -0
  170. package/dist/commands/init.d.ts.map +1 -0
  171. package/dist/commands/init.js +1660 -0
  172. package/dist/commands/init.js.map +1 -0
  173. package/dist/commands/migrate.d.ts +10 -0
  174. package/dist/commands/migrate.d.ts.map +1 -0
  175. package/dist/commands/migrate.js +38 -0
  176. package/dist/commands/migrate.js.map +1 -0
  177. package/dist/commands/purge.d.ts +9 -0
  178. package/dist/commands/purge.d.ts.map +1 -0
  179. package/dist/commands/purge.js +29 -0
  180. package/dist/commands/purge.js.map +1 -0
  181. package/dist/commands/rollback.d.ts +7 -0
  182. package/dist/commands/rollback.d.ts.map +1 -0
  183. package/dist/commands/rollback.js +27 -0
  184. package/dist/commands/rollback.js.map +1 -0
  185. package/dist/commands/rollout.d.ts +34 -0
  186. package/dist/commands/rollout.d.ts.map +1 -0
  187. package/dist/commands/rollout.js +102 -0
  188. package/dist/commands/rollout.js.map +1 -0
  189. package/dist/commands/uninstall.d.ts +23 -0
  190. package/dist/commands/uninstall.d.ts.map +1 -0
  191. package/dist/commands/uninstall.js +68 -0
  192. package/dist/commands/uninstall.js.map +1 -0
  193. package/dist/commands/update.d.ts +139 -0
  194. package/dist/commands/update.d.ts.map +1 -0
  195. package/dist/commands/update.js +2459 -0
  196. package/dist/commands/update.js.map +1 -0
  197. package/dist/commands/upgrade.d.ts +28 -0
  198. package/dist/commands/upgrade.d.ts.map +1 -0
  199. package/dist/commands/upgrade.js +84 -0
  200. package/dist/commands/upgrade.js.map +1 -0
  201. package/dist/commands/validate-rules.d.ts +6 -0
  202. package/dist/commands/validate-rules.d.ts.map +1 -0
  203. package/dist/commands/validate-rules.js +33 -0
  204. package/dist/commands/validate-rules.js.map +1 -0
  205. package/dist/commands/workflow.d.ts +35 -0
  206. package/dist/commands/workflow.d.ts.map +1 -0
  207. package/dist/commands/workflow.js +220 -0
  208. package/dist/commands/workflow.js.map +1 -0
  209. package/dist/configurators/cursor.d.ts +21 -0
  210. package/dist/configurators/cursor.d.ts.map +1 -0
  211. package/dist/configurators/cursor.js +51 -0
  212. package/dist/configurators/cursor.js.map +1 -0
  213. package/dist/configurators/index.d.ts +68 -0
  214. package/dist/configurators/index.d.ts.map +1 -0
  215. package/dist/configurators/index.js +170 -0
  216. package/dist/configurators/index.js.map +1 -0
  217. package/dist/configurators/shared.d.ts +200 -0
  218. package/dist/configurators/shared.d.ts.map +1 -0
  219. package/dist/configurators/shared.js +602 -0
  220. package/dist/configurators/shared.js.map +1 -0
  221. package/dist/configurators/workflow.d.ts +45 -0
  222. package/dist/configurators/workflow.d.ts.map +1 -0
  223. package/dist/configurators/workflow.js +256 -0
  224. package/dist/configurators/workflow.js.map +1 -0
  225. package/dist/constants/paths.d.ts +93 -0
  226. package/dist/constants/paths.d.ts.map +1 -0
  227. package/dist/constants/paths.js +106 -0
  228. package/dist/constants/paths.js.map +1 -0
  229. package/dist/constants/version.d.ts +9 -0
  230. package/dist/constants/version.d.ts.map +1 -0
  231. package/dist/constants/version.js +15 -0
  232. package/dist/constants/version.js.map +1 -0
  233. package/dist/index.d.ts +10 -0
  234. package/dist/index.d.ts.map +1 -0
  235. package/dist/index.js +10 -0
  236. package/dist/index.js.map +1 -0
  237. package/dist/migrations/index.d.ts +62 -0
  238. package/dist/migrations/index.d.ts.map +1 -0
  239. package/dist/migrations/index.js +187 -0
  240. package/dist/migrations/index.js.map +1 -0
  241. package/dist/migrations/manifests/0.1.0.json +9 -0
  242. package/dist/migrations/manifests/0.1.1.json +9 -0
  243. package/dist/migrations/manifests/0.1.2.json +9 -0
  244. package/dist/migrations/manifests/0.1.3.json +9 -0
  245. package/dist/migrations/manifests/0.1.4.json +9 -0
  246. package/dist/migrations/manifests/0.2.1.json +9 -0
  247. package/dist/migrations/manifests/0.2.10.json +374 -0
  248. package/dist/migrations/manifests/0.2.2.json +9 -0
  249. package/dist/migrations/manifests/0.2.3.json +9 -0
  250. package/dist/migrations/manifests/0.2.4.json +9 -0
  251. package/dist/migrations/manifests/0.2.5.json +9 -0
  252. package/dist/migrations/manifests/0.2.6.json +9 -0
  253. package/dist/migrations/manifests/0.2.7.json +9 -0
  254. package/dist/migrations/manifests/0.2.8.json +9 -0
  255. package/dist/migrations/manifests/0.2.9.json +9 -0
  256. package/dist/migrations/manifests/0.3.0.json +89 -0
  257. package/dist/migrations/manifests/0.3.1.json +32 -0
  258. package/dist/migrations/manifests/0.3.2.json +16 -0
  259. package/dist/migrations/manifests/0.3.3.json +9 -0
  260. package/dist/migrations/manifests/0.3.4.json +9 -0
  261. package/dist/migrations/manifests/0.3.5.json +9 -0
  262. package/dist/migrations/manifests/0.3.6.json +9 -0
  263. package/dist/migrations/manifests/0.4.0.json +9 -0
  264. package/dist/migrations/manifests/0.4.1.json +9 -0
  265. package/dist/migrations/manifests/0.4.2.json +9 -0
  266. package/dist/migrations/manifests/0.4.3.json +9 -0
  267. package/dist/migrations/manifests/0.5.0-beta.0.json +9 -0
  268. package/dist/migrations/manifests/0.5.0-beta.1.json +9 -0
  269. package/dist/migrations/manifests/0.5.0-beta.2.json +9 -0
  270. package/dist/migrations/manifests/0.5.0-beta.3.json +9 -0
  271. package/dist/migrations/manifests/0.5.0-beta.4.json +9 -0
  272. package/dist/migrations/manifests/0.5.0-beta.5.json +9 -0
  273. package/dist/pactile/adapters/codex/index.d.ts +75 -0
  274. package/dist/pactile/adapters/codex/index.d.ts.map +1 -0
  275. package/dist/pactile/adapters/codex/index.js +269 -0
  276. package/dist/pactile/adapters/codex/index.js.map +1 -0
  277. package/dist/pactile/adapters/cursor/index.d.ts +60 -0
  278. package/dist/pactile/adapters/cursor/index.d.ts.map +1 -0
  279. package/dist/pactile/adapters/cursor/index.js +186 -0
  280. package/dist/pactile/adapters/cursor/index.js.map +1 -0
  281. package/dist/pactile/adapters/index.d.ts +4 -0
  282. package/dist/pactile/adapters/index.d.ts.map +1 -0
  283. package/dist/pactile/adapters/index.js +4 -0
  284. package/dist/pactile/adapters/index.js.map +1 -0
  285. package/dist/pactile/adoption/bindings.d.ts +20 -0
  286. package/dist/pactile/adoption/bindings.d.ts.map +1 -0
  287. package/dist/pactile/adoption/bindings.js +99 -0
  288. package/dist/pactile/adoption/bindings.js.map +1 -0
  289. package/dist/pactile/adoption/discovery.d.ts +8 -0
  290. package/dist/pactile/adoption/discovery.d.ts.map +1 -0
  291. package/dist/pactile/adoption/discovery.js +159 -0
  292. package/dist/pactile/adoption/discovery.js.map +1 -0
  293. package/dist/pactile/adoption/inventory.d.ts +28 -0
  294. package/dist/pactile/adoption/inventory.d.ts.map +1 -0
  295. package/dist/pactile/adoption/inventory.js +261 -0
  296. package/dist/pactile/adoption/inventory.js.map +1 -0
  297. package/dist/pactile/adoption/safety.d.ts +19 -0
  298. package/dist/pactile/adoption/safety.d.ts.map +1 -0
  299. package/dist/pactile/adoption/safety.js +88 -0
  300. package/dist/pactile/adoption/safety.js.map +1 -0
  301. package/dist/pactile/adoption/workflow.d.ts +31 -0
  302. package/dist/pactile/adoption/workflow.d.ts.map +1 -0
  303. package/dist/pactile/adoption/workflow.js +259 -0
  304. package/dist/pactile/adoption/workflow.js.map +1 -0
  305. package/dist/pactile/compat/cli-options.d.ts +9 -0
  306. package/dist/pactile/compat/cli-options.d.ts.map +1 -0
  307. package/dist/pactile/compat/cli-options.js +9 -0
  308. package/dist/pactile/compat/cli-options.js.map +1 -0
  309. package/dist/pactile/compat/init-context.d.ts +16 -0
  310. package/dist/pactile/compat/init-context.d.ts.map +1 -0
  311. package/dist/pactile/compat/init-context.js +87 -0
  312. package/dist/pactile/compat/init-context.js.map +1 -0
  313. package/dist/pactile/compat/legacy-migrations.d.ts +8 -0
  314. package/dist/pactile/compat/legacy-migrations.d.ts.map +1 -0
  315. package/dist/pactile/compat/legacy-migrations.js +17 -0
  316. package/dist/pactile/compat/legacy-migrations.js.map +1 -0
  317. package/dist/pactile/compat/retired-alternate-client.d.ts +42 -0
  318. package/dist/pactile/compat/retired-alternate-client.d.ts.map +1 -0
  319. package/dist/pactile/compat/retired-alternate-client.js +153 -0
  320. package/dist/pactile/compat/retired-alternate-client.js.map +1 -0
  321. package/dist/pactile/compat/update-residue.d.ts +4 -0
  322. package/dist/pactile/compat/update-residue.d.ts.map +1 -0
  323. package/dist/pactile/compat/update-residue.js +55 -0
  324. package/dist/pactile/compat/update-residue.js.map +1 -0
  325. package/dist/pactile/exit/index.d.ts +3 -0
  326. package/dist/pactile/exit/index.d.ts.map +1 -0
  327. package/dist/pactile/exit/index.js +3 -0
  328. package/dist/pactile/exit/index.js.map +1 -0
  329. package/dist/pactile/exit/receipt.d.ts +23 -0
  330. package/dist/pactile/exit/receipt.d.ts.map +1 -0
  331. package/dist/pactile/exit/receipt.js +64 -0
  332. package/dist/pactile/exit/receipt.js.map +1 -0
  333. package/dist/pactile/exit/service.d.ts +107 -0
  334. package/dist/pactile/exit/service.d.ts.map +1 -0
  335. package/dist/pactile/exit/service.js +774 -0
  336. package/dist/pactile/exit/service.js.map +1 -0
  337. package/dist/pactile/index.d.ts +26 -0
  338. package/dist/pactile/index.d.ts.map +1 -0
  339. package/dist/pactile/index.js +22 -0
  340. package/dist/pactile/index.js.map +1 -0
  341. package/dist/pactile/lifecycle/command-runner.d.ts +26 -0
  342. package/dist/pactile/lifecycle/command-runner.d.ts.map +1 -0
  343. package/dist/pactile/lifecycle/command-runner.js +129 -0
  344. package/dist/pactile/lifecycle/command-runner.js.map +1 -0
  345. package/dist/pactile/lifecycle/default-adapters.d.ts +5 -0
  346. package/dist/pactile/lifecycle/default-adapters.d.ts.map +1 -0
  347. package/dist/pactile/lifecycle/default-adapters.js +136 -0
  348. package/dist/pactile/lifecycle/default-adapters.js.map +1 -0
  349. package/dist/pactile/lifecycle/index.d.ts +6 -0
  350. package/dist/pactile/lifecycle/index.d.ts.map +1 -0
  351. package/dist/pactile/lifecycle/index.js +6 -0
  352. package/dist/pactile/lifecycle/index.js.map +1 -0
  353. package/dist/pactile/lifecycle/legacy-import.d.ts +16 -0
  354. package/dist/pactile/lifecycle/legacy-import.d.ts.map +1 -0
  355. package/dist/pactile/lifecycle/legacy-import.js +228 -0
  356. package/dist/pactile/lifecycle/legacy-import.js.map +1 -0
  357. package/dist/pactile/lifecycle/orchestrator.d.ts +109 -0
  358. package/dist/pactile/lifecycle/orchestrator.d.ts.map +1 -0
  359. package/dist/pactile/lifecycle/orchestrator.js +1030 -0
  360. package/dist/pactile/lifecycle/orchestrator.js.map +1 -0
  361. package/dist/pactile/lifecycle/project-files.d.ts +28 -0
  362. package/dist/pactile/lifecycle/project-files.d.ts.map +1 -0
  363. package/dist/pactile/lifecycle/project-files.js +297 -0
  364. package/dist/pactile/lifecycle/project-files.js.map +1 -0
  365. package/dist/pactile/middleware/index.d.ts +3 -0
  366. package/dist/pactile/middleware/index.d.ts.map +1 -0
  367. package/dist/pactile/middleware/index.js +2 -0
  368. package/dist/pactile/middleware/index.js.map +1 -0
  369. package/dist/pactile/middleware/manifest-loader.d.ts +22 -0
  370. package/dist/pactile/middleware/manifest-loader.d.ts.map +1 -0
  371. package/dist/pactile/middleware/manifest-loader.js +228 -0
  372. package/dist/pactile/middleware/manifest-loader.js.map +1 -0
  373. package/dist/pactile/migration/index.d.ts +2 -0
  374. package/dist/pactile/migration/index.d.ts.map +1 -0
  375. package/dist/pactile/migration/index.js +2 -0
  376. package/dist/pactile/migration/index.js.map +1 -0
  377. package/dist/pactile/migration/transaction.d.ts +54 -0
  378. package/dist/pactile/migration/transaction.d.ts.map +1 -0
  379. package/dist/pactile/migration/transaction.js +445 -0
  380. package/dist/pactile/migration/transaction.js.map +1 -0
  381. package/dist/pactile/projection/managed-block.d.ts +32 -0
  382. package/dist/pactile/projection/managed-block.d.ts.map +1 -0
  383. package/dist/pactile/projection/managed-block.js +78 -0
  384. package/dist/pactile/projection/managed-block.js.map +1 -0
  385. package/dist/pactile/projection/planner.d.ts +65 -0
  386. package/dist/pactile/projection/planner.d.ts.map +1 -0
  387. package/dist/pactile/projection/planner.js +349 -0
  388. package/dist/pactile/projection/planner.js.map +1 -0
  389. package/dist/pactile/projection/shared/index.d.ts +38 -0
  390. package/dist/pactile/projection/shared/index.d.ts.map +1 -0
  391. package/dist/pactile/projection/shared/index.js +186 -0
  392. package/dist/pactile/projection/shared/index.js.map +1 -0
  393. package/dist/pactile/projection/store.d.ts +72 -0
  394. package/dist/pactile/projection/store.d.ts.map +1 -0
  395. package/dist/pactile/projection/store.js +669 -0
  396. package/dist/pactile/projection/store.js.map +1 -0
  397. package/dist/pactile/projection/structured-merge.d.ts +11 -0
  398. package/dist/pactile/projection/structured-merge.d.ts.map +1 -0
  399. package/dist/pactile/projection/structured-merge.js +347 -0
  400. package/dist/pactile/projection/structured-merge.js.map +1 -0
  401. package/dist/pactile/providers/index.d.ts +3 -0
  402. package/dist/pactile/providers/index.d.ts.map +1 -0
  403. package/dist/pactile/providers/index.js +3 -0
  404. package/dist/pactile/providers/index.js.map +1 -0
  405. package/dist/pactile/providers/probes.d.ts +55 -0
  406. package/dist/pactile/providers/probes.d.ts.map +1 -0
  407. package/dist/pactile/providers/probes.js +164 -0
  408. package/dist/pactile/providers/probes.js.map +1 -0
  409. package/dist/pactile/registry.d.ts +64 -0
  410. package/dist/pactile/registry.d.ts.map +1 -0
  411. package/dist/pactile/registry.js +196 -0
  412. package/dist/pactile/registry.js.map +1 -0
  413. package/dist/pactile/retrieval/boundary.d.ts +18 -0
  414. package/dist/pactile/retrieval/boundary.d.ts.map +1 -0
  415. package/dist/pactile/retrieval/boundary.js +113 -0
  416. package/dist/pactile/retrieval/boundary.js.map +1 -0
  417. package/dist/pactile/retrieval/evidence.d.ts +9 -0
  418. package/dist/pactile/retrieval/evidence.d.ts.map +1 -0
  419. package/dist/pactile/retrieval/evidence.js +327 -0
  420. package/dist/pactile/retrieval/evidence.js.map +1 -0
  421. package/dist/pactile/retrieval/index.d.ts +4 -0
  422. package/dist/pactile/retrieval/index.d.ts.map +1 -0
  423. package/dist/pactile/retrieval/index.js +4 -0
  424. package/dist/pactile/retrieval/index.js.map +1 -0
  425. package/dist/pactile/retrieval/planner.d.ts +23 -0
  426. package/dist/pactile/retrieval/planner.d.ts.map +1 -0
  427. package/dist/pactile/retrieval/planner.js +702 -0
  428. package/dist/pactile/retrieval/planner.js.map +1 -0
  429. package/dist/pactile/retrieval/types.d.ts +108 -0
  430. package/dist/pactile/retrieval/types.d.ts.map +1 -0
  431. package/dist/pactile/retrieval/types.js +36 -0
  432. package/dist/pactile/retrieval/types.js.map +1 -0
  433. package/dist/pactile/runtime/case-fold.d.ts +2 -0
  434. package/dist/pactile/runtime/case-fold.d.ts.map +1 -0
  435. package/dist/pactile/runtime/case-fold.js +31 -0
  436. package/dist/pactile/runtime/case-fold.js.map +1 -0
  437. package/dist/pactile/runtime/index.d.ts +8 -0
  438. package/dist/pactile/runtime/index.d.ts.map +1 -0
  439. package/dist/pactile/runtime/index.js +5 -0
  440. package/dist/pactile/runtime/index.js.map +1 -0
  441. package/dist/pactile/runtime/json-api.d.ts +15 -0
  442. package/dist/pactile/runtime/json-api.d.ts.map +1 -0
  443. package/dist/pactile/runtime/json-api.js +68 -0
  444. package/dist/pactile/runtime/json-api.js.map +1 -0
  445. package/dist/pactile/runtime/paths.d.ts +59 -0
  446. package/dist/pactile/runtime/paths.d.ts.map +1 -0
  447. package/dist/pactile/runtime/paths.js +201 -0
  448. package/dist/pactile/runtime/paths.js.map +1 -0
  449. package/dist/pactile/runtime/stores.d.ts +54 -0
  450. package/dist/pactile/runtime/stores.d.ts.map +1 -0
  451. package/dist/pactile/runtime/stores.js +375 -0
  452. package/dist/pactile/runtime/stores.js.map +1 -0
  453. package/dist/pactile/runtime/unicode-nfc-data.d.ts +12 -0
  454. package/dist/pactile/runtime/unicode-nfc-data.d.ts.map +1 -0
  455. package/dist/pactile/runtime/unicode-nfc-data.js +8 -0
  456. package/dist/pactile/runtime/unicode-nfc-data.js.map +1 -0
  457. package/dist/pactile/runtime/unicode-nfc.d.ts +3 -0
  458. package/dist/pactile/runtime/unicode-nfc.d.ts.map +1 -0
  459. package/dist/pactile/runtime/unicode-nfc.js +77 -0
  460. package/dist/pactile/runtime/unicode-nfc.js.map +1 -0
  461. package/dist/pactile/tiles/catalog.d.ts +35 -0
  462. package/dist/pactile/tiles/catalog.d.ts.map +1 -0
  463. package/dist/pactile/tiles/catalog.js +235 -0
  464. package/dist/pactile/tiles/catalog.js.map +1 -0
  465. package/dist/pactile/tiles/compiler.d.ts +54 -0
  466. package/dist/pactile/tiles/compiler.d.ts.map +1 -0
  467. package/dist/pactile/tiles/compiler.js +270 -0
  468. package/dist/pactile/tiles/compiler.js.map +1 -0
  469. package/dist/pactile/tiles/content/baseline/index.d.ts +5 -0
  470. package/dist/pactile/tiles/content/baseline/index.d.ts.map +1 -0
  471. package/dist/pactile/tiles/content/baseline/index.js +115 -0
  472. package/dist/pactile/tiles/content/baseline/index.js.map +1 -0
  473. package/dist/pactile/tiles/content/index.d.ts +4 -0
  474. package/dist/pactile/tiles/content/index.d.ts.map +1 -0
  475. package/dist/pactile/tiles/content/index.js +4 -0
  476. package/dist/pactile/tiles/content/index.js.map +1 -0
  477. package/dist/pactile/tiles/content/ondemand/index.d.ts +5 -0
  478. package/dist/pactile/tiles/content/ondemand/index.d.ts.map +1 -0
  479. package/dist/pactile/tiles/content/ondemand/index.js +184 -0
  480. package/dist/pactile/tiles/content/ondemand/index.js.map +1 -0
  481. package/dist/pactile/tiles/content/source.d.ts +29 -0
  482. package/dist/pactile/tiles/content/source.d.ts.map +1 -0
  483. package/dist/pactile/tiles/content/source.js +86 -0
  484. package/dist/pactile/tiles/content/source.js.map +1 -0
  485. package/dist/pactile/tiles/loader.d.ts +31 -0
  486. package/dist/pactile/tiles/loader.d.ts.map +1 -0
  487. package/dist/pactile/tiles/loader.js +139 -0
  488. package/dist/pactile/tiles/loader.js.map +1 -0
  489. package/dist/pactile/tiles/safe-yaml.d.ts +11 -0
  490. package/dist/pactile/tiles/safe-yaml.d.ts.map +1 -0
  491. package/dist/pactile/tiles/safe-yaml.js +212 -0
  492. package/dist/pactile/tiles/safe-yaml.js.map +1 -0
  493. package/dist/templates/common/bundled-skills/pactile-check/SKILL.md +161 -0
  494. package/dist/templates/common/bundled-skills/pactile-check/references/fowler-12-smells.md +28 -0
  495. package/dist/templates/common/bundled-skills/pactile-handoff/SKILL.md +117 -0
  496. package/dist/templates/common/bundled-skills/pactile-meta/SKILL.md +75 -0
  497. package/dist/templates/common/bundled-skills/pactile-meta/references/customize-local/add-project-local-conventions.md +83 -0
  498. package/dist/templates/common/bundled-skills/pactile-meta/references/customize-local/change-agents.md +47 -0
  499. package/dist/templates/common/bundled-skills/pactile-meta/references/customize-local/change-context-loading.md +84 -0
  500. package/dist/templates/common/bundled-skills/pactile-meta/references/customize-local/change-hooks.md +57 -0
  501. package/dist/templates/common/bundled-skills/pactile-meta/references/customize-local/change-skills-or-commands.md +77 -0
  502. package/dist/templates/common/bundled-skills/pactile-meta/references/customize-local/change-spec-structure.md +83 -0
  503. package/dist/templates/common/bundled-skills/pactile-meta/references/customize-local/change-task-lifecycle.md +88 -0
  504. package/dist/templates/common/bundled-skills/pactile-meta/references/customize-local/change-workflow.md +65 -0
  505. package/dist/templates/common/bundled-skills/pactile-meta/references/customize-local/overview.md +55 -0
  506. package/dist/templates/common/bundled-skills/pactile-meta/references/local-architecture/context-injection.md +68 -0
  507. package/dist/templates/common/bundled-skills/pactile-meta/references/local-architecture/generated-files.md +83 -0
  508. package/dist/templates/common/bundled-skills/pactile-meta/references/local-architecture/overview.md +51 -0
  509. package/dist/templates/common/bundled-skills/pactile-meta/references/local-architecture/spec-system.md +102 -0
  510. package/dist/templates/common/bundled-skills/pactile-meta/references/local-architecture/task-system.md +133 -0
  511. package/dist/templates/common/bundled-skills/pactile-meta/references/local-architecture/workflow.md +75 -0
  512. package/dist/templates/common/bundled-skills/pactile-meta/references/local-architecture/workspace-memory.md +71 -0
  513. package/dist/templates/common/bundled-skills/pactile-meta/references/platform-files/agents.md +69 -0
  514. package/dist/templates/common/bundled-skills/pactile-meta/references/platform-files/hooks-and-settings.md +59 -0
  515. package/dist/templates/common/bundled-skills/pactile-meta/references/platform-files/overview.md +45 -0
  516. package/dist/templates/common/bundled-skills/pactile-meta/references/platform-files/platform-map.md +45 -0
  517. package/dist/templates/common/bundled-skills/pactile-meta/references/platform-files/skills-and-commands.md +108 -0
  518. package/dist/templates/common/bundled-skills/pactile-micro-grill/SKILL.md +68 -0
  519. package/dist/templates/common/bundled-skills/pactile-skill-creator/SKILL.md +47 -0
  520. package/dist/templates/common/bundled-skills/pactile-skill-creator/references/authoring-rules.md +171 -0
  521. package/dist/templates/common/bundled-skills/pactile-skill-creator/references/general-authoring.md +189 -0
  522. package/dist/templates/common/bundled-skills/pactile-skill-creator/references/pactile-skill-locations.md +52 -0
  523. package/dist/templates/common/bundled-skills/pactile-skill-creator/references/review-checklist.md +52 -0
  524. package/dist/templates/common/bundled-skills/pactile-spec-bootstrap/SKILL.md +41 -0
  525. package/dist/templates/common/bundled-skills/pactile-spec-bootstrap/references/mcp-setup.md +90 -0
  526. package/dist/templates/common/bundled-skills/pactile-spec-bootstrap/references/repository-analysis.md +59 -0
  527. package/dist/templates/common/bundled-skills/pactile-spec-bootstrap/references/spec-task-planning.md +61 -0
  528. package/dist/templates/common/bundled-skills/pactile-spec-bootstrap/references/spec-writing.md +70 -0
  529. package/dist/templates/common/bundled-skills/smart-search-cli/SKILL.md +393 -0
  530. package/dist/templates/common/bundled-skills/smart-search-cli/agents/openai.yaml +3 -0
  531. package/dist/templates/common/bundled-skills/smart-search-cli/examples/batch-search.md +98 -0
  532. package/dist/templates/common/bundled-skills/smart-search-cli/examples/evidence-gathering.md +89 -0
  533. package/dist/templates/common/bundled-skills/smart-search-cli/references/cli-contract.md +323 -0
  534. package/dist/templates/common/commands/continue.md +62 -0
  535. package/dist/templates/common/commands/finish-work.md +82 -0
  536. package/dist/templates/common/commands/start.md +66 -0
  537. package/dist/templates/common/index.d.ts +68 -0
  538. package/dist/templates/common/index.d.ts.map +1 -0
  539. package/dist/templates/common/index.js +123 -0
  540. package/dist/templates/common/index.js.map +1 -0
  541. package/dist/templates/common/optional-skills/chrome-cdp/SKILL.md +179 -0
  542. package/dist/templates/common/optional-skills/chrome-cdp/examples/fetch-hook-api-capture.md +149 -0
  543. package/dist/templates/common/optional-skills/chrome-cdp/scripts/cdp.mjs +904 -0
  544. package/dist/templates/common/skills/before-dev.md +35 -0
  545. package/dist/templates/common/skills/brainstorm.md +204 -0
  546. package/dist/templates/common/skills/break-loop.md +125 -0
  547. package/dist/templates/common/skills/update-spec.md +364 -0
  548. package/dist/templates/cursor/agents/pactile-check.md +58 -0
  549. package/dist/templates/cursor/agents/pactile-implement.md +56 -0
  550. package/dist/templates/cursor/agents/pactile-research.md +51 -0
  551. package/dist/templates/cursor/commands/handoff.md +101 -0
  552. package/dist/templates/cursor/fixtures/expected-rules.d.ts +14 -0
  553. package/dist/templates/cursor/fixtures/expected-rules.d.ts.map +1 -0
  554. package/dist/templates/cursor/fixtures/expected-rules.js +20 -0
  555. package/dist/templates/cursor/fixtures/expected-rules.js.map +1 -0
  556. package/dist/templates/cursor/hooks.json +52 -0
  557. package/dist/templates/cursor/index.d.ts +19 -0
  558. package/dist/templates/cursor/index.d.ts.map +1 -0
  559. package/dist/templates/cursor/index.js +19 -0
  560. package/dist/templates/cursor/index.js.map +1 -0
  561. package/dist/templates/cursor/rules/pactile-bootstrap.mdc +18 -0
  562. package/dist/templates/cursor/worktrees.json +11 -0
  563. package/dist/templates/extract.d.ts +38 -0
  564. package/dist/templates/extract.d.ts.map +1 -0
  565. package/dist/templates/extract.js +120 -0
  566. package/dist/templates/extract.js.map +1 -0
  567. package/dist/templates/markdown/agents.md +14 -0
  568. package/dist/templates/markdown/framework/artifact-locale-guide.md.txt +93 -0
  569. package/dist/templates/markdown/framework/cursor-context-injection-guide.md.txt +85 -0
  570. package/dist/templates/markdown/framework/cursor-native-modes-guide.md.txt +239 -0
  571. package/dist/templates/markdown/framework/cursor-semantic-compliance.md.txt +41 -0
  572. package/dist/templates/markdown/framework/cursor-subagent-policy.md.txt +90 -0
  573. package/dist/templates/markdown/framework/dogfood-only-surfaces.md.txt +28 -0
  574. package/dist/templates/markdown/framework/execution-strategy.md.txt +45 -0
  575. package/dist/templates/markdown/framework/index.md.txt +31 -0
  576. package/dist/templates/markdown/framework/injection-budget-guide.md.txt +111 -0
  577. package/dist/templates/markdown/framework/internal-skills-cursor-reachability.md.txt +45 -0
  578. package/dist/templates/markdown/framework/middleware-protocol.md.txt +238 -0
  579. package/dist/templates/markdown/framework/parallel-first-execution.md.txt +116 -0
  580. package/dist/templates/markdown/framework/prd-grill-frontier.md.txt +105 -0
  581. package/dist/templates/markdown/framework/release-boundary.md.txt +30 -0
  582. package/dist/templates/markdown/framework/retrieval-daily-guide.md.txt +199 -0
  583. package/dist/templates/markdown/framework/upgrade.md.txt +15 -0
  584. package/dist/templates/markdown/framework/verification-strength-guide.md.txt +185 -0
  585. package/dist/templates/markdown/gitignore.txt +15 -0
  586. package/dist/templates/markdown/index.d.ts +59 -0
  587. package/dist/templates/markdown/index.d.ts.map +1 -0
  588. package/dist/templates/markdown/index.js +144 -0
  589. package/dist/templates/markdown/index.js.map +1 -0
  590. package/dist/templates/markdown/prompts/run-semantic-slice-12.md.txt +25 -0
  591. package/dist/templates/markdown/spec/backend/database-guidelines.md.txt +51 -0
  592. package/dist/templates/markdown/spec/backend/directory-structure.md.txt +54 -0
  593. package/dist/templates/markdown/spec/backend/error-handling.md.txt +51 -0
  594. package/dist/templates/markdown/spec/backend/index.md.txt +38 -0
  595. package/dist/templates/markdown/spec/backend/logging-guidelines.md.txt +51 -0
  596. package/dist/templates/markdown/spec/backend/quality-guidelines.md.txt +51 -0
  597. package/dist/templates/markdown/spec/frontend/component-guidelines.md.txt +59 -0
  598. package/dist/templates/markdown/spec/frontend/directory-structure.md.txt +54 -0
  599. package/dist/templates/markdown/spec/frontend/hook-guidelines.md.txt +51 -0
  600. package/dist/templates/markdown/spec/frontend/index.md.txt +39 -0
  601. package/dist/templates/markdown/spec/frontend/quality-guidelines.md.txt +51 -0
  602. package/dist/templates/markdown/spec/frontend/state-management.md.txt +51 -0
  603. package/dist/templates/markdown/spec/frontend/type-safety.md.txt +51 -0
  604. package/dist/templates/markdown/spec/guides/code-reuse-thinking-guide.md.txt +174 -0
  605. package/dist/templates/markdown/spec/guides/cross-layer-thinking-guide.md.txt +162 -0
  606. package/dist/templates/markdown/spec/guides/cross-platform-thinking-guide.md.txt +634 -0
  607. package/dist/templates/markdown/spec/guides/debug-loop-guide.md.txt +227 -0
  608. package/dist/templates/markdown/spec/guides/durable-learning-decision-guide.md.txt +76 -0
  609. package/dist/templates/markdown/spec/guides/e2e-walkthrough-guide.md.txt +93 -0
  610. package/dist/templates/markdown/spec/guides/index.md.txt +131 -0
  611. package/dist/templates/markdown/spec/guides/prototype-guide.md.txt +139 -0
  612. package/dist/templates/markdown/spec/guides/test-discipline-guide.md.txt +179 -0
  613. package/dist/templates/markdown/workspace-index.md +125 -0
  614. package/dist/templates/markdown/worktree.yaml.txt +58 -0
  615. package/dist/templates/pactile/CONTEXT.md +113 -0
  616. package/dist/templates/pactile/config/execution-strategy-rules.json +31 -0
  617. package/dist/templates/pactile/config.yaml +130 -0
  618. package/dist/templates/pactile/docs/adr/README.md +21 -0
  619. package/dist/templates/pactile/gitignore.txt +35 -0
  620. package/dist/templates/pactile/index.d.ts +134 -0
  621. package/dist/templates/pactile/index.d.ts.map +1 -0
  622. package/dist/templates/pactile/index.js +316 -0
  623. package/dist/templates/pactile/index.js.map +1 -0
  624. package/dist/templates/pactile/modules/approval-personal/contract.md +43 -0
  625. package/dist/templates/pactile/modules/candidate-pool/contract.md +37 -0
  626. package/dist/templates/pactile/modules/catalog.d.ts +32 -0
  627. package/dist/templates/pactile/modules/catalog.d.ts.map +1 -0
  628. package/dist/templates/pactile/modules/catalog.js +46 -0
  629. package/dist/templates/pactile/modules/catalog.js.map +1 -0
  630. package/dist/templates/pactile/modules/close-basic/contract.md +38 -0
  631. package/dist/templates/pactile/modules/context-progressive/contract.md +44 -0
  632. package/dist/templates/pactile/modules/debug-recovery/contract.md +44 -0
  633. package/dist/templates/pactile/modules/define-basic/contract.md +39 -0
  634. package/dist/templates/pactile/modules/define-extended/contract.md +45 -0
  635. package/dist/templates/pactile/modules/execute-agent/contract.md +38 -0
  636. package/dist/templates/pactile/modules/independent-check/contract.md +35 -0
  637. package/dist/templates/pactile/modules/index.json +133 -0
  638. package/dist/templates/pactile/modules/intake-basic/contract.md +34 -0
  639. package/dist/templates/pactile/modules/observability-local/contract.md +31 -0
  640. package/dist/templates/pactile/modules/parent-child/contract.md +45 -0
  641. package/dist/templates/pactile/modules/personal-memory/contract.md +56 -0
  642. package/dist/templates/pactile/modules/retention-storage/contract.md +43 -0
  643. package/dist/templates/pactile/modules/retrieval-extended/contract.md +77 -0
  644. package/dist/templates/pactile/modules/session-transfer/contract.md +41 -0
  645. package/dist/templates/pactile/modules/spec-learning/contract.md +50 -0
  646. package/dist/templates/pactile/modules/vcs-integration/contract.md +46 -0
  647. package/dist/templates/pactile/modules/verify-basic/contract.md +38 -0
  648. package/dist/templates/pactile/modules/worker-orchestration/contract.md +47 -0
  649. package/dist/templates/pactile/pool/README.md +149 -0
  650. package/dist/templates/pactile/pool/items/.gitkeep +0 -0
  651. package/dist/templates/pactile/pool/plan.md +50 -0
  652. package/dist/templates/pactile/scripts/__init__.py +5 -0
  653. package/dist/templates/pactile/scripts/add_session.py +547 -0
  654. package/dist/templates/pactile/scripts/build_context_pack.py +17 -0
  655. package/dist/templates/pactile/scripts/build_retrieval_pack.py +376 -0
  656. package/dist/templates/pactile/scripts/codegraph_session_smoke.py +76 -0
  657. package/dist/templates/pactile/scripts/common/__init__.py +103 -0
  658. package/dist/templates/pactile/scripts/common/active_task.py +576 -0
  659. package/dist/templates/pactile/scripts/common/adapter_middleware.py +1373 -0
  660. package/dist/templates/pactile/scripts/common/artifact_locale.py +303 -0
  661. package/dist/templates/pactile/scripts/common/artifact_search.py +587 -0
  662. package/dist/templates/pactile/scripts/common/cli_adapter.py +135 -0
  663. package/dist/templates/pactile/scripts/common/cli_environment.py +155 -0
  664. package/dist/templates/pactile/scripts/common/codebase_retrieval_router.py +1339 -0
  665. package/dist/templates/pactile/scripts/common/config.py +497 -0
  666. package/dist/templates/pactile/scripts/common/context_pack.py +371 -0
  667. package/dist/templates/pactile/scripts/common/cursor_retrieval_env.py +31 -0
  668. package/dist/templates/pactile/scripts/common/developer.py +190 -0
  669. package/dist/templates/pactile/scripts/common/execution_strategy.py +268 -0
  670. package/dist/templates/pactile/scripts/common/full_quality.py +237 -0
  671. package/dist/templates/pactile/scripts/common/git.py +37 -0
  672. package/dist/templates/pactile/scripts/common/git_context.py +198 -0
  673. package/dist/templates/pactile/scripts/common/injection_budget.py +317 -0
  674. package/dist/templates/pactile/scripts/common/io.py +37 -0
  675. package/dist/templates/pactile/scripts/common/kernel_command.py +369 -0
  676. package/dist/templates/pactile/scripts/common/lite_context.py +280 -0
  677. package/dist/templates/pactile/scripts/common/log.py +45 -0
  678. package/dist/templates/pactile/scripts/common/ondemand_topology.py +419 -0
  679. package/dist/templates/pactile/scripts/common/packages_context.py +239 -0
  680. package/dist/templates/pactile/scripts/common/pactile_config.py +131 -0
  681. package/dist/templates/pactile/scripts/common/parallel_declaration.py +369 -0
  682. package/dist/templates/pactile/scripts/common/parent_orchestration.py +1198 -0
  683. package/dist/templates/pactile/scripts/common/paths.py +756 -0
  684. package/dist/templates/pactile/scripts/common/pool_refs.py +119 -0
  685. package/dist/templates/pactile/scripts/common/pool_slice.py +269 -0
  686. package/dist/templates/pactile/scripts/common/pool_store.py +766 -0
  687. package/dist/templates/pactile/scripts/common/project_file_stats.py +91 -0
  688. package/dist/templates/pactile/scripts/common/retrieval_adapter_metadata.py +943 -0
  689. package/dist/templates/pactile/scripts/common/retrieval_agent_instructions.py +123 -0
  690. package/dist/templates/pactile/scripts/common/retrieval_evidence.py +915 -0
  691. package/dist/templates/pactile/scripts/common/retrieval_pack.py +422 -0
  692. package/dist/templates/pactile/scripts/common/retrieval_pack_context.py +122 -0
  693. package/dist/templates/pactile/scripts/common/retrieval_plan_gate.py +61 -0
  694. package/dist/templates/pactile/scripts/common/retrieval_result_ranking.py +104 -0
  695. package/dist/templates/pactile/scripts/common/retrieval_tool_classification.py +166 -0
  696. package/dist/templates/pactile/scripts/common/safe_commit.py +285 -0
  697. package/dist/templates/pactile/scripts/common/semantic_plan_gate.py +53 -0
  698. package/dist/templates/pactile/scripts/common/session_context.py +1213 -0
  699. package/dist/templates/pactile/scripts/common/session_memory.py +386 -0
  700. package/dist/templates/pactile/scripts/common/session_pack.py +886 -0
  701. package/dist/templates/pactile/scripts/common/smart_search_evidence.py +514 -0
  702. package/dist/templates/pactile/scripts/common/smart_search_resolve.py +150 -0
  703. package/dist/templates/pactile/scripts/common/subagent_dispatch.py +592 -0
  704. package/dist/templates/pactile/scripts/common/task_context.py +241 -0
  705. package/dist/templates/pactile/scripts/common/task_dashboard.py +286 -0
  706. package/dist/templates/pactile/scripts/common/task_dependencies.py +648 -0
  707. package/dist/templates/pactile/scripts/common/task_gates.py +2478 -0
  708. package/dist/templates/pactile/scripts/common/task_map.py +703 -0
  709. package/dist/templates/pactile/scripts/common/task_queue.py +188 -0
  710. package/dist/templates/pactile/scripts/common/task_store.py +2252 -0
  711. package/dist/templates/pactile/scripts/common/task_utils.py +274 -0
  712. package/dist/templates/pactile/scripts/common/tasks.py +231 -0
  713. package/dist/templates/pactile/scripts/common/test_adapter_middleware.py +199 -0
  714. package/dist/templates/pactile/scripts/common/test_depends_mode_block.py +509 -0
  715. package/dist/templates/pactile/scripts/common/test_full_quality.py +197 -0
  716. package/dist/templates/pactile/scripts/common/test_kernel_command.py +336 -0
  717. package/dist/templates/pactile/scripts/common/test_lite_path.py +190 -0
  718. package/dist/templates/pactile/scripts/common/test_observable_defaults.py +141 -0
  719. package/dist/templates/pactile/scripts/common/test_ondemand_topology.py +237 -0
  720. package/dist/templates/pactile/scripts/common/test_parallel_declaration.py +264 -0
  721. package/dist/templates/pactile/scripts/common/test_pool_slice.py +198 -0
  722. package/dist/templates/pactile/scripts/common/test_pool_store.py +452 -0
  723. package/dist/templates/pactile/scripts/common/test_retrieval_arbitration.py +331 -0
  724. package/dist/templates/pactile/scripts/common/test_task_dashboard.py +155 -0
  725. package/dist/templates/pactile/scripts/common/test_task_dependencies.py +329 -0
  726. package/dist/templates/pactile/scripts/common/test_task_store_kernel_patch.py +163 -0
  727. package/dist/templates/pactile/scripts/common/types.py +110 -0
  728. package/dist/templates/pactile/scripts/common/workflow_phase.py +194 -0
  729. package/dist/templates/pactile/scripts/compile_session_pack.py +15 -0
  730. package/dist/templates/pactile/scripts/cursor_retrieval_probe.py +64 -0
  731. package/dist/templates/pactile/scripts/cursor_retrieval_probe_prompt.md +33 -0
  732. package/dist/templates/pactile/scripts/generate_dispatch_prompt.py +182 -0
  733. package/dist/templates/pactile/scripts/get_context.py +19 -0
  734. package/dist/templates/pactile/scripts/get_developer.py +26 -0
  735. package/dist/templates/pactile/scripts/hooks/linear_sync.py +243 -0
  736. package/dist/templates/pactile/scripts/init_developer.py +51 -0
  737. package/dist/templates/pactile/scripts/injection_budget_probe.py +69 -0
  738. package/dist/templates/pactile/scripts/pool.py +237 -0
  739. package/dist/templates/pactile/scripts/rank_retrieval_candidates.py +98 -0
  740. package/dist/templates/pactile/scripts/retrieval_probe_matrix_template.json +43 -0
  741. package/dist/templates/pactile/scripts/route_codebase_retrieval.py +72 -0
  742. package/dist/templates/pactile/scripts/run_smart_search.py +12 -0
  743. package/dist/templates/pactile/scripts/score_evidence.py +131 -0
  744. package/dist/templates/pactile/scripts/search_artifacts.py +13 -0
  745. package/dist/templates/pactile/scripts/search_memory.py +12 -0
  746. package/dist/templates/pactile/scripts/spec_health_outcomes.py +160 -0
  747. package/dist/templates/pactile/scripts/task.py +1118 -0
  748. package/dist/templates/pactile/scripts/verify_evidence_probe.py +138 -0
  749. package/dist/templates/pactile/tasks/.gitkeep +0 -0
  750. package/dist/templates/pactile/tasks/locale/en/default-prd.md +27 -0
  751. package/dist/templates/pactile/tasks/locale/zh/default-prd.md +27 -0
  752. package/dist/templates/pactile/tasks/templates/release-execution/design.md +61 -0
  753. package/dist/templates/pactile/tasks/templates/release-execution/handoff-template.md +29 -0
  754. package/dist/templates/pactile/tasks/templates/release-execution/implement.md +29 -0
  755. package/dist/templates/pactile/tasks/templates/release-execution/prd.md +31 -0
  756. package/dist/templates/pactile/tasks/templates/release-readiness/design.md +50 -0
  757. package/dist/templates/pactile/tasks/templates/release-readiness/handoff-template.md +39 -0
  758. package/dist/templates/pactile/tasks/templates/release-readiness/implement.md +29 -0
  759. package/dist/templates/pactile/tasks/templates/release-readiness/prd.md +31 -0
  760. package/dist/templates/pactile/workflow.md +64 -0
  761. package/dist/templates/shared-hooks/event-bridge.py +121 -0
  762. package/dist/templates/shared-hooks/index.d.ts +50 -0
  763. package/dist/templates/shared-hooks/index.d.ts.map +1 -0
  764. package/dist/templates/shared-hooks/index.js +96 -0
  765. package/dist/templates/shared-hooks/index.js.map +1 -0
  766. package/dist/templates/shared-hooks/inject-retrieval-plan.py +217 -0
  767. package/dist/templates/shared-hooks/inject-shell-session-context.py +183 -0
  768. package/dist/templates/shared-hooks/inject-subagent-context.py +267 -0
  769. package/dist/templates/shared-hooks/inject-workflow-state.py +106 -0
  770. package/dist/templates/shared-hooks/rename-session-for-task.py +231 -0
  771. package/dist/templates/shared-hooks/research-end-retrieval-pack.py +345 -0
  772. package/dist/templates/shared-hooks/session-start.py +355 -0
  773. package/dist/templates/shared-hooks/spec-write-audit.py +173 -0
  774. package/dist/templates/template-utils.d.ts +28 -0
  775. package/dist/templates/template-utils.d.ts.map +1 -0
  776. package/dist/templates/template-utils.js +83 -0
  777. package/dist/templates/template-utils.js.map +1 -0
  778. package/dist/types/ai-tools.d.ts +102 -0
  779. package/dist/types/ai-tools.d.ts.map +1 -0
  780. package/dist/types/ai-tools.js +60 -0
  781. package/dist/types/ai-tools.js.map +1 -0
  782. package/dist/types/migration.d.ts +125 -0
  783. package/dist/types/migration.d.ts.map +1 -0
  784. package/dist/types/migration.js +8 -0
  785. package/dist/types/migration.js.map +1 -0
  786. package/dist/utils/agents-md.d.ts +27 -0
  787. package/dist/utils/agents-md.d.ts.map +1 -0
  788. package/dist/utils/agents-md.js +70 -0
  789. package/dist/utils/agents-md.js.map +1 -0
  790. package/dist/utils/codebase-retrieval-router.d.ts +31 -0
  791. package/dist/utils/codebase-retrieval-router.d.ts.map +1 -0
  792. package/dist/utils/codebase-retrieval-router.js +88 -0
  793. package/dist/utils/codebase-retrieval-router.js.map +1 -0
  794. package/dist/utils/compare-versions.d.ts +12 -0
  795. package/dist/utils/compare-versions.d.ts.map +1 -0
  796. package/dist/utils/compare-versions.js +86 -0
  797. package/dist/utils/compare-versions.js.map +1 -0
  798. package/dist/utils/cursor-retrieval-env.d.ts +17 -0
  799. package/dist/utils/cursor-retrieval-env.d.ts.map +1 -0
  800. package/dist/utils/cursor-retrieval-env.js +19 -0
  801. package/dist/utils/cursor-retrieval-env.js.map +1 -0
  802. package/dist/utils/cwd-guard.d.ts +38 -0
  803. package/dist/utils/cwd-guard.d.ts.map +1 -0
  804. package/dist/utils/cwd-guard.js +63 -0
  805. package/dist/utils/cwd-guard.js.map +1 -0
  806. package/dist/utils/file-writer.d.ts +36 -0
  807. package/dist/utils/file-writer.d.ts.map +1 -0
  808. package/dist/utils/file-writer.js +203 -0
  809. package/dist/utils/file-writer.js.map +1 -0
  810. package/dist/utils/manifest-prune.d.ts +59 -0
  811. package/dist/utils/manifest-prune.d.ts.map +1 -0
  812. package/dist/utils/manifest-prune.js +176 -0
  813. package/dist/utils/manifest-prune.js.map +1 -0
  814. package/dist/utils/mirror-check.d.ts +22 -0
  815. package/dist/utils/mirror-check.d.ts.map +1 -0
  816. package/dist/utils/mirror-check.js +90 -0
  817. package/dist/utils/mirror-check.js.map +1 -0
  818. package/dist/utils/normalize-text.d.ts +7 -0
  819. package/dist/utils/normalize-text.d.ts.map +1 -0
  820. package/dist/utils/normalize-text.js +16 -0
  821. package/dist/utils/normalize-text.js.map +1 -0
  822. package/dist/utils/p36-upgrade.d.ts +62 -0
  823. package/dist/utils/p36-upgrade.d.ts.map +1 -0
  824. package/dist/utils/p36-upgrade.js +165 -0
  825. package/dist/utils/p36-upgrade.js.map +1 -0
  826. package/dist/utils/posix.d.ts +13 -0
  827. package/dist/utils/posix.d.ts.map +1 -0
  828. package/dist/utils/posix.js +15 -0
  829. package/dist/utils/posix.js.map +1 -0
  830. package/dist/utils/post-update-smoke.d.ts +7 -0
  831. package/dist/utils/post-update-smoke.d.ts.map +1 -0
  832. package/dist/utils/post-update-smoke.js +40 -0
  833. package/dist/utils/post-update-smoke.js.map +1 -0
  834. package/dist/utils/project-capabilities.d.ts +82 -0
  835. package/dist/utils/project-capabilities.d.ts.map +1 -0
  836. package/dist/utils/project-capabilities.js +691 -0
  837. package/dist/utils/project-capabilities.js.map +1 -0
  838. package/dist/utils/project-detector.d.ts +46 -0
  839. package/dist/utils/project-detector.d.ts.map +1 -0
  840. package/dist/utils/project-detector.js +666 -0
  841. package/dist/utils/project-detector.js.map +1 -0
  842. package/dist/utils/proxy.d.ts +25 -0
  843. package/dist/utils/proxy.d.ts.map +1 -0
  844. package/dist/utils/proxy.js +60 -0
  845. package/dist/utils/proxy.js.map +1 -0
  846. package/dist/utils/readiness.d.ts +40 -0
  847. package/dist/utils/readiness.d.ts.map +1 -0
  848. package/dist/utils/readiness.js +491 -0
  849. package/dist/utils/readiness.js.map +1 -0
  850. package/dist/utils/retrieval-agent-instructions.d.ts +10 -0
  851. package/dist/utils/retrieval-agent-instructions.d.ts.map +1 -0
  852. package/dist/utils/retrieval-agent-instructions.js +97 -0
  853. package/dist/utils/retrieval-agent-instructions.js.map +1 -0
  854. package/dist/utils/retrieval-execution-telemetry.d.ts +53 -0
  855. package/dist/utils/retrieval-execution-telemetry.d.ts.map +1 -0
  856. package/dist/utils/retrieval-execution-telemetry.js +116 -0
  857. package/dist/utils/retrieval-execution-telemetry.js.map +1 -0
  858. package/dist/utils/retrieval-result-ranking.d.ts +34 -0
  859. package/dist/utils/retrieval-result-ranking.d.ts.map +1 -0
  860. package/dist/utils/retrieval-result-ranking.js +71 -0
  861. package/dist/utils/retrieval-result-ranking.js.map +1 -0
  862. package/dist/utils/retrieval-tool-classification.d.ts +18 -0
  863. package/dist/utils/retrieval-tool-classification.d.ts.map +1 -0
  864. package/dist/utils/retrieval-tool-classification.js +118 -0
  865. package/dist/utils/retrieval-tool-classification.js.map +1 -0
  866. package/dist/utils/semantic-plan-gate.d.ts +8 -0
  867. package/dist/utils/semantic-plan-gate.d.ts.map +1 -0
  868. package/dist/utils/semantic-plan-gate.js +24 -0
  869. package/dist/utils/semantic-plan-gate.js.map +1 -0
  870. package/dist/utils/task-json.d.ts +13 -0
  871. package/dist/utils/task-json.d.ts.map +1 -0
  872. package/dist/utils/task-json.js +12 -0
  873. package/dist/utils/task-json.js.map +1 -0
  874. package/dist/utils/template-fetcher.d.ts +150 -0
  875. package/dist/utils/template-fetcher.d.ts.map +1 -0
  876. package/dist/utils/template-fetcher.js +907 -0
  877. package/dist/utils/template-fetcher.js.map +1 -0
  878. package/dist/utils/template-hash.d.ts +123 -0
  879. package/dist/utils/template-hash.d.ts.map +1 -0
  880. package/dist/utils/template-hash.js +375 -0
  881. package/dist/utils/template-hash.js.map +1 -0
  882. package/dist/utils/uninstall-scrubbers.d.ts +30 -0
  883. package/dist/utils/uninstall-scrubbers.d.ts.map +1 -0
  884. package/dist/utils/uninstall-scrubbers.js +196 -0
  885. package/dist/utils/uninstall-scrubbers.js.map +1 -0
  886. package/dist/utils/update-rollout-report.d.ts +171 -0
  887. package/dist/utils/update-rollout-report.d.ts.map +1 -0
  888. package/dist/utils/update-rollout-report.js +126 -0
  889. package/dist/utils/update-rollout-report.js.map +1 -0
  890. package/dist/utils/validate-rules.d.ts +21 -0
  891. package/dist/utils/validate-rules.d.ts.map +1 -0
  892. package/dist/utils/validate-rules.js +88 -0
  893. package/dist/utils/validate-rules.js.map +1 -0
  894. package/dist/utils/workflow-dir.d.ts +15 -0
  895. package/dist/utils/workflow-dir.d.ts.map +1 -0
  896. package/dist/utils/workflow-dir.js +33 -0
  897. package/dist/utils/workflow-dir.js.map +1 -0
  898. package/dist/utils/workflow-resolver.d.ts +86 -0
  899. package/dist/utils/workflow-resolver.d.ts.map +1 -0
  900. package/dist/utils/workflow-resolver.js +265 -0
  901. package/dist/utils/workflow-resolver.js.map +1 -0
  902. package/package.json +117 -0
  903. package/scripts/postinstall.js +6 -0
@@ -0,0 +1,2252 @@
1
+ #!/usr/bin/env python
2
+ """
3
+ Task CRUD operations.
4
+
5
+ Provides:
6
+ ensure_tasks_dir - Ensure tasks directory exists
7
+ cmd_create - Create a new task
8
+ cmd_archive - Archive completed task
9
+ cmd_set_branch - Set git branch for task
10
+ cmd_set_base_branch - Set PR target branch
11
+ cmd_set_scope - Set scope for PR title
12
+ cmd_set_deps - Set task-level depends_on (declare + soft checks)
13
+ cmd_set_depends_mode - Set meta.depends_mode (warn | block | off)
14
+ cmd_add_subtask - Link child task to parent
15
+ cmd_remove_subtask - Unlink child task from parent
16
+ cmd_prepare_child_worktree - Create/register Child git worktree
17
+ cmd_set_child_state - Set Child-reported task-map state
18
+ cmd_integrate_child - Set Parent-controlled Child integration state
19
+ """
20
+
21
+ from __future__ import annotations
22
+
23
+ import argparse
24
+ import json
25
+ import re
26
+ import sys
27
+ from datetime import datetime
28
+ from pathlib import Path
29
+
30
+ from .artifact_locale import (
31
+ default_prd_content,
32
+ default_verify_content,
33
+ resolve_artifact_locale,
34
+ )
35
+ from .config import (
36
+ get_packages,
37
+ get_session_auto_commit,
38
+ is_monorepo,
39
+ resolve_package,
40
+ validate_package,
41
+ )
42
+ from .cli_environment import format_git_repo_errors, print_environment_repair_hints
43
+ from .git import is_git_worktree, run_git
44
+ from .io import read_json
45
+ from .kernel_command import (
46
+ KernelCliNotFound,
47
+ KernelCommandError,
48
+ kernel_archive,
49
+ kernel_create,
50
+ kernel_expected_revision,
51
+ kernel_patch,
52
+ print_kernel_error,
53
+ )
54
+ from .log import Colors, colored
55
+ from .paths import (
56
+ DIR_ARCHIVE,
57
+ DIR_TASKS,
58
+ DIR_WORKFLOW,
59
+ FILE_TASK_JSON,
60
+ generate_task_date_prefix,
61
+ get_developer,
62
+ get_repo_root,
63
+ get_tasks_dir,
64
+ )
65
+ from .safe_commit import (
66
+ print_gitignore_warning,
67
+ safe_archive_paths_to_add,
68
+ safe_git_add,
69
+ )
70
+ from .task_gates import (
71
+ BASELINE_GATE,
72
+ archive_repair_hints,
73
+ build_notes_projection,
74
+ build_spec_update_scaffold,
75
+ collect_kernel_projection_extras,
76
+ prepare_archive_evidence,
77
+ task_closeout_profile,
78
+ validate_archive,
79
+ write_gate_record,
80
+ )
81
+ from .ondemand_topology import TopologyError, assign_parent, default_topology
82
+ from .task_map import (
83
+ CHILD_STATES,
84
+ CHILD_REPORT_STATES,
85
+ PARENT_CONTROLLED_STATES,
86
+ ensure_task_map,
87
+ get_child_state,
88
+ record_child_worktree,
89
+ remove_child_from_task_map,
90
+ resolve_child_map_id,
91
+ set_child_state,
92
+ set_parent_child_integration_state,
93
+ validate_parent_child_integration,
94
+ )
95
+ from .tasks import parent_archive_child_followup_hint
96
+ from .task_utils import (
97
+ archive_task_complete,
98
+ find_task_by_name,
99
+ resolve_task_dir,
100
+ run_task_hooks,
101
+ )
102
+
103
+
104
+ # =============================================================================
105
+ # Helper Functions
106
+ # =============================================================================
107
+
108
+ def _slugify(title: str) -> str:
109
+ """Convert title to slug (only works with ASCII)."""
110
+ result = title.lower()
111
+ result = re.sub(r"[^a-z0-9]", "-", result)
112
+ result = re.sub(r"-+", "-", result)
113
+ result = result.strip("-")
114
+ return result
115
+
116
+
117
+ def ensure_tasks_dir(repo_root: Path) -> Path:
118
+ """Ensure tasks directory exists."""
119
+ tasks_dir = get_tasks_dir(repo_root)
120
+ archive_dir = tasks_dir / "archive"
121
+
122
+ if not tasks_dir.exists():
123
+ tasks_dir.mkdir(parents=True)
124
+ print(colored(f"Created tasks directory: {tasks_dir}", Colors.GREEN), file=sys.stderr)
125
+
126
+ if not archive_dir.exists():
127
+ archive_dir.mkdir(parents=True)
128
+
129
+ return tasks_dir
130
+
131
+
132
+ def _find_archived_task_by_dir_name(tasks_dir: Path, dir_name: str) -> Path | None:
133
+ """Find an archived task directory with the exact active-task dir name."""
134
+ archive_dir = tasks_dir / DIR_ARCHIVE
135
+ if not archive_dir.is_dir():
136
+ return None
137
+
138
+ for month_dir in sorted(archive_dir.iterdir()):
139
+ if not month_dir.is_dir():
140
+ continue
141
+ candidate = month_dir / dir_name
142
+ if candidate.is_dir():
143
+ return candidate
144
+
145
+ return None
146
+
147
+
148
+ def _repo_relative_path(path: Path, repo_root: Path) -> str:
149
+ """Format a path relative to the repo root when possible."""
150
+ try:
151
+ return path.relative_to(repo_root).as_posix()
152
+ except ValueError:
153
+ return str(path)
154
+
155
+
156
+ def _short_cli_ref(value: str | None) -> bool:
157
+ """Return True for a compact single-argument ref/path value."""
158
+ if not value or not isinstance(value, str):
159
+ return False
160
+ if value.startswith("-"):
161
+ return False
162
+ if len(value) > 240:
163
+ return False
164
+ return "\n" not in value and "\r" not in value
165
+
166
+
167
+ def _validate_git_repo(repo_root: Path) -> list[str]:
168
+ rc, out, err = run_git(["rev-parse", "--is-inside-work-tree"], cwd=repo_root)
169
+ if rc != 0 or out.strip() != "true":
170
+ detail = err.strip() or out.strip() or "not a Git worktree"
171
+ return format_git_repo_errors([f"Git repository required: {detail}"])
172
+ return []
173
+
174
+
175
+ def _validate_branch_name(repo_root: Path, branch: str) -> list[str]:
176
+ if not _short_cli_ref(branch):
177
+ return ["branch must be a short git branch name"]
178
+ rc, _, err = run_git(["check-ref-format", "--branch", branch], cwd=repo_root)
179
+ if rc != 0:
180
+ return [f"invalid branch name: {branch} ({err.strip() or 'git check-ref-format failed'})"]
181
+ return []
182
+
183
+
184
+ def _git_commit_exists(repo_root: Path, ref: str | None) -> bool:
185
+ if not _short_cli_ref(ref):
186
+ return False
187
+ rc, _, _ = run_git(["rev-parse", "--verify", f"{ref}^{{commit}}"], cwd=repo_root)
188
+ return rc == 0
189
+
190
+
191
+ def _git_branch_exists(repo_root: Path, branch: str) -> bool:
192
+ rc, _, _ = run_git(
193
+ ["rev-parse", "--verify", "--quiet", f"refs/heads/{branch}"],
194
+ cwd=repo_root,
195
+ )
196
+ return rc == 0
197
+
198
+
199
+ def _default_child_worktree_path(repo_root: Path, child_dir: Path) -> Path:
200
+ return repo_root / DIR_WORKFLOW / "worktrees" / child_dir.name
201
+
202
+
203
+ def _resolve_child_worktree_path(repo_root: Path, child_dir: Path, raw_path: str | None) -> tuple[Path | None, str | None]:
204
+ path = Path(raw_path) if raw_path else _default_child_worktree_path(repo_root, child_dir)
205
+ if not path.is_absolute():
206
+ path = repo_root / path
207
+ resolved = path.resolve()
208
+ worktree_root = (repo_root / DIR_WORKFLOW / "worktrees").resolve()
209
+ try:
210
+ resolved.relative_to(worktree_root)
211
+ except ValueError:
212
+ return None, f"worktree path must stay under {_repo_relative_path(worktree_root, repo_root)}"
213
+ return resolved, None
214
+
215
+
216
+ def _non_pactile_dirty_paths(repo_root: Path) -> list[str]:
217
+ rc, out, err = run_git(["status", "--porcelain"], cwd=repo_root)
218
+ if rc != 0:
219
+ return [err.strip() or "git status failed"]
220
+
221
+ dirty: list[str] = []
222
+ for raw_line in out.splitlines():
223
+ path = raw_line[3:].strip()
224
+ if " -> " in path:
225
+ path = path.split(" -> ", 1)[1].strip()
226
+ path = path.replace("\\", "/").strip('"')
227
+ if path and path != ".pactile" and not path.startswith(".pactile/"):
228
+ dirty.append(path)
229
+ return dirty
230
+
231
+
232
+ def _validate_merge_execution(repo_root: Path, state: str, ref: str | None) -> list[str]:
233
+ errors = _validate_git_repo(repo_root)
234
+ if state != "integrated":
235
+ errors.append("--execute-merge is only valid with state `integrated`")
236
+ if not _short_cli_ref(ref):
237
+ errors.append("--execute-merge requires --ref")
238
+ elif not _git_commit_exists(repo_root, ref):
239
+ errors.append(f"merge ref does not resolve to a commit: {ref}")
240
+
241
+ dirty = _non_pactile_dirty_paths(repo_root)
242
+ if dirty:
243
+ errors.append(
244
+ "non-Pactile working tree changes block merge execution: "
245
+ + ", ".join(dirty[:8])
246
+ )
247
+ return errors
248
+
249
+
250
+ # =============================================================================
251
+ # Sub-agent platform detection + JSONL seeding
252
+ # =============================================================================
253
+
254
+ # Config directories of platforms that consume implement.jsonl / check.jsonl.
255
+ # Keep in sync with src/types/ai-tools.ts AI_TOOLS entries — these are the
256
+ # platforms listed in workflow.md's "agent-capable" Skill Routing block
257
+ # (Class-1 hook-inject + Class-2 pull-based preludes). Kilo / Antigravity /
258
+ # Windsurf are NOT in this list: they do not consume JSONL.
259
+ _SUBAGENT_CONFIG_DIRS: tuple[str, ...] = (
260
+ ".claude",
261
+ ".cursor",
262
+ ".codex",
263
+ ".kiro",
264
+ ".gemini",
265
+ ".opencode",
266
+ ".qoder",
267
+ ".codebuddy",
268
+ ".factory", # Factory Droid
269
+ ".github/copilot",
270
+ ".pi", # Pi Agent
271
+ )
272
+
273
+ # Stable seed paths for jsonl create: one user-owned thinking-guide index plus
274
+ # framework-owned verification/injection docs (both always present after init).
275
+ _STABLE_SEED_PATHS: tuple[str, ...] = (
276
+ ".pactile/spec/guides/index.md",
277
+ ".pactile/framework/verification-strength-guide.md",
278
+ ".pactile/framework/injection-budget-guide.md",
279
+ )
280
+
281
+
282
+ def _has_subagent_platform(repo_root: Path) -> bool:
283
+ """Return True if any sub-agent-capable platform is configured.
284
+
285
+ Detected by probing well-known config directories at the repo root. Used
286
+ only to decide whether ``task.py create`` should seed empty
287
+ ``implement.jsonl`` / ``check.jsonl`` files.
288
+ """
289
+ for config_dir in _SUBAGENT_CONFIG_DIRS:
290
+ if (repo_root / config_dir).is_dir():
291
+ return True
292
+ return False
293
+
294
+
295
+ def _resolve_seed_spec_paths(
296
+ repo_root: Path,
297
+ task_dir: Path,
298
+ task_data: dict,
299
+ ) -> list[str]:
300
+ """Return 2–3 existing spec file paths for jsonl create seeds."""
301
+ from .task_gates import suggest_spec_targets
302
+
303
+ candidates: list[str] = []
304
+ for path in suggest_spec_targets(repo_root, task_dir, task_data):
305
+ if path not in candidates:
306
+ candidates.append(path)
307
+
308
+ guides_dir = repo_root / ".pactile" / "spec" / "guides"
309
+ if guides_dir.is_dir():
310
+ for guide in sorted(guides_dir.iterdir()):
311
+ if guide.is_file() and guide.suffix.lower() == ".md":
312
+ rel = f".pactile/spec/guides/{guide.name}"
313
+ if rel not in candidates:
314
+ candidates.append(rel)
315
+
316
+ for rel in _STABLE_SEED_PATHS:
317
+ if rel not in candidates:
318
+ candidates.append(rel)
319
+
320
+ existing = [rel for rel in candidates if (repo_root / rel).is_file()]
321
+ if len(existing) >= 2:
322
+ return existing[:3]
323
+
324
+ fallback = [
325
+ rel for rel in _STABLE_SEED_PATHS if (repo_root / rel).is_file()
326
+ ]
327
+ return fallback[:3]
328
+
329
+
330
+ def _write_seed_jsonl(
331
+ path: Path,
332
+ repo_root: Path,
333
+ task_dir: Path,
334
+ task_data: dict,
335
+ ) -> None:
336
+ """Write 2–3 JSONL rows with real spec ``file`` paths for sub-agent context."""
337
+ spec_paths = _resolve_seed_spec_paths(repo_root, task_dir, task_data)
338
+ if len(spec_paths) < 2:
339
+ raise ValueError(
340
+ "task create requires at least two existing spec guide paths for jsonl seed"
341
+ )
342
+
343
+ lines: list[str] = []
344
+ for spec_path in spec_paths:
345
+ lines.append(
346
+ json.dumps(
347
+ {
348
+ "file": spec_path,
349
+ "reason": "default spec seed — curate entries for this task",
350
+ },
351
+ ensure_ascii=False,
352
+ )
353
+ )
354
+ path.write_text("\n".join(lines) + "\n", encoding="utf-8")
355
+
356
+
357
+ def _default_prd_content(
358
+ title: str,
359
+ description: str | None = None,
360
+ *,
361
+ repo_root: Path | None = None,
362
+ task_dir: Path | None = None,
363
+ ) -> str:
364
+ """Return the default PRD skeleton created with every task."""
365
+ root = repo_root or get_repo_root()
366
+ locale = resolve_artifact_locale(task_dir, root)
367
+ return default_prd_content(title, description, locale, root)
368
+
369
+
370
+ def _kernel_patch_task_json(
371
+ task_dir: Path,
372
+ data: dict,
373
+ *,
374
+ actor: str,
375
+ op: str,
376
+ evidence: str,
377
+ ) -> bool:
378
+ """Persist mutated task.json fields through Kernel patch (not task-map.md)."""
379
+ extras = collect_kernel_projection_extras(data)
380
+ if "depends_on" in data:
381
+ extras["depends_on"] = data["depends_on"]
382
+ try:
383
+ expected = kernel_expected_revision(task_dir)
384
+ kernel_patch(
385
+ task_dir,
386
+ data,
387
+ extras,
388
+ expected_revision=expected,
389
+ actor=actor,
390
+ idempotency_key=f"patch:{op}:{task_dir.name}:r{expected}",
391
+ evidence=evidence,
392
+ )
393
+ return True
394
+ except (KernelCliNotFound, KernelCommandError) as err:
395
+ print_kernel_error(err)
396
+ return False
397
+
398
+
399
+ # =============================================================================
400
+ # Command: create
401
+ # =============================================================================
402
+
403
+ def cmd_create(args: argparse.Namespace) -> int:
404
+ """Create a new task."""
405
+ repo_root = get_repo_root()
406
+
407
+ if not args.title:
408
+ print(colored("Error: title is required", Colors.RED), file=sys.stderr)
409
+ return 1
410
+
411
+ # Validate --package (CLI source: fail-fast)
412
+ package: str | None = getattr(args, "package", None)
413
+ if not is_monorepo(repo_root):
414
+ # Single-repo: ignore --package, no package prefix
415
+ if package:
416
+ print(colored(f"Warning: --package ignored in single-repo project", Colors.YELLOW), file=sys.stderr)
417
+ package = None
418
+ elif package:
419
+ if not validate_package(package, repo_root):
420
+ packages = get_packages(repo_root)
421
+ available = ", ".join(sorted(packages.keys())) if packages else "(none)"
422
+ print(colored(f"Error: unknown package '{package}'. Available: {available}", Colors.RED), file=sys.stderr)
423
+ return 1
424
+ else:
425
+ # Inferred: default_package → None (no task.json yet for create)
426
+ package = resolve_package(repo_root=repo_root)
427
+
428
+ # Default assignee to current developer
429
+ assignee = args.assignee
430
+ if not assignee:
431
+ assignee = get_developer(repo_root)
432
+ if not assignee:
433
+ print(colored("Error: No developer set. Run init_developer.py first or use --assignee", Colors.RED), file=sys.stderr)
434
+ return 1
435
+
436
+ ensure_tasks_dir(repo_root)
437
+
438
+ # Get current developer as creator
439
+ creator = get_developer(repo_root) or assignee
440
+
441
+ # Generate slug if not provided
442
+ slug = args.slug or _slugify(args.title)
443
+ if not slug:
444
+ print(colored("Error: could not generate slug from title", Colors.RED), file=sys.stderr)
445
+ return 1
446
+
447
+ # Create task directory with MM-DD-slug format
448
+ tasks_dir = get_tasks_dir(repo_root)
449
+ date_prefix = generate_task_date_prefix()
450
+ dir_name = f"{date_prefix}-{slug}"
451
+ task_dir = tasks_dir / dir_name
452
+ task_json_path = task_dir / FILE_TASK_JSON
453
+
454
+ archived_task_dir = _find_archived_task_by_dir_name(tasks_dir, dir_name)
455
+ if archived_task_dir:
456
+ print(colored(f"Error: Task already archived: {dir_name}", Colors.RED), file=sys.stderr)
457
+ print(f"Archived at: {_repo_relative_path(archived_task_dir, repo_root)}", file=sys.stderr)
458
+ print("Use a new slug if you intend to create a new task.", file=sys.stderr)
459
+ return 1
460
+
461
+ if task_dir.exists():
462
+ print(colored(f"Warning: Task directory already exists: {dir_name}", Colors.YELLOW), file=sys.stderr)
463
+ else:
464
+ task_dir.mkdir(parents=True)
465
+
466
+ today = datetime.now().strftime("%Y-%m-%d")
467
+
468
+ # Record current branch as base_branch (PR target)
469
+ _, branch_out, _ = run_git(["branch", "--show-current"], cwd=repo_root)
470
+ current_branch = branch_out.strip() or "main"
471
+
472
+ task_data = {
473
+ "id": slug,
474
+ "name": slug,
475
+ "title": args.title,
476
+ "description": args.description or "",
477
+ "status": "planning",
478
+ "dev_type": None,
479
+ "scope": None,
480
+ "package": package,
481
+ "priority": args.priority,
482
+ "creator": creator,
483
+ "assignee": assignee,
484
+ "createdAt": today,
485
+ "completedAt": None,
486
+ "branch": None,
487
+ "base_branch": current_branch,
488
+ "worktree_path": None,
489
+ "commit": None,
490
+ "pr_url": None,
491
+ "subtasks": [],
492
+ "children": [],
493
+ "parent": None,
494
+ "relatedFiles": [],
495
+ "notes": "",
496
+ "meta": {},
497
+ }
498
+
499
+ parent_dir = None
500
+ parent_data = None
501
+ if args.parent:
502
+ parent_dir = resolve_task_dir(args.parent, repo_root)
503
+ parent_json_path = parent_dir / FILE_TASK_JSON
504
+ if not parent_json_path.is_file():
505
+ print(colored(f"Warning: Parent task.json not found: {args.parent}", Colors.YELLOW), file=sys.stderr)
506
+ else:
507
+ parent_data = read_json(parent_json_path)
508
+ if parent_data:
509
+ task_data["parent"] = parent_dir.name
510
+
511
+ try:
512
+ kernel_create(
513
+ task_dir,
514
+ task_data,
515
+ actor="task.py create",
516
+ idempotency_key=f"create:{slug}",
517
+ evidence="task.py create",
518
+ )
519
+ except (KernelCliNotFound, KernelCommandError) as err:
520
+ print_kernel_error(err)
521
+ return 1
522
+
523
+ persisted = read_json(task_json_path)
524
+ if persisted:
525
+ task_data = persisted
526
+
527
+ prd_path = task_dir / "prd.md"
528
+ if not prd_path.exists():
529
+ prd_path.write_text(
530
+ _default_prd_content(args.title, args.description, repo_root=repo_root, task_dir=task_dir),
531
+ encoding="utf-8",
532
+ )
533
+
534
+ verify_path = task_dir / "verify.md"
535
+ if not verify_path.exists():
536
+ verify_path.write_text(
537
+ default_verify_content(repo_root=repo_root, task_dir=task_dir),
538
+ encoding="utf-8",
539
+ )
540
+
541
+ # Seed implement.jsonl / check.jsonl for sub-agent-capable platforms.
542
+ # Agent curates real entries during planning when the task needs them.
543
+ # Agent-less platforms (Kilo / Antigravity / Windsurf) skip this — they
544
+ # load specs via the pactile-before-dev skill instead of JSONL.
545
+ seeded_jsonl = False
546
+ if _has_subagent_platform(repo_root):
547
+ for jsonl_name in ("implement.jsonl", "check.jsonl"):
548
+ jsonl_path = task_dir / jsonl_name
549
+ if not jsonl_path.exists():
550
+ _write_seed_jsonl(jsonl_path, repo_root, task_dir, task_data)
551
+ seeded_jsonl = True
552
+
553
+ # Handle --parent: child's `parent` is in the Kernel create record above;
554
+ # parent `children[]` goes through Kernel patch. task-map.md stays Python.
555
+ if parent_dir is not None and parent_data:
556
+ parent_children = parent_data.get("children", [])
557
+ if dir_name not in parent_children:
558
+ parent_children.append(dir_name)
559
+ parent_data["children"] = parent_children
560
+ if not _kernel_patch_task_json(
561
+ parent_dir,
562
+ parent_data,
563
+ actor="task.py create",
564
+ op="create-parent-link",
565
+ evidence="children[]",
566
+ ):
567
+ return 1
568
+ ensure_task_map(
569
+ parent_dir,
570
+ parent_data,
571
+ list(parent_data.get("children", [])),
572
+ f"Linked Child `{dir_name}`.",
573
+ )
574
+ print(colored(f"Linked as child of: {parent_dir.name}", Colors.GREEN), file=sys.stderr)
575
+
576
+ print(colored(f"Created task: {dir_name}", Colors.GREEN), file=sys.stderr)
577
+ print("", file=sys.stderr)
578
+ print(colored("Next steps:", Colors.BLUE), file=sys.stderr)
579
+ print(" - Fill prd.md with requirements and acceptance criteria", file=sys.stderr)
580
+ print(f" - Select it when ready: python ./.pactile/scripts/task.py select {DIR_WORKFLOW}/{DIR_TASKS}/{dir_name}", file=sys.stderr)
581
+ print(" - Personal Lite: Definition=prd.md, Evidence=verify.md; start-execution --check is not approval", file=sys.stderr)
582
+ print(" - Lightweight task: PRD-only is valid", file=sys.stderr)
583
+ print(" - Complex task: add design.md and implement.md before task.py start-execution --check", file=sys.stderr)
584
+ if seeded_jsonl:
585
+ print(
586
+ " - Curate implement.jsonl / check.jsonl as spec/research manifests when sub-agents need context",
587
+ file=sys.stderr,
588
+ )
589
+ print(" - Use /pactile:continue or phase context to decide the next step", file=sys.stderr)
590
+ print("", file=sys.stderr)
591
+
592
+ # Output relative path for script chaining
593
+ print(f"{DIR_WORKFLOW}/{DIR_TASKS}/{dir_name}")
594
+
595
+ run_task_hooks("after_create", task_json_path, repo_root)
596
+ return 0
597
+
598
+
599
+ # =============================================================================
600
+ # Command: archive / prepare-archive-evidence
601
+ # =============================================================================
602
+
603
+ def cmd_prepare_archive_evidence(args: argparse.Namespace) -> int:
604
+ """Append missing archive evidence sections to verify.md (non-destructive)."""
605
+ repo_root = get_repo_root()
606
+ task_name = args.name
607
+ if not task_name:
608
+ print(colored("Error: Task name is required", Colors.RED), file=sys.stderr)
609
+ return 1
610
+
611
+ task_dir = resolve_task_dir(task_name, repo_root)
612
+ if not task_dir or not task_dir.is_dir():
613
+ print(colored(f"Error: Task not found: {task_name}", Colors.RED), file=sys.stderr)
614
+ return 1
615
+
616
+ task_json_path = task_dir / FILE_TASK_JSON
617
+ task_data = read_json(task_json_path) if task_json_path.is_file() else None
618
+ dry_run = getattr(args, "dry_run", False)
619
+
620
+ changed, messages = prepare_archive_evidence(
621
+ task_dir, task_data, dry_run=dry_run
622
+ )
623
+ for msg in messages:
624
+ print(msg)
625
+ if not changed and messages and messages[0].startswith("task.json"):
626
+ return 1
627
+
628
+ guard = validate_archive(task_dir, task_data)
629
+ if guard.ok:
630
+ print(colored("Archive check: PASS (after prepare)", Colors.GREEN))
631
+ return 0
632
+ print(colored("Archive check: still blocked", Colors.YELLOW))
633
+ for item in guard.errors:
634
+ print(f" - {item}")
635
+ if task_data is not None:
636
+ hints = archive_repair_hints(guard.errors, task_dir, task_data, guard)
637
+ if hints:
638
+ print(colored("Next steps:", Colors.BLUE))
639
+ for hint in hints:
640
+ print(f" - {hint}")
641
+ return 0 if changed else 1
642
+
643
+
644
+ def cmd_prepare_learning_scaffold(args: argparse.Namespace) -> int:
645
+ """Print spec-update scaffolding for a task (stdout only; does not edit specs)."""
646
+ repo_root = get_repo_root()
647
+ task_name = args.name
648
+ if not task_name:
649
+ print(colored("Error: Task name is required", Colors.RED), file=sys.stderr)
650
+ return 1
651
+
652
+ task_dir = resolve_task_dir(task_name, repo_root)
653
+ if not task_dir or not task_dir.is_dir():
654
+ print(colored(f"Error: Task not found: {task_name}", Colors.RED), file=sys.stderr)
655
+ return 1
656
+
657
+ task_json_path = task_dir / FILE_TASK_JSON
658
+ task_data = read_json(task_json_path) if task_json_path.is_file() else None
659
+ if task_data is None:
660
+ print(colored("Error: task.json missing or invalid", Colors.RED), file=sys.stderr)
661
+ return 1
662
+
663
+ trigger = getattr(args, "trigger", None)
664
+ print(build_spec_update_scaffold(repo_root, task_dir, task_data, trigger=trigger))
665
+ return 0
666
+
667
+
668
+ def _integrated_children_still_active(
669
+ parent_dir: Path,
670
+ child_names: list[str],
671
+ tasks_dir: Path,
672
+ ) -> list[tuple[str, Path]]:
673
+ """Return (name, dir) for integrated children that remain in the active set."""
674
+ pending: list[tuple[str, Path]] = []
675
+ for child_name in child_names:
676
+ if not isinstance(child_name, str):
677
+ continue
678
+ if get_child_state(parent_dir, child_name) != "integrated":
679
+ continue
680
+ child_dir = find_task_by_name(child_name, tasks_dir)
681
+ if child_dir and child_dir.is_dir():
682
+ pending.append((child_name, child_dir))
683
+ return pending
684
+
685
+
686
+ def _archive_one_task(
687
+ task_dir: Path,
688
+ repo_root: Path,
689
+ tasks_dir: Path,
690
+ *,
691
+ no_commit: bool,
692
+ ) -> tuple[bool, list[str], str | None]:
693
+ """Archive a single task directory after validate_archive passed.
694
+
695
+ Returns (success, modified_child_names, archived_relative_path_or_none).
696
+ """
697
+ dir_name = task_dir.name
698
+ task_json_path = task_dir / FILE_TASK_JSON
699
+ task_data = read_json(task_json_path) if task_json_path.is_file() else None
700
+ guard = validate_archive(task_dir, task_data)
701
+ if not guard.ok:
702
+ return False, [], None
703
+
704
+ today = datetime.now().strftime("%Y-%m-%d")
705
+ modified_children: list[str] = []
706
+ if task_data:
707
+ data = task_data
708
+ if guard.baseline_record:
709
+ write_gate_record(data, "full-task-complete", BASELINE_GATE, guard.baseline_record)
710
+ data["status"] = "completed"
711
+ data["completedAt"] = today
712
+
713
+ year_month = datetime.now().strftime("%Y-%m")
714
+ archived_rel_guess = (
715
+ f"{DIR_WORKFLOW}/{DIR_TASKS}/{DIR_ARCHIVE}/{year_month}/{dir_name}"
716
+ )
717
+ data["notes_projection"] = build_notes_projection(
718
+ task_dir, data, archived_rel=archived_rel_guess
719
+ )
720
+
721
+ extras = collect_kernel_projection_extras(data)
722
+ try:
723
+ expected = kernel_expected_revision(task_dir)
724
+ kernel_archive(
725
+ task_dir,
726
+ data,
727
+ extras,
728
+ expected_revision=expected,
729
+ actor="task.py archive",
730
+ idempotency_key=f"archive:{dir_name}:{today}",
731
+ evidence="task.py archive",
732
+ )
733
+ except (KernelCliNotFound, KernelCommandError) as err:
734
+ print_kernel_error(err)
735
+ return False, [], None
736
+
737
+ task_children = data.get("children", [])
738
+ if task_children:
739
+ for child_name in task_children:
740
+ child_dir_path = find_task_by_name(child_name, tasks_dir)
741
+ if child_dir_path:
742
+ child_json = child_dir_path / FILE_TASK_JSON
743
+ if child_json.is_file():
744
+ child_data = read_json(child_json)
745
+ if child_data:
746
+ child_data["parent"] = None
747
+ if not _kernel_patch_task_json(
748
+ child_dir_path,
749
+ child_data,
750
+ actor="task.py archive",
751
+ op="archive-clear-parent",
752
+ evidence="parent",
753
+ ):
754
+ return False, modified_children, None
755
+ modified_children.append(child_dir_path.name)
756
+
757
+ from .active_task import clear_task_from_sessions
758
+
759
+ profile = task_closeout_profile(task_dir, task_data) if task_data else "lite"
760
+ clear_task_from_sessions(str(task_dir), repo_root)
761
+ result = archive_task_complete(task_dir, repo_root)
762
+ if "archived_to" not in result:
763
+ if profile == "lite":
764
+ print(
765
+ colored(
766
+ "Close Outcome written; physical archive/retention skipped "
767
+ "(not a Lite completion condition).",
768
+ Colors.YELLOW,
769
+ ),
770
+ file=sys.stderr,
771
+ )
772
+ return True, modified_children, f"{DIR_WORKFLOW}/{DIR_TASKS}/{dir_name}"
773
+ return False, modified_children, None
774
+
775
+ archive_dest = Path(result["archived_to"])
776
+ year_month = archive_dest.parent.name
777
+ print(
778
+ colored(f"Archived: {dir_name} -> archive/{year_month}/", Colors.GREEN),
779
+ file=sys.stderr,
780
+ )
781
+
782
+ if not no_commit:
783
+ if not _auto_commit_archive(dir_name, repo_root, modified_children):
784
+ if profile == "lite":
785
+ print(
786
+ colored(
787
+ "Lite Close Outcome stands; git auto-commit skipped or failed "
788
+ "(VCS is On-demand, not a completion condition).",
789
+ Colors.YELLOW,
790
+ ),
791
+ file=sys.stderr,
792
+ )
793
+ else:
794
+ print(
795
+ colored(
796
+ "Archive moved on disk, but git auto-commit did not complete. "
797
+ "Resolve `git status` before continuing.",
798
+ Colors.RED,
799
+ ),
800
+ file=sys.stderr,
801
+ )
802
+ return False, modified_children, None
803
+
804
+ rel = f"{DIR_WORKFLOW}/{DIR_TASKS}/{DIR_ARCHIVE}/{year_month}/{dir_name}"
805
+ archived_json = archive_dest / FILE_TASK_JSON
806
+ run_task_hooks("after_archive", archived_json, repo_root)
807
+ return True, modified_children, rel
808
+
809
+
810
+ def cmd_archive(args: argparse.Namespace) -> int:
811
+ """Archive completed task."""
812
+ repo_root = get_repo_root()
813
+ task_name = args.name
814
+
815
+ if not task_name:
816
+ print(colored("Error: Task name is required", Colors.RED), file=sys.stderr)
817
+ return 1
818
+
819
+ tasks_dir = get_tasks_dir(repo_root)
820
+
821
+ # Resolve task directory (supports task name, relative path, or absolute path)
822
+ task_dir = resolve_task_dir(task_name, repo_root)
823
+
824
+ if not task_dir or not task_dir.is_dir():
825
+ print(colored(f"Error: Task not found: {task_name}", Colors.RED), file=sys.stderr)
826
+ print("Active tasks:", file=sys.stderr)
827
+ # Import lazily to avoid circular dependency
828
+ from .tasks import iter_active_tasks
829
+ for t in iter_active_tasks(tasks_dir):
830
+ print(f" - {t.dir_name}/", file=sys.stderr)
831
+ return 1
832
+
833
+ dir_name = task_dir.name
834
+ task_json_path = task_dir / FILE_TASK_JSON
835
+ task_data = read_json(task_json_path) if task_json_path.is_file() else None
836
+
837
+ guard = validate_archive(task_dir, task_data)
838
+ task_children_raw = (
839
+ task_data.get("children", []) if isinstance(task_data, dict) else []
840
+ )
841
+ structural_children = [
842
+ name for name in task_children_raw if isinstance(name, str)
843
+ ]
844
+ cascade = getattr(args, "archive_integrated_children", False)
845
+ pending_integrated = _integrated_children_still_active(
846
+ task_dir, structural_children, tasks_dir
847
+ )
848
+
849
+ if getattr(args, "check", False):
850
+ if not guard.ok:
851
+ print(colored("Archive check: FAIL", Colors.RED))
852
+ for item in guard.errors:
853
+ print(f" - {item}")
854
+ if task_data is not None:
855
+ hints = archive_repair_hints(
856
+ guard.errors, task_dir, task_data, guard
857
+ )
858
+ if hints:
859
+ print(colored("Next steps:", Colors.BLUE))
860
+ for hint in hints:
861
+ print(f" - {hint}")
862
+ return 1
863
+ if cascade and pending_integrated:
864
+ for child_name, child_dir in pending_integrated:
865
+ child_data = read_json(child_dir / FILE_TASK_JSON)
866
+ child_guard = validate_archive(child_dir, child_data)
867
+ if not child_guard.ok:
868
+ print(colored("Archive check: FAIL", Colors.RED))
869
+ print(
870
+ f" - integrated child {child_name} not ready to archive:"
871
+ )
872
+ for item in child_guard.errors:
873
+ print(f" - {item}")
874
+ if child_data is not None:
875
+ hints = archive_repair_hints(
876
+ child_guard.errors, child_dir, child_data, child_guard
877
+ )
878
+ if hints:
879
+ print(colored("Next steps:", Colors.BLUE))
880
+ for hint in hints:
881
+ print(f" - {hint}")
882
+ return 1
883
+ print(colored("Archive check: PASS", Colors.GREEN))
884
+ print(f"Contract fingerprint: {guard.contract_fingerprint}")
885
+ if guard.required_gates:
886
+ print(f"Required completion gates: {', '.join(guard.required_gates)}")
887
+ if pending_integrated and not cascade:
888
+ hint = parent_archive_child_followup_hint(
889
+ task_dir, structural_children, tasks_dir
890
+ )
891
+ if hint:
892
+ print(hint)
893
+ if cascade and pending_integrated:
894
+ names = ", ".join(n for n, _ in pending_integrated)
895
+ print(
896
+ f"Cascade: {len(pending_integrated)} integrated child dir(s) "
897
+ f"would archive with parent: {names}"
898
+ )
899
+ return 0
900
+
901
+ if not guard.ok:
902
+ print(colored("Error: cannot archive task; completion check failed.", Colors.RED), file=sys.stderr)
903
+ for item in guard.errors:
904
+ print(f" - {item}", file=sys.stderr)
905
+ if task_data is not None:
906
+ hints = archive_repair_hints(guard.errors, task_dir, task_data, guard)
907
+ if hints:
908
+ print(colored("Next steps:", Colors.BLUE), file=sys.stderr)
909
+ for hint in hints:
910
+ print(f" - {hint}", file=sys.stderr)
911
+ print("Run `task.py archive <task> --check` for a non-mutating preflight.", file=sys.stderr)
912
+ return 1
913
+
914
+ no_commit = getattr(args, "no_commit", False)
915
+ if cascade and pending_integrated:
916
+ for child_name, child_dir in pending_integrated:
917
+ child_data = read_json(child_dir / FILE_TASK_JSON)
918
+ child_guard = validate_archive(child_dir, child_data)
919
+ if not child_guard.ok:
920
+ print(
921
+ colored(
922
+ f"Error: integrated child {child_name} failed archive check.",
923
+ Colors.RED,
924
+ ),
925
+ file=sys.stderr,
926
+ )
927
+ for item in child_guard.errors:
928
+ print(f" - {item}", file=sys.stderr)
929
+ return 1
930
+ for child_name, child_dir in pending_integrated:
931
+ ok, _, _ = _archive_one_task(
932
+ child_dir, repo_root, tasks_dir, no_commit=no_commit
933
+ )
934
+ if not ok:
935
+ print(
936
+ colored(
937
+ f"Error: failed to archive integrated child {child_name}.",
938
+ Colors.RED,
939
+ ),
940
+ file=sys.stderr,
941
+ )
942
+ return 1
943
+
944
+ manual_child_hint = None
945
+ if pending_integrated and not cascade:
946
+ manual_child_hint = parent_archive_child_followup_hint(
947
+ task_dir, structural_children, tasks_dir
948
+ )
949
+
950
+ ok, _, rel_path = _archive_one_task(
951
+ task_dir, repo_root, tasks_dir, no_commit=no_commit
952
+ )
953
+ if not ok or not rel_path:
954
+ return 1
955
+
956
+ if manual_child_hint:
957
+ print(colored("Note:", Colors.YELLOW), file=sys.stderr)
958
+ print(manual_child_hint, file=sys.stderr)
959
+
960
+ print(rel_path)
961
+ return 0
962
+
963
+
964
+ def _auto_commit_archive(
965
+ task_name: str,
966
+ repo_root: Path,
967
+ modified_children: list[str] | None = None,
968
+ ) -> bool:
969
+ """Stage Pactile-owned task paths and commit after archive.
970
+
971
+ Scoped narrowly to the archived task's source + destination paths
972
+ plus any child task dirs whose ``task.json`` was edited (parent →
973
+ children relationship update). Dirty changes in OTHER active task
974
+ dirs are NOT bundled into the archive commit.
975
+
976
+ If ``.gitignore`` blocks the paths, we warn + skip — we do NOT
977
+ retry with ``git add -f``. The warning explicitly forbids
978
+ ``git add -f .pactile/`` (which would fan out to caches/backups)
979
+ and points users at ``session_auto_commit: false``.
980
+
981
+ Honors ``session_auto_commit`` in ``.pactile/config.yaml``: when
982
+ set to ``false``, this function returns immediately without
983
+ touching git (the archive directory move on disk is unaffected).
984
+ """
985
+ if not is_git_worktree(repo_root):
986
+ print(
987
+ "[OK] no Git worktree — skipping archive auto-commit "
988
+ "(Close Outcome already written).",
989
+ file=sys.stderr,
990
+ )
991
+ return True
992
+
993
+ if not get_session_auto_commit(repo_root):
994
+ print(
995
+ "[OK] session_auto_commit: false — skipping git stage/commit.",
996
+ file=sys.stderr,
997
+ )
998
+ return True
999
+
1000
+ source_rel = f"{DIR_WORKFLOW}/{DIR_TASKS}/{task_name}"
1001
+ rc, tracked_out, _ = run_git(
1002
+ ["ls-files", "--", source_rel],
1003
+ cwd=repo_root,
1004
+ )
1005
+ source_was_tracked = rc == 0 and bool(tracked_out.strip())
1006
+
1007
+ paths = safe_archive_paths_to_add(
1008
+ repo_root, task_name=task_name, modified_children=modified_children
1009
+ )
1010
+ if not paths:
1011
+ print("[OK] No task changes to commit.", file=sys.stderr)
1012
+ return True
1013
+
1014
+ success, _, err = safe_git_add(paths, repo_root)
1015
+ if not success:
1016
+ if err and "ignored by" in err.lower():
1017
+ print_gitignore_warning(paths)
1018
+ else:
1019
+ print(
1020
+ f"[WARN] git add failed: {err.strip() if err else 'unknown error'}",
1021
+ file=sys.stderr,
1022
+ )
1023
+ return not source_was_tracked
1024
+
1025
+ # Belt-and-suspenders for the phantom-delete bug: `safe_git_add` uses
1026
+ # `git add` (no -A) which only stages additions/modifications. The
1027
+ # source task directory was moved away by `shutil.move`, so its files
1028
+ # need an explicit `git rm --cached` to stage the deletions in this
1029
+ # same commit — otherwise they sit as uncommitted "phantom deletes"
1030
+ # against HEAD until something later picks them up.
1031
+ #
1032
+ # `--ignore-unmatch` makes this a no-op when the task was never tracked
1033
+ # (e.g. archiving a task that lived only in working tree).
1034
+ run_git(
1035
+ ["rm", "-r", "--cached", "--ignore-unmatch", "--", source_rel],
1036
+ cwd=repo_root,
1037
+ )
1038
+
1039
+ rc, _, _ = run_git(
1040
+ ["diff", "--cached", "--quiet", "--", *paths, source_rel],
1041
+ cwd=repo_root,
1042
+ )
1043
+ if rc == 0:
1044
+ print("[OK] No task changes to commit.", file=sys.stderr)
1045
+ return True
1046
+
1047
+ commit_msg = f"chore(task): archive {task_name}"
1048
+ rc, _, err = run_git(["commit", "-m", commit_msg], cwd=repo_root)
1049
+ if rc == 0:
1050
+ print(f"[OK] Auto-committed: {commit_msg}", file=sys.stderr)
1051
+ return True
1052
+ else:
1053
+ print(f"[WARN] Auto-commit failed: {err.strip()}", file=sys.stderr)
1054
+ return not source_was_tracked
1055
+
1056
+
1057
+ # =============================================================================
1058
+ # Command: add-subtask
1059
+ # =============================================================================
1060
+
1061
+ def cmd_add_subtask(args: argparse.Namespace) -> int:
1062
+ """Link a child task to a parent task."""
1063
+ repo_root = get_repo_root()
1064
+
1065
+ parent_dir = resolve_task_dir(args.parent_dir, repo_root)
1066
+ child_dir = resolve_task_dir(args.child_dir, repo_root)
1067
+
1068
+ parent_json_path = parent_dir / FILE_TASK_JSON
1069
+ child_json_path = child_dir / FILE_TASK_JSON
1070
+
1071
+ if not parent_json_path.is_file():
1072
+ print(colored(f"Error: Parent task.json not found: {args.parent_dir}", Colors.RED), file=sys.stderr)
1073
+ return 1
1074
+
1075
+ if not child_json_path.is_file():
1076
+ print(colored(f"Error: Child task.json not found: {args.child_dir}", Colors.RED), file=sys.stderr)
1077
+ return 1
1078
+
1079
+ parent_data = read_json(parent_json_path)
1080
+ child_data = read_json(child_json_path)
1081
+
1082
+ if not parent_data or not child_data:
1083
+ print(colored("Error: Failed to read task.json", Colors.RED), file=sys.stderr)
1084
+ return 1
1085
+
1086
+ # Check if child already has a parent
1087
+ existing_parent = child_data.get("parent")
1088
+ if existing_parent:
1089
+ print(colored(f"Error: Child task already has a parent: {existing_parent}", Colors.RED), file=sys.stderr)
1090
+ return 1
1091
+
1092
+ # Add child to parent's children list
1093
+ parent_children = parent_data.get("children", [])
1094
+ child_dir_name = child_dir.name
1095
+ if child_dir_name not in parent_children:
1096
+ parent_children.append(child_dir_name)
1097
+ parent_data["children"] = parent_children
1098
+
1099
+ # Set parent in child's task.json
1100
+ child_data["parent"] = parent_dir.name
1101
+
1102
+ parent_id = str(parent_data.get("id") or parent_dir.name)
1103
+ parent_existing = (
1104
+ parent_data.get("topology")
1105
+ if isinstance(parent_data.get("topology"), dict)
1106
+ else {}
1107
+ )
1108
+ parent_data["topology"] = default_topology(
1109
+ parent=parent_existing.get("parent_id") or parent_data.get("parent"),
1110
+ children=parent_children,
1111
+ )
1112
+ child_existing = (
1113
+ child_data.get("topology")
1114
+ if isinstance(child_data.get("topology"), dict)
1115
+ else {}
1116
+ )
1117
+ child_children = list(
1118
+ child_existing.get("children") or child_data.get("children") or []
1119
+ )
1120
+ try:
1121
+ child_data["topology"] = assign_parent(
1122
+ default_topology(parent=None, children=child_children),
1123
+ parent_id,
1124
+ )
1125
+ except TopologyError as err:
1126
+ print(colored(f"Error: {err}", Colors.RED), file=sys.stderr)
1127
+ return 1
1128
+
1129
+ if not _kernel_patch_task_json(
1130
+ parent_dir,
1131
+ parent_data,
1132
+ actor="task.py add-subtask",
1133
+ op="add-subtask",
1134
+ evidence="children[]",
1135
+ ):
1136
+ return 1
1137
+ if not _kernel_patch_task_json(
1138
+ child_dir,
1139
+ child_data,
1140
+ actor="task.py add-subtask",
1141
+ op="add-subtask",
1142
+ evidence="parent",
1143
+ ):
1144
+ return 1
1145
+ ensure_task_map(
1146
+ parent_dir,
1147
+ parent_data,
1148
+ list(parent_data.get("children", [])),
1149
+ f"Linked Child `{child_dir_name}`.",
1150
+ )
1151
+
1152
+ print(colored(f"Linked: {child_dir.name} -> {parent_dir.name}", Colors.GREEN), file=sys.stderr)
1153
+ return 0
1154
+
1155
+
1156
+ # =============================================================================
1157
+ # Command: remove-subtask
1158
+ # =============================================================================
1159
+
1160
+ def cmd_remove_subtask(args: argparse.Namespace) -> int:
1161
+ """Unlink a child task from a parent task."""
1162
+ repo_root = get_repo_root()
1163
+
1164
+ parent_dir = resolve_task_dir(args.parent_dir, repo_root)
1165
+ child_dir = resolve_task_dir(args.child_dir, repo_root)
1166
+
1167
+ parent_json_path = parent_dir / FILE_TASK_JSON
1168
+ child_json_path = child_dir / FILE_TASK_JSON
1169
+
1170
+ if not parent_json_path.is_file():
1171
+ print(colored(f"Error: Parent task.json not found: {args.parent_dir}", Colors.RED), file=sys.stderr)
1172
+ return 1
1173
+
1174
+ if not child_json_path.is_file():
1175
+ print(colored(f"Error: Child task.json not found: {args.child_dir}", Colors.RED), file=sys.stderr)
1176
+ return 1
1177
+
1178
+ parent_data = read_json(parent_json_path)
1179
+ child_data = read_json(child_json_path)
1180
+
1181
+ if not parent_data or not child_data:
1182
+ print(colored("Error: Failed to read task.json", Colors.RED), file=sys.stderr)
1183
+ return 1
1184
+
1185
+ # Remove child from parent's children list
1186
+ parent_children = parent_data.get("children", [])
1187
+ child_dir_name = child_dir.name
1188
+ if child_dir_name in parent_children:
1189
+ parent_children.remove(child_dir_name)
1190
+ parent_data["children"] = parent_children
1191
+
1192
+ # Clear parent in child's task.json
1193
+ child_data["parent"] = None
1194
+
1195
+ parent_existing = (
1196
+ parent_data.get("topology")
1197
+ if isinstance(parent_data.get("topology"), dict)
1198
+ else {}
1199
+ )
1200
+ parent_data["topology"] = default_topology(
1201
+ parent=parent_existing.get("parent_id") or parent_data.get("parent"),
1202
+ children=parent_children,
1203
+ )
1204
+ child_existing = (
1205
+ child_data.get("topology")
1206
+ if isinstance(child_data.get("topology"), dict)
1207
+ else {}
1208
+ )
1209
+ child_data["topology"] = default_topology(
1210
+ parent=None,
1211
+ children=list(child_existing.get("children") or child_data.get("children") or []),
1212
+ )
1213
+
1214
+ if not _kernel_patch_task_json(
1215
+ parent_dir,
1216
+ parent_data,
1217
+ actor="task.py remove-subtask",
1218
+ op="remove-subtask",
1219
+ evidence="children[]",
1220
+ ):
1221
+ return 1
1222
+ if not _kernel_patch_task_json(
1223
+ child_dir,
1224
+ child_data,
1225
+ actor="task.py remove-subtask",
1226
+ op="remove-subtask",
1227
+ evidence="parent",
1228
+ ):
1229
+ return 1
1230
+ remove_child_from_task_map(parent_dir, parent_data, child_dir_name)
1231
+
1232
+ print(colored(f"Unlinked: {child_dir.name} from {parent_dir.name}", Colors.GREEN), file=sys.stderr)
1233
+ return 0
1234
+
1235
+
1236
+ # =============================================================================
1237
+ # Command: prepare-child-worktree
1238
+ # =============================================================================
1239
+
1240
+ def cmd_prepare_child_worktree(args: argparse.Namespace) -> int:
1241
+ """Create and register a Git worktree for a Child task."""
1242
+ repo_root = get_repo_root()
1243
+
1244
+ parent_dir = resolve_task_dir(args.parent_dir, repo_root)
1245
+ child_dir = resolve_task_dir(args.child_dir, repo_root)
1246
+
1247
+ parent_json_path = parent_dir / FILE_TASK_JSON
1248
+ child_json_path = child_dir / FILE_TASK_JSON
1249
+
1250
+ if not parent_json_path.is_file():
1251
+ print(colored(f"Error: Parent task.json not found: {args.parent_dir}", Colors.RED), file=sys.stderr)
1252
+ return 1
1253
+ if not child_json_path.is_file():
1254
+ print(colored(f"Error: Child task.json not found: {args.child_dir}", Colors.RED), file=sys.stderr)
1255
+ return 1
1256
+
1257
+ parent_data = read_json(parent_json_path)
1258
+ child_data = read_json(child_json_path)
1259
+ if not parent_data or not child_data:
1260
+ print(colored("Error: Failed to read task.json", Colors.RED), file=sys.stderr)
1261
+ return 1
1262
+
1263
+ if child_data.get("parent") != parent_dir.name:
1264
+ print(colored(f"Error: Child is not linked to parent: {child_dir.name}", Colors.RED), file=sys.stderr)
1265
+ return 1
1266
+
1267
+ branch = args.branch
1268
+ base_ref = getattr(args, "base", None) or child_data.get("base_branch") or parent_data.get("base_branch") or "HEAD"
1269
+ worktree_path, path_error = _resolve_child_worktree_path(
1270
+ repo_root,
1271
+ child_dir,
1272
+ getattr(args, "path", None),
1273
+ )
1274
+
1275
+ errors: list[str] = []
1276
+ errors.extend(_validate_git_repo(repo_root))
1277
+ errors.extend(_validate_branch_name(repo_root, branch))
1278
+ if path_error:
1279
+ errors.append(path_error)
1280
+ if worktree_path and worktree_path.exists():
1281
+ errors.append(f"worktree path already exists: {_repo_relative_path(worktree_path, repo_root)}")
1282
+ if not _git_commit_exists(repo_root, base_ref):
1283
+ errors.append(f"base ref does not resolve to a commit: {base_ref}")
1284
+
1285
+ if errors:
1286
+ print("Prepare-child-worktree check: FAIL" if getattr(args, "check", False) else colored("Error: cannot prepare child worktree.", Colors.RED), file=sys.stderr)
1287
+ for item in errors:
1288
+ print(f" - {item}", file=sys.stderr)
1289
+ print_environment_repair_hints(errors)
1290
+ return 1
1291
+
1292
+ assert worktree_path is not None
1293
+ worktree_rel = _repo_relative_path(worktree_path, repo_root)
1294
+ if getattr(args, "check", False):
1295
+ print("Prepare-child-worktree check: PASS")
1296
+ print(f"Branch: {branch}")
1297
+ print(f"Base: {base_ref}")
1298
+ print(f"Path: {worktree_rel}")
1299
+ print("No files changed.")
1300
+ return 0
1301
+
1302
+ worktree_path.parent.mkdir(parents=True, exist_ok=True)
1303
+ if _git_branch_exists(repo_root, branch):
1304
+ git_args = ["worktree", "add", str(worktree_path), branch]
1305
+ else:
1306
+ git_args = ["worktree", "add", "-b", branch, str(worktree_path), base_ref]
1307
+ rc, _, err = run_git(git_args, cwd=repo_root)
1308
+ if rc != 0:
1309
+ print(colored("Error: git worktree add failed.", Colors.RED), file=sys.stderr)
1310
+ print(err.strip() or "unknown git error", file=sys.stderr)
1311
+ return 1
1312
+
1313
+ child_data["branch"] = branch
1314
+ child_data["worktree_path"] = worktree_rel
1315
+ if not _kernel_patch_task_json(
1316
+ child_dir,
1317
+ child_data,
1318
+ actor="task.py prepare-child-worktree",
1319
+ op="prepare-child-worktree",
1320
+ evidence="branch,worktree_path",
1321
+ ):
1322
+ return 1
1323
+
1324
+ ok, map_errors = record_child_worktree(
1325
+ parent_dir,
1326
+ parent_data,
1327
+ child_dir.name,
1328
+ branch,
1329
+ worktree_rel,
1330
+ base_ref,
1331
+ )
1332
+ if not ok:
1333
+ print(colored("Error: worktree created, but Parent task-map update failed.", Colors.RED), file=sys.stderr)
1334
+ for item in map_errors:
1335
+ print(f" - {item}", file=sys.stderr)
1336
+ return 1
1337
+
1338
+ print(colored(f"✓ Child worktree prepared: {child_dir.name}", Colors.GREEN))
1339
+ print(f"Branch: {branch}")
1340
+ print(f"Path: {worktree_rel}")
1341
+ print(f"Parent map: {_repo_relative_path(parent_dir / 'task-map.md', repo_root)}")
1342
+ return 0
1343
+
1344
+
1345
+ # =============================================================================
1346
+ # Plan B: depends_mode block helpers
1347
+ # =============================================================================
1348
+
1349
+ def append_depends_ignore_event(
1350
+ task_data: dict,
1351
+ *,
1352
+ command: str,
1353
+ mode: str,
1354
+ blocking_summary: list[str],
1355
+ evidence: str | None = None,
1356
+ reason: str | None = None,
1357
+ ) -> None:
1358
+ """Append a depends-ignore audit event to meta.depends_ignore_events.
1359
+
1360
+ Mutates task_data in memory; the caller persists it in the SAME
1361
+ Kernel patch as the mutation it accompanies (execution approval / child
1362
+ state), so the event can never dangle as a separate stale write. The
1363
+ list is capped at MAX_IGNORE_EVENTS (FIFO drops the oldest entry).
1364
+ """
1365
+ from .task_dependencies import MAX_IGNORE_EVENTS
1366
+ from .task_map import utc_now
1367
+
1368
+ meta = task_data.get("meta")
1369
+ if not isinstance(meta, dict):
1370
+ meta = {}
1371
+ task_data["meta"] = meta
1372
+ events = meta.get("depends_ignore_events")
1373
+ if not isinstance(events, list):
1374
+ events = []
1375
+ event: dict = {
1376
+ "at": utc_now(),
1377
+ "command": command,
1378
+ "by": "user",
1379
+ "mode": mode,
1380
+ "blocking_summary": list(blocking_summary),
1381
+ }
1382
+ if evidence:
1383
+ event["evidence"] = evidence
1384
+ if reason:
1385
+ event["reason"] = reason
1386
+ events.append(event)
1387
+ del events[:-MAX_IGNORE_EVENTS]
1388
+ meta["depends_ignore_events"] = events
1389
+
1390
+
1391
+ def _guard_child_working_deps(
1392
+ args: argparse.Namespace,
1393
+ parent_dir: Path,
1394
+ parent_data: dict,
1395
+ child_dir: Path,
1396
+ child_data: dict,
1397
+ child_map_id: str,
1398
+ ) -> int:
1399
+ """Enforce depends_mode=block before a Child transitions to `working`.
1400
+
1401
+ Reads the CHILD's meta.depends_mode (the Parent mode never proxies for
1402
+ the Child). The dependency union covers the Child task.json depends_on
1403
+ plus the Parent task-map children[].depends_on entry; either source
1404
+ blocking rejects the transition unless --ignore-deps is passed, which
1405
+ appends an audit event to the Child task.json and a line to the Parent
1406
+ task-map Event Log. Returns 0 to allow, nonzero to reject.
1407
+ """
1408
+ from .task_dependencies import describe_child_dependencies, read_depends_mode
1409
+ from .task_map import get_child_entry, load_task_map, write_task_map
1410
+
1411
+ mode = read_depends_mode(child_data)
1412
+ if mode != "block":
1413
+ return 0
1414
+
1415
+ repo_root = get_repo_root()
1416
+ map_data, _ = load_task_map(parent_dir)
1417
+ entry = get_child_entry(map_data, child_map_id)
1418
+ report = describe_child_dependencies(
1419
+ child_dir, child_data, entry, repo_root=repo_root
1420
+ )
1421
+ blocking = report.blocking_errors()
1422
+ if not blocking:
1423
+ return 0
1424
+
1425
+ if not getattr(args, "ignore_deps", False):
1426
+ print(
1427
+ colored(
1428
+ "Error: cannot set Child state to 'working': blocking dependencies "
1429
+ "(depends_mode=block).",
1430
+ Colors.RED,
1431
+ ),
1432
+ file=sys.stderr,
1433
+ )
1434
+ for item in blocking:
1435
+ print(f" - {item}", file=sys.stderr)
1436
+ print(
1437
+ "Run with --ignore-deps to override (audited via meta.depends_ignore_events).",
1438
+ file=sys.stderr,
1439
+ )
1440
+ return 1
1441
+
1442
+ append_depends_ignore_event(
1443
+ child_data,
1444
+ command="set-child-state",
1445
+ mode=mode,
1446
+ blocking_summary=blocking,
1447
+ evidence=args.evidence,
1448
+ reason=getattr(args, "reason", None),
1449
+ )
1450
+ if not _kernel_patch_task_json(
1451
+ child_dir,
1452
+ child_data,
1453
+ actor="task.py set-child-state",
1454
+ op="depends-ignore",
1455
+ evidence=str(args.evidence or "ignore-deps"),
1456
+ ):
1457
+ print(
1458
+ colored("Error: failed to write task.json (ignore event)", Colors.RED),
1459
+ file=sys.stderr,
1460
+ )
1461
+ return 1
1462
+
1463
+ map_data_evt, map_body = load_task_map(parent_dir)
1464
+ if map_data_evt is not None:
1465
+ event = (
1466
+ f"Ignored dependencies (mode=block) for child `{child_dir.name}` "
1467
+ f"while setting state `working`: {'; '.join(blocking)}"
1468
+ )
1469
+ write_task_map(parent_dir, map_data_evt, map_body, event)
1470
+ return 0
1471
+
1472
+
1473
+ # =============================================================================
1474
+ # Command: set-child-state
1475
+ # =============================================================================
1476
+
1477
+ def cmd_set_child_state(args: argparse.Namespace) -> int:
1478
+ """Set a Child Worker-reported state in the Parent task-map.md."""
1479
+ repo_root = get_repo_root()
1480
+
1481
+ parent_dir = resolve_task_dir(args.parent_dir, repo_root)
1482
+ child_dir = resolve_task_dir(args.child_dir, repo_root)
1483
+ state = args.state
1484
+
1485
+ parent_json_path = parent_dir / FILE_TASK_JSON
1486
+ child_json_path = child_dir / FILE_TASK_JSON
1487
+
1488
+ if not parent_json_path.is_file():
1489
+ print(colored(f"Error: Parent task.json not found: {args.parent_dir}", Colors.RED), file=sys.stderr)
1490
+ return 1
1491
+ if not child_json_path.is_file():
1492
+ print(colored(f"Error: Child task.json not found: {args.child_dir}", Colors.RED), file=sys.stderr)
1493
+ return 1
1494
+
1495
+ parent_data = read_json(parent_json_path)
1496
+ child_data = read_json(child_json_path)
1497
+ if not parent_data or not child_data:
1498
+ print(colored("Error: Failed to read task.json", Colors.RED), file=sys.stderr)
1499
+ return 1
1500
+
1501
+ if child_data.get("parent") != parent_dir.name:
1502
+ print(colored(f"Error: Child is not linked to parent: {child_dir.name}", Colors.RED), file=sys.stderr)
1503
+ return 1
1504
+
1505
+ if state in CHILD_STATES and not getattr(args, "evidence", None):
1506
+ print(colored("Error: --evidence is required when setting child state", Colors.RED), file=sys.stderr)
1507
+ return 1
1508
+ if state in PARENT_CONTROLLED_STATES:
1509
+ print(
1510
+ colored(
1511
+ "Error: Parent-controlled Child states require `task.py integrate-child`.",
1512
+ Colors.RED,
1513
+ ),
1514
+ file=sys.stderr,
1515
+ )
1516
+ print(
1517
+ "Allowed set-child-state values: "
1518
+ + ", ".join(sorted(CHILD_REPORT_STATES)),
1519
+ file=sys.stderr,
1520
+ )
1521
+ return 1
1522
+
1523
+ child_map_id = resolve_child_map_id(parent_data, child_dir, child_data)
1524
+ if state == "working":
1525
+ guard_rc = _guard_child_working_deps(
1526
+ args,
1527
+ parent_dir,
1528
+ parent_data,
1529
+ child_dir,
1530
+ child_data,
1531
+ child_map_id,
1532
+ )
1533
+ if guard_rc != 0:
1534
+ return guard_rc
1535
+
1536
+ ok, errors = set_child_state(
1537
+ parent_dir,
1538
+ parent_data,
1539
+ child_map_id,
1540
+ state,
1541
+ args.evidence,
1542
+ getattr(args, "reason", None),
1543
+ )
1544
+ if not ok:
1545
+ print(colored("Error: cannot set child state.", Colors.RED), file=sys.stderr)
1546
+ for item in errors:
1547
+ print(f" - {item}", file=sys.stderr)
1548
+ return 1
1549
+
1550
+ print(colored(f"✓ Child state updated: {child_dir.name} -> {state}", Colors.GREEN))
1551
+ print(f"Parent map: {_repo_relative_path(parent_dir / 'task-map.md', repo_root)}")
1552
+ return 0
1553
+
1554
+
1555
+ # =============================================================================
1556
+ # Command: integrate-child
1557
+ # =============================================================================
1558
+
1559
+ def cmd_integrate_child(args: argparse.Namespace) -> int:
1560
+ """Set a Parent-controlled Child integration state."""
1561
+ repo_root = get_repo_root()
1562
+
1563
+ parent_dir = resolve_task_dir(args.parent_dir, repo_root)
1564
+ child_dir = resolve_task_dir(args.child_dir, repo_root)
1565
+ state = args.state
1566
+
1567
+ parent_json_path = parent_dir / FILE_TASK_JSON
1568
+ child_json_path = child_dir / FILE_TASK_JSON
1569
+
1570
+ if not parent_json_path.is_file():
1571
+ print(colored(f"Error: Parent task.json not found: {args.parent_dir}", Colors.RED), file=sys.stderr)
1572
+ return 1
1573
+ if not child_json_path.is_file():
1574
+ print(colored(f"Error: Child task.json not found: {args.child_dir}", Colors.RED), file=sys.stderr)
1575
+ return 1
1576
+
1577
+ parent_data = read_json(parent_json_path)
1578
+ child_data = read_json(child_json_path)
1579
+ if not parent_data or not child_data:
1580
+ print(colored("Error: Failed to read task.json", Colors.RED), file=sys.stderr)
1581
+ return 1
1582
+
1583
+ if state not in PARENT_CONTROLLED_STATES:
1584
+ print(
1585
+ colored("Error: state is not Parent-controlled.", Colors.RED),
1586
+ file=sys.stderr,
1587
+ )
1588
+ print(
1589
+ "Allowed integrate-child values: "
1590
+ + ", ".join(sorted(PARENT_CONTROLLED_STATES)),
1591
+ file=sys.stderr,
1592
+ )
1593
+ return 1
1594
+
1595
+ evidence = args.evidence
1596
+ ref = getattr(args, "ref", None)
1597
+ reason = getattr(args, "reason", None)
1598
+ execute_merge = getattr(args, "execute_merge", False)
1599
+ if getattr(args, "check", False):
1600
+ errors = validate_parent_child_integration(
1601
+ parent_dir,
1602
+ parent_data,
1603
+ child_dir,
1604
+ child_data,
1605
+ state,
1606
+ evidence,
1607
+ ref,
1608
+ reason,
1609
+ )
1610
+ if execute_merge:
1611
+ errors.extend(_validate_merge_execution(repo_root, state, ref))
1612
+ if errors:
1613
+ print("Integrate-child check: FAIL")
1614
+ for item in errors:
1615
+ print(f" - {item}")
1616
+ print_environment_repair_hints(errors, stream=sys.stdout)
1617
+ return 1
1618
+ print("Integrate-child check: PASS")
1619
+ if execute_merge:
1620
+ print("Merge execution check: PASS")
1621
+ print("No files changed.")
1622
+ return 0
1623
+
1624
+ merge_ref = None
1625
+ if execute_merge:
1626
+ merge_errors = _validate_merge_execution(repo_root, state, ref)
1627
+ if merge_errors:
1628
+ print(colored("Error: cannot execute child merge.", Colors.RED), file=sys.stderr)
1629
+ for item in merge_errors:
1630
+ print(f" - {item}", file=sys.stderr)
1631
+ print_environment_repair_hints(merge_errors)
1632
+ return 1
1633
+
1634
+ rc, _, err = run_git(["merge", "--no-ff", "--no-commit", ref], cwd=repo_root)
1635
+ if rc != 0:
1636
+ print(colored("Error: git merge failed; Parent task-map was not advanced to integrated.", Colors.RED), file=sys.stderr)
1637
+ if err.strip():
1638
+ print(err.strip(), file=sys.stderr)
1639
+ print_environment_repair_hints([err.strip()])
1640
+ print("Resolve the merge manually, abort it with `git merge --abort`, or record a `changes` / `cancelled` Parent decision.", file=sys.stderr)
1641
+ return 1
1642
+ merge_ref = ref
1643
+
1644
+ ok, errors = set_parent_child_integration_state(
1645
+ parent_dir,
1646
+ parent_data,
1647
+ child_dir,
1648
+ child_data,
1649
+ state,
1650
+ evidence,
1651
+ ref,
1652
+ reason,
1653
+ merge_ref,
1654
+ )
1655
+ if not ok:
1656
+ print(colored("Error: cannot integrate child.", Colors.RED), file=sys.stderr)
1657
+ for item in errors:
1658
+ print(f" - {item}", file=sys.stderr)
1659
+ return 1
1660
+
1661
+ print(colored(f"✓ Child integration updated: {child_dir.name} -> {state}", Colors.GREEN))
1662
+ if merge_ref:
1663
+ print(f"Merge executed: git merge --no-ff --no-commit {merge_ref}")
1664
+ print(f"Parent map: {_repo_relative_path(parent_dir / 'task-map.md', repo_root)}")
1665
+
1666
+ if state == "integrated" and not getattr(args, "no_publish_pack", False):
1667
+ from .parent_orchestration import build_publish_pack
1668
+
1669
+ summary, pack_errors = build_publish_pack(parent_dir)
1670
+ if pack_errors or summary is None:
1671
+ for item in pack_errors:
1672
+ print(f" - {item}", file=sys.stderr)
1673
+ print(
1674
+ colored(
1675
+ "Warning: integrate succeeded but publish-pack failed; "
1676
+ "PACK may be stale (see parent-status stalePack).",
1677
+ Colors.YELLOW,
1678
+ ),
1679
+ file=sys.stderr,
1680
+ )
1681
+ return 2
1682
+ print(colored("✓ PACK refreshed after integrate", Colors.GREEN))
1683
+ for line in summary.splitlines():
1684
+ if line.startswith("- Generated:"):
1685
+ print(line)
1686
+ break
1687
+
1688
+ return 0
1689
+
1690
+
1691
+ # =============================================================================
1692
+ # Command: generate-child-prompt / parent-status / review-child
1693
+ # =============================================================================
1694
+
1695
+ def cmd_suggest_execution_strategy(args: argparse.Namespace) -> int:
1696
+ """Suggest execution_mode and isolation for implement.md contract."""
1697
+ import json
1698
+
1699
+ from .execution_strategy import (
1700
+ format_contract_yaml_block,
1701
+ suggest_execution_strategy,
1702
+ )
1703
+ from .task_utils import resolve_task_dir
1704
+
1705
+ repo_root = get_repo_root()
1706
+ task_dir = resolve_task_dir(args.task_dir, repo_root)
1707
+ task_json_path = task_dir / FILE_TASK_JSON
1708
+ if not task_json_path.is_file():
1709
+ print(colored("Error: task.json not found", Colors.RED), file=sys.stderr)
1710
+ return 1
1711
+ task_data = read_json(task_json_path)
1712
+ if not isinstance(task_data, dict):
1713
+ print(colored("Error: invalid task.json", Colors.RED), file=sys.stderr)
1714
+ return 1
1715
+
1716
+ suggestion = suggest_execution_strategy(repo_root, task_dir, task_data)
1717
+ if getattr(args, "json", False):
1718
+ print(json.dumps(suggestion.to_dict(), indent=2, ensure_ascii=False))
1719
+ return 0
1720
+ print(format_contract_yaml_block(suggestion))
1721
+ return 0
1722
+
1723
+
1724
+ def cmd_generate_dispatch_prompt(args: argparse.Namespace) -> int:
1725
+ """Build a full Task dispatch prompt (Agent-facing CLI Layer 2)."""
1726
+ from .subagent_dispatch import build_dispatch_prompt
1727
+
1728
+ repo_root = get_repo_root()
1729
+ task_dir = resolve_task_dir(args.task_dir, repo_root)
1730
+ role = args.role
1731
+ scope = getattr(args, "scope", None)
1732
+ finish = bool(getattr(args, "finish", False))
1733
+ max_chars = getattr(args, "max_chars", None)
1734
+
1735
+ prompt, warnings, errors = build_dispatch_prompt(
1736
+ repo_root,
1737
+ task_dir,
1738
+ role,
1739
+ scope=scope,
1740
+ finish=finish,
1741
+ max_chars=max_chars,
1742
+ )
1743
+ for item in warnings:
1744
+ print(f"[generate-dispatch-prompt] WARN: {item}", file=sys.stderr)
1745
+ if errors:
1746
+ for item in errors:
1747
+ print(f"[generate-dispatch-prompt] Error: {item}", file=sys.stderr)
1748
+ return 1
1749
+ if prompt is None:
1750
+ print(colored("Error: could not build dispatch prompt", Colors.RED), file=sys.stderr)
1751
+ return 1
1752
+ print(prompt)
1753
+ return 0
1754
+
1755
+
1756
+ def cmd_generate_child_prompt(args: argparse.Namespace) -> int:
1757
+ """Generate a child implementation prompt for parent orchestration."""
1758
+ from .parent_orchestration import build_child_prompt
1759
+
1760
+ repo_root = get_repo_root()
1761
+ parent_dir = resolve_task_dir(args.parent_dir, repo_root)
1762
+ child_dir = resolve_task_dir(args.child_dir, repo_root)
1763
+ mode = getattr(args, "mode", "inline") or "inline"
1764
+ if mode not in ("inline", "subagent"):
1765
+ print(colored("Error: --mode must be inline or subagent", Colors.RED), file=sys.stderr)
1766
+ return 1
1767
+
1768
+ prompt, errors = build_child_prompt(
1769
+ parent_dir,
1770
+ child_dir,
1771
+ include_artifacts=getattr(args, "include_artifacts", False),
1772
+ mode=mode,
1773
+ )
1774
+ if errors:
1775
+ for item in errors:
1776
+ print(f" - {item}", file=sys.stderr)
1777
+ return 1
1778
+ if prompt is None:
1779
+ print(colored("Error: could not generate child prompt", Colors.RED), file=sys.stderr)
1780
+ return 1
1781
+
1782
+ out_path = getattr(args, "output", None)
1783
+ if out_path:
1784
+ path = Path(out_path)
1785
+ if not path.is_absolute():
1786
+ path = repo_root / path
1787
+ path.parent.mkdir(parents=True, exist_ok=True)
1788
+ path.write_text(prompt + "\n", encoding="utf-8")
1789
+ print(colored(f"✓ Child prompt written: {_repo_relative_path(path, repo_root)}", Colors.GREEN))
1790
+ else:
1791
+ print(prompt)
1792
+ return 0
1793
+
1794
+
1795
+ def cmd_parent_status(args: argparse.Namespace) -> int:
1796
+ """Show parent task-map orchestration status."""
1797
+ import json
1798
+
1799
+ from .parent_orchestration import build_parent_status, build_parent_status_dict
1800
+
1801
+ repo_root = get_repo_root()
1802
+ parent_dir = resolve_task_dir(args.parent_dir, repo_root)
1803
+ if getattr(args, "json", False):
1804
+ payload = build_parent_status_dict(parent_dir)
1805
+ if payload is None:
1806
+ print(
1807
+ json.dumps(
1808
+ {
1809
+ "error": "task-map.md missing or invalid",
1810
+ "path": str(parent_dir),
1811
+ }
1812
+ )
1813
+ )
1814
+ return 1
1815
+ print(json.dumps(payload, ensure_ascii=False, indent=2))
1816
+ return 0
1817
+ print(build_parent_status(parent_dir))
1818
+ return 0
1819
+
1820
+
1821
+
1822
+ def cmd_publish_pack(args: argparse.Namespace) -> int:
1823
+ """Publish HYBRID campaign PACK (ready list + child prompts)."""
1824
+ from .parent_orchestration import build_publish_pack
1825
+
1826
+ repo_root = get_repo_root()
1827
+ parent_dir = resolve_task_dir(args.parent_dir, repo_root)
1828
+ mode = getattr(args, "mode", "inline") or "inline"
1829
+ if mode not in ("inline", "subagent"):
1830
+ print(colored("Error: --mode must be inline or subagent", Colors.RED), file=sys.stderr)
1831
+ return 1
1832
+
1833
+ summary, errors = build_publish_pack(
1834
+ parent_dir,
1835
+ stage_id=getattr(args, "stage", None),
1836
+ mode=mode,
1837
+ dry_run=bool(getattr(args, "dry_run", False)),
1838
+ )
1839
+ if errors:
1840
+ for item in errors:
1841
+ print(f" - {item}", file=sys.stderr)
1842
+ print(colored("Error: publish-pack failed", Colors.RED), file=sys.stderr)
1843
+ return 1
1844
+ if summary is None:
1845
+ print(colored("Error: publish-pack produced no summary", Colors.RED), file=sys.stderr)
1846
+ return 1
1847
+ print(summary)
1848
+ return 0
1849
+
1850
+
1851
+ def cmd_review_child(args: argparse.Namespace) -> int:
1852
+ """Review child handoff and optionally advance parent integration states."""
1853
+ from .parent_orchestration import (
1854
+ append_parent_review_notes,
1855
+ build_review_report,
1856
+ write_review_artifact,
1857
+ )
1858
+ from .task_map import set_parent_child_integration_state
1859
+
1860
+ repo_root = get_repo_root()
1861
+ parent_dir = resolve_task_dir(args.parent_dir, repo_root)
1862
+ child_dir = resolve_task_dir(args.child_dir, repo_root)
1863
+
1864
+ parent_json_path = parent_dir / FILE_TASK_JSON
1865
+ child_json_path = child_dir / FILE_TASK_JSON
1866
+ if not parent_json_path.is_file() or not child_json_path.is_file():
1867
+ print(colored("Error: parent or child task.json missing", Colors.RED), file=sys.stderr)
1868
+ return 1
1869
+
1870
+ parent_data = read_json(parent_json_path)
1871
+ child_data = read_json(child_json_path)
1872
+ if not parent_data or not child_data:
1873
+ print(colored("Error: Failed to read task.json", Colors.RED), file=sys.stderr)
1874
+ return 1
1875
+
1876
+ decision = getattr(args, "decision", None)
1877
+ ref = getattr(args, "ref", None)
1878
+ reason = getattr(args, "reason", None)
1879
+ notes = getattr(args, "notes", None)
1880
+ check_only = getattr(args, "check", False) or not decision
1881
+
1882
+ report, errors, actions = build_review_report(
1883
+ parent_dir,
1884
+ child_dir,
1885
+ parent_data,
1886
+ child_data,
1887
+ decision=decision if not check_only else None,
1888
+ ref=ref,
1889
+ reason=reason,
1890
+ notes=notes,
1891
+ )
1892
+
1893
+ if getattr(args, "write_artifact", False):
1894
+ artifact = write_review_artifact(parent_dir, child_dir.name, report)
1895
+ print(colored(f"✓ Review artifact: {_repo_relative_path(artifact, repo_root)}", Colors.GREEN))
1896
+
1897
+ if check_only:
1898
+ if errors:
1899
+ print("Review-child check: FAIL")
1900
+ for item in errors:
1901
+ print(f" - {item}")
1902
+ print("")
1903
+ print(report)
1904
+ return 1
1905
+ print("Review-child check: PASS")
1906
+ print("")
1907
+ print(report)
1908
+ return 0
1909
+
1910
+ if errors:
1911
+ print(colored("Error: review decision blocked by validation.", Colors.RED), file=sys.stderr)
1912
+ for item in errors:
1913
+ print(f" - {item}", file=sys.stderr)
1914
+ return 1
1915
+
1916
+ evidence_default = "handoff.md"
1917
+ if actions.get("integrate_sequence"):
1918
+ for step in actions["integrate_sequence"]:
1919
+ ok, step_errors = set_parent_child_integration_state(
1920
+ parent_dir,
1921
+ parent_data,
1922
+ child_dir,
1923
+ child_data,
1924
+ step["state"],
1925
+ step.get("evidence", evidence_default),
1926
+ step.get("ref", ref),
1927
+ step.get("reason", reason),
1928
+ )
1929
+ if not ok:
1930
+ print(colored("Error: integrate-child step failed.", Colors.RED), file=sys.stderr)
1931
+ for item in step_errors:
1932
+ print(f" - {item}", file=sys.stderr)
1933
+ return 1
1934
+ print(colored(f"✓ Child integration updated: {child_dir.name} -> {step['state']}", Colors.GREEN))
1935
+ elif actions.get("integrate"):
1936
+ step = actions["integrate"]
1937
+ ok, step_errors = set_parent_child_integration_state(
1938
+ parent_dir,
1939
+ parent_data,
1940
+ child_dir,
1941
+ child_data,
1942
+ step["state"],
1943
+ step.get("evidence", evidence_default),
1944
+ step.get("ref", ref),
1945
+ step.get("reason", reason),
1946
+ )
1947
+ if not ok:
1948
+ print(colored("Error: integrate-child failed.", Colors.RED), file=sys.stderr)
1949
+ for item in step_errors:
1950
+ print(f" - {item}", file=sys.stderr)
1951
+ return 1
1952
+ print(colored(f"✓ Child integration updated: {child_dir.name} -> {step['state']}", Colors.GREEN))
1953
+
1954
+ if not getattr(args, "no_append_parent_verify", False):
1955
+ append_parent_review_notes(parent_dir, child_dir.name, report)
1956
+ print(colored("✓ Appended review notes to parent verify.md", Colors.GREEN))
1957
+
1958
+ for gate in actions.get("gates", []):
1959
+ if gate.get("optional") and gate.get("hint"):
1960
+ print(f"Optional reviewer gate: {gate['hint']}")
1961
+
1962
+ print(report)
1963
+ return 0
1964
+
1965
+
1966
+ # =============================================================================
1967
+ # Command: set-branch
1968
+ # =============================================================================
1969
+
1970
+ def cmd_set_branch(args: argparse.Namespace) -> int:
1971
+ """Set git branch for task."""
1972
+ repo_root = get_repo_root()
1973
+ target_dir = resolve_task_dir(args.dir, repo_root)
1974
+ branch = args.branch
1975
+
1976
+ if not branch:
1977
+ print(colored("Error: Missing arguments", Colors.RED))
1978
+ print("Usage: python task.py set-branch <task-dir> <branch-name>")
1979
+ return 1
1980
+
1981
+ task_json = target_dir / FILE_TASK_JSON
1982
+ if not task_json.is_file():
1983
+ print(colored(f"Error: task.json not found at {target_dir}", Colors.RED))
1984
+ return 1
1985
+
1986
+ data = read_json(task_json)
1987
+ if not data:
1988
+ return 1
1989
+
1990
+ data["branch"] = branch
1991
+ if not _kernel_patch_task_json(
1992
+ target_dir,
1993
+ data,
1994
+ actor="task.py set-branch",
1995
+ op="set-branch",
1996
+ evidence="branch",
1997
+ ):
1998
+ return 1
1999
+
2000
+ print(colored(f"✓ Branch set to: {branch}", Colors.GREEN))
2001
+ return 0
2002
+
2003
+
2004
+ # =============================================================================
2005
+ # Command: set-base-branch
2006
+ # =============================================================================
2007
+
2008
+ def cmd_set_base_branch(args: argparse.Namespace) -> int:
2009
+ """Set the base branch (PR target) for task."""
2010
+ repo_root = get_repo_root()
2011
+ target_dir = resolve_task_dir(args.dir, repo_root)
2012
+ base_branch = args.base_branch
2013
+
2014
+ if not base_branch:
2015
+ print(colored("Error: Missing arguments", Colors.RED))
2016
+ print("Usage: python task.py set-base-branch <task-dir> <base-branch>")
2017
+ print("Example: python task.py set-base-branch <dir> develop")
2018
+ print()
2019
+ print("This sets the target branch for PR (the branch your feature will merge into).")
2020
+ return 1
2021
+
2022
+ task_json = target_dir / FILE_TASK_JSON
2023
+ if not task_json.is_file():
2024
+ print(colored(f"Error: task.json not found at {target_dir}", Colors.RED))
2025
+ return 1
2026
+
2027
+ data = read_json(task_json)
2028
+ if not data:
2029
+ return 1
2030
+
2031
+ data["base_branch"] = base_branch
2032
+ if not _kernel_patch_task_json(
2033
+ target_dir,
2034
+ data,
2035
+ actor="task.py set-base-branch",
2036
+ op="set-base-branch",
2037
+ evidence="base_branch",
2038
+ ):
2039
+ return 1
2040
+
2041
+ print(colored(f"✓ Base branch set to: {base_branch}", Colors.GREEN))
2042
+ print(f" PR will target: {base_branch}")
2043
+ return 0
2044
+
2045
+
2046
+ # =============================================================================
2047
+ # Command: set-scope
2048
+ # =============================================================================
2049
+
2050
+ def cmd_set_scope(args: argparse.Namespace) -> int:
2051
+ """Set scope for PR title."""
2052
+ repo_root = get_repo_root()
2053
+ target_dir = resolve_task_dir(args.dir, repo_root)
2054
+ scope = args.scope
2055
+
2056
+ if not scope:
2057
+ print(colored("Error: Missing arguments", Colors.RED))
2058
+ print("Usage: python task.py set-scope <task-dir> <scope>")
2059
+ return 1
2060
+
2061
+ task_json = target_dir / FILE_TASK_JSON
2062
+ if not task_json.is_file():
2063
+ print(colored(f"Error: task.json not found at {target_dir}", Colors.RED))
2064
+ return 1
2065
+
2066
+ data = read_json(task_json)
2067
+ if not data:
2068
+ return 1
2069
+
2070
+ data["scope"] = scope
2071
+ if not _kernel_patch_task_json(
2072
+ target_dir,
2073
+ data,
2074
+ actor="task.py set-scope",
2075
+ op="set-scope",
2076
+ evidence="scope",
2077
+ ):
2078
+ return 1
2079
+
2080
+ print(colored(f"✓ Scope set to: {scope}", Colors.GREEN))
2081
+ return 0
2082
+
2083
+
2084
+ # =============================================================================
2085
+ # Command: set-deps
2086
+ # =============================================================================
2087
+
2088
+ def cmd_set_deps(args: argparse.Namespace) -> int:
2089
+ """Set task-level depends_on (Plan A: declare + soft checks, no blocking).
2090
+
2091
+ Bare task ids and `pool:Pxx` refs are supported. Dangling references are
2092
+ warned but still written (Plan A is discovery-first; `warn` never errors).
2093
+ """
2094
+ from .task_dependencies import KIND_MISSING, normalize_dep_list, resolve_dep_ref
2095
+
2096
+ repo_root = get_repo_root()
2097
+ target_dir = resolve_task_dir(args.dir, repo_root)
2098
+
2099
+ task_json = target_dir / FILE_TASK_JSON
2100
+ if not task_json.is_file():
2101
+ print(
2102
+ colored(f"Error: task.json not found at {target_dir}", Colors.RED),
2103
+ file=sys.stderr,
2104
+ )
2105
+ return 1
2106
+
2107
+ data = read_json(task_json)
2108
+ if not data:
2109
+ return 1
2110
+
2111
+ normalized = normalize_dep_list(args.dep)
2112
+ data["depends_on"] = normalized
2113
+
2114
+ dangling = [
2115
+ ref
2116
+ for ref in normalized
2117
+ if resolve_dep_ref(ref, repo_root=repo_root).kind == KIND_MISSING
2118
+ ]
2119
+
2120
+ if not _kernel_patch_task_json(
2121
+ target_dir,
2122
+ data,
2123
+ actor="task.py set-deps",
2124
+ op="set-deps",
2125
+ evidence="depends_on",
2126
+ ):
2127
+ print(colored("Error: failed to write task.json", Colors.RED), file=sys.stderr)
2128
+ return 1
2129
+
2130
+ if normalized:
2131
+ print(
2132
+ colored(
2133
+ f"✓ depends_on set: {', '.join(normalized)}",
2134
+ Colors.GREEN,
2135
+ )
2136
+ )
2137
+ else:
2138
+ print(colored("✓ depends_on cleared to []", Colors.GREEN))
2139
+ for ref in dangling:
2140
+ print(
2141
+ colored(
2142
+ f"[dependencies] WARN: dangling dependency: {ref} "
2143
+ "(no matching task or child; declaration kept)",
2144
+ Colors.YELLOW,
2145
+ ),
2146
+ file=sys.stderr,
2147
+ )
2148
+ return 0
2149
+
2150
+
2151
+ # =============================================================================
2152
+ # Command: set-depends-mode
2153
+ # =============================================================================
2154
+
2155
+ def cmd_set_depends_mode(args: argparse.Namespace) -> int:
2156
+ """Set meta.depends_mode (warn | block | off) for a task.
2157
+
2158
+ warn is the default (= Plan A soft checks); block opts the task into
2159
+ hard dependency gates at start-execution --approved and
2160
+ set-child-state working; off silences dependency output entirely.
2161
+ """
2162
+ from .task_dependencies import DEPENDS_MODES, read_depends_mode
2163
+
2164
+ repo_root = get_repo_root()
2165
+ target_dir = resolve_task_dir(args.dir, repo_root)
2166
+
2167
+ task_json = target_dir / FILE_TASK_JSON
2168
+ if not task_json.is_file():
2169
+ print(
2170
+ colored(f"Error: task.json not found at {target_dir}", Colors.RED),
2171
+ file=sys.stderr,
2172
+ )
2173
+ return 1
2174
+
2175
+ data = read_json(task_json)
2176
+ if not data:
2177
+ return 1
2178
+
2179
+ previous = read_depends_mode(data)
2180
+ if args.mode not in DEPENDS_MODES:
2181
+ print(
2182
+ colored(
2183
+ f"Error: depends_mode must be one of {', '.join(DEPENDS_MODES)}",
2184
+ Colors.RED,
2185
+ ),
2186
+ file=sys.stderr,
2187
+ )
2188
+ return 1
2189
+
2190
+ meta = data.get("meta")
2191
+ if not isinstance(meta, dict):
2192
+ meta = {}
2193
+ data["meta"] = meta
2194
+ meta["depends_mode"] = args.mode
2195
+
2196
+ if not _kernel_patch_task_json(
2197
+ target_dir,
2198
+ data,
2199
+ actor="task.py set-depends-mode",
2200
+ op="set-depends-mode",
2201
+ evidence="meta.depends_mode",
2202
+ ):
2203
+ print(colored("Error: failed to write task.json", Colors.RED), file=sys.stderr)
2204
+ return 1
2205
+
2206
+ print(
2207
+ colored(
2208
+ f"✓ depends_mode: {previous} -> {args.mode}",
2209
+ Colors.GREEN,
2210
+ )
2211
+ )
2212
+ return 0
2213
+
2214
+
2215
+ # =============================================================================
2216
+ # Command: artifact-locale
2217
+ # =============================================================================
2218
+
2219
+ def cmd_artifact_locale(args: argparse.Namespace) -> int:
2220
+ """Get or set human-reviewed artifact locale (zh | en)."""
2221
+ from .artifact_locale import (
2222
+ resolve_artifact_locale,
2223
+ set_task_artifact_locale,
2224
+ set_workspace_artifact_locale,
2225
+ )
2226
+
2227
+ repo_root = get_repo_root()
2228
+ subcommand = getattr(args, "artifact_locale_command", None)
2229
+
2230
+ if subcommand == "get":
2231
+ task_dir = resolve_task_dir(args.task, repo_root) if args.task else None
2232
+ print(resolve_artifact_locale(task_dir, repo_root))
2233
+ return 0
2234
+
2235
+ if subcommand == "set":
2236
+ try:
2237
+ if args.task:
2238
+ task_dir = resolve_task_dir(args.task, repo_root)
2239
+ set_task_artifact_locale(task_dir, args.locale, repo_root)
2240
+ scope = f"task {task_dir.name}"
2241
+ else:
2242
+ set_workspace_artifact_locale(args.locale, repo_root)
2243
+ scope = "workspace"
2244
+ except (ValueError, FileNotFoundError) as exc:
2245
+ print(colored(f"Error: {exc}", Colors.RED), file=sys.stderr)
2246
+ return 1
2247
+
2248
+ print(colored(f"✓ artifact_locale set to {args.locale} ({scope})", Colors.GREEN))
2249
+ return 0
2250
+
2251
+ print(colored("Error: use artifact-locale get|set", Colors.RED), file=sys.stderr)
2252
+ return 1