@pennixrv/trellis 0.7.0-beta.9 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (483) hide show
  1. package/README.md +1 -1
  2. package/dist/cli/index.d.ts.map +1 -1
  3. package/dist/cli/index.js +14 -26
  4. package/dist/cli/index.js.map +1 -1
  5. package/dist/commands/ablate.js +1 -1
  6. package/dist/commands/ablate.js.map +1 -1
  7. package/dist/commands/init.d.ts.map +1 -1
  8. package/dist/commands/init.js +30 -56
  9. package/dist/commands/init.js.map +1 -1
  10. package/dist/commands/task-progress.d.ts +2 -0
  11. package/dist/commands/task-progress.d.ts.map +1 -1
  12. package/dist/commands/task-progress.js +37 -30
  13. package/dist/commands/task-progress.js.map +1 -1
  14. package/dist/commands/{channel → teamwork}/adapters/claude.d.ts +3 -3
  15. package/dist/commands/teamwork/adapters/claude.d.ts.map +1 -0
  16. package/dist/commands/{channel → teamwork}/adapters/claude.js +3 -3
  17. package/dist/commands/teamwork/adapters/claude.js.map +1 -0
  18. package/dist/commands/{channel → teamwork}/adapters/codex.d.ts +1 -15
  19. package/dist/commands/teamwork/adapters/codex.d.ts.map +1 -0
  20. package/dist/commands/{channel → teamwork}/adapters/codex.js +5 -42
  21. package/dist/commands/teamwork/adapters/codex.js.map +1 -0
  22. package/dist/commands/{channel → teamwork}/adapters/index.d.ts +6 -6
  23. package/dist/commands/teamwork/adapters/index.d.ts.map +1 -0
  24. package/dist/commands/{channel → teamwork}/adapters/index.js +16 -52
  25. package/dist/commands/teamwork/adapters/index.js.map +1 -0
  26. package/dist/commands/{channel → teamwork}/adapters/types.d.ts +2 -2
  27. package/dist/commands/teamwork/adapters/types.d.ts.map +1 -0
  28. package/dist/commands/teamwork/adapters/types.js.map +1 -0
  29. package/dist/commands/{channel → teamwork}/agent-loader.d.ts +2 -4
  30. package/dist/commands/teamwork/agent-loader.d.ts.map +1 -0
  31. package/dist/commands/{channel → teamwork}/agent-loader.js +4 -35
  32. package/dist/commands/teamwork/agent-loader.js.map +1 -0
  33. package/dist/commands/teamwork/artifact.d.ts +177 -0
  34. package/dist/commands/teamwork/artifact.d.ts.map +1 -0
  35. package/dist/commands/teamwork/artifact.js +908 -0
  36. package/dist/commands/teamwork/artifact.js.map +1 -0
  37. package/dist/commands/{channel → teamwork}/context-loader.d.ts +1 -1
  38. package/dist/commands/teamwork/context-loader.d.ts.map +1 -0
  39. package/dist/commands/{channel → teamwork}/context-loader.js +12 -12
  40. package/dist/commands/teamwork/context-loader.js.map +1 -0
  41. package/dist/commands/{channel → teamwork}/context-trust.d.ts +6 -6
  42. package/dist/commands/teamwork/context-trust.d.ts.map +1 -0
  43. package/dist/commands/{channel → teamwork}/context-trust.js +16 -16
  44. package/dist/commands/teamwork/context-trust.js.map +1 -0
  45. package/dist/commands/teamwork/context.d.ts +16 -0
  46. package/dist/commands/teamwork/context.d.ts.map +1 -0
  47. package/dist/commands/{channel → teamwork}/context.js +17 -17
  48. package/dist/commands/teamwork/context.js.map +1 -0
  49. package/dist/commands/teamwork/coordinator.d.ts +89 -0
  50. package/dist/commands/teamwork/coordinator.d.ts.map +1 -0
  51. package/dist/commands/teamwork/coordinator.js +1198 -0
  52. package/dist/commands/teamwork/coordinator.js.map +1 -0
  53. package/dist/commands/teamwork/create.d.ts +23 -0
  54. package/dist/commands/teamwork/create.d.ts.map +1 -0
  55. package/dist/commands/teamwork/create.js +39 -0
  56. package/dist/commands/teamwork/create.js.map +1 -0
  57. package/dist/commands/{channel → teamwork}/dev-parse-trace.d.ts +1 -1
  58. package/dist/commands/teamwork/dev-parse-trace.d.ts.map +1 -0
  59. package/dist/commands/{channel → teamwork}/dev-parse-trace.js +1 -1
  60. package/dist/commands/teamwork/dev-parse-trace.js.map +1 -0
  61. package/dist/commands/{channel → teamwork}/guard.d.ts +27 -46
  62. package/dist/commands/teamwork/guard.d.ts.map +1 -0
  63. package/dist/commands/{channel → teamwork}/guard.js +54 -156
  64. package/dist/commands/teamwork/guard.js.map +1 -0
  65. package/dist/commands/teamwork/index.d.ts +3 -0
  66. package/dist/commands/teamwork/index.d.ts.map +1 -0
  67. package/dist/commands/{channel → teamwork}/index.js +135 -165
  68. package/dist/commands/teamwork/index.js.map +1 -0
  69. package/dist/commands/{channel → teamwork}/interrupt.d.ts +1 -1
  70. package/dist/commands/teamwork/interrupt.d.ts.map +1 -0
  71. package/dist/commands/teamwork/interrupt.js +22 -0
  72. package/dist/commands/teamwork/interrupt.js.map +1 -0
  73. package/dist/commands/teamwork/kill.d.ts +7 -0
  74. package/dist/commands/teamwork/kill.d.ts.map +1 -0
  75. package/dist/commands/{channel → teamwork}/kill.js +21 -21
  76. package/dist/commands/teamwork/kill.js.map +1 -0
  77. package/dist/commands/teamwork/list.d.ts +17 -0
  78. package/dist/commands/teamwork/list.d.ts.map +1 -0
  79. package/dist/commands/{channel → teamwork}/list.js +18 -27
  80. package/dist/commands/teamwork/list.js.map +1 -0
  81. package/dist/commands/{channel → teamwork}/messages.d.ts +1 -1
  82. package/dist/commands/teamwork/messages.d.ts.map +1 -0
  83. package/dist/commands/{channel → teamwork}/messages.js +18 -18
  84. package/dist/commands/teamwork/messages.js.map +1 -0
  85. package/dist/commands/teamwork/profiles.d.ts +25 -0
  86. package/dist/commands/teamwork/profiles.d.ts.map +1 -0
  87. package/dist/commands/teamwork/profiles.js +125 -0
  88. package/dist/commands/teamwork/profiles.js.map +1 -0
  89. package/dist/commands/teamwork/rm.d.ts +27 -0
  90. package/dist/commands/teamwork/rm.d.ts.map +1 -0
  91. package/dist/commands/{channel → teamwork}/rm.js +21 -22
  92. package/dist/commands/teamwork/rm.js.map +1 -0
  93. package/dist/commands/teamwork/run.d.ts +29 -0
  94. package/dist/commands/{channel → teamwork}/run.d.ts.map +1 -1
  95. package/dist/commands/teamwork/run.js +129 -0
  96. package/dist/commands/teamwork/run.js.map +1 -0
  97. package/dist/commands/{channel → teamwork}/send.d.ts +1 -1
  98. package/dist/commands/teamwork/send.d.ts.map +1 -0
  99. package/dist/commands/{channel → teamwork}/send.js +7 -7
  100. package/dist/commands/teamwork/send.js.map +1 -0
  101. package/dist/commands/{channel → teamwork}/spawn.d.ts +8 -12
  102. package/dist/commands/teamwork/spawn.d.ts.map +1 -0
  103. package/dist/commands/{channel → teamwork}/spawn.js +51 -112
  104. package/dist/commands/teamwork/spawn.js.map +1 -0
  105. package/dist/commands/teamwork/store/events.d.ts +22 -0
  106. package/dist/commands/teamwork/store/events.d.ts.map +1 -0
  107. package/dist/commands/{channel → teamwork}/store/events.js +8 -8
  108. package/dist/commands/teamwork/store/events.js.map +1 -0
  109. package/dist/commands/teamwork/store/filter.d.ts +3 -0
  110. package/dist/commands/teamwork/store/filter.d.ts.map +1 -0
  111. package/dist/commands/teamwork/store/filter.js +2 -0
  112. package/dist/commands/teamwork/store/filter.js.map +1 -0
  113. package/dist/commands/{channel → teamwork}/store/lock.d.ts +1 -1
  114. package/dist/commands/teamwork/store/lock.d.ts.map +1 -0
  115. package/dist/commands/{channel → teamwork}/store/lock.js +2 -2
  116. package/dist/commands/teamwork/store/lock.js.map +1 -0
  117. package/dist/commands/{channel → teamwork}/store/paths.d.ts +18 -32
  118. package/dist/commands/teamwork/store/paths.d.ts.map +1 -0
  119. package/dist/commands/{channel → teamwork}/store/paths.js +42 -109
  120. package/dist/commands/teamwork/store/paths.js.map +1 -0
  121. package/dist/commands/teamwork/store/schema.d.ts +18 -0
  122. package/dist/commands/teamwork/store/schema.d.ts.map +1 -0
  123. package/dist/commands/teamwork/store/schema.js +23 -0
  124. package/dist/commands/teamwork/store/schema.js.map +1 -0
  125. package/dist/commands/{channel → teamwork}/store/thread-state.d.ts +3 -3
  126. package/dist/commands/teamwork/store/thread-state.d.ts.map +1 -0
  127. package/dist/commands/{channel → teamwork}/store/thread-state.js +1 -1
  128. package/dist/commands/teamwork/store/thread-state.js.map +1 -0
  129. package/dist/commands/teamwork/store/watch.d.ts +19 -0
  130. package/dist/commands/teamwork/store/watch.d.ts.map +1 -0
  131. package/dist/commands/{channel → teamwork}/store/watch.js +8 -8
  132. package/dist/commands/teamwork/store/watch.js.map +1 -0
  133. package/dist/commands/teamwork/supervisor/idle.d.ts.map +1 -0
  134. package/dist/commands/teamwork/supervisor/idle.js.map +1 -0
  135. package/dist/commands/{channel → teamwork}/supervisor/inbox.d.ts +2 -2
  136. package/dist/commands/teamwork/supervisor/inbox.d.ts.map +1 -0
  137. package/dist/commands/{channel → teamwork}/supervisor/inbox.js +14 -14
  138. package/dist/commands/teamwork/supervisor/inbox.js.map +1 -0
  139. package/dist/commands/{channel → teamwork}/supervisor/shutdown.d.ts +3 -6
  140. package/dist/commands/teamwork/supervisor/shutdown.d.ts.map +1 -0
  141. package/dist/commands/{channel → teamwork}/supervisor/shutdown.js +7 -17
  142. package/dist/commands/teamwork/supervisor/shutdown.js.map +1 -0
  143. package/dist/commands/{channel → teamwork}/supervisor/stdout.d.ts +3 -5
  144. package/dist/commands/teamwork/supervisor/stdout.d.ts.map +1 -0
  145. package/dist/commands/{channel → teamwork}/supervisor/stdout.js +12 -23
  146. package/dist/commands/teamwork/supervisor/stdout.js.map +1 -0
  147. package/dist/commands/teamwork/supervisor/turns.d.ts.map +1 -0
  148. package/dist/commands/teamwork/supervisor/turns.js.map +1 -0
  149. package/dist/commands/{channel → teamwork}/supervisor/warning.d.ts +5 -5
  150. package/dist/commands/teamwork/supervisor/warning.d.ts.map +1 -0
  151. package/dist/commands/{channel → teamwork}/supervisor/warning.js +6 -6
  152. package/dist/commands/teamwork/supervisor/warning.js.map +1 -0
  153. package/dist/commands/{channel → teamwork}/supervisor.d.ts +21 -21
  154. package/dist/commands/teamwork/supervisor.d.ts.map +1 -0
  155. package/dist/commands/{channel → teamwork}/supervisor.js +53 -63
  156. package/dist/commands/teamwork/supervisor.js.map +1 -0
  157. package/dist/commands/teamwork/teammate-adapters.d.ts +31 -0
  158. package/dist/commands/teamwork/teammate-adapters.d.ts.map +1 -0
  159. package/dist/commands/teamwork/teammate-adapters.js +96 -0
  160. package/dist/commands/teamwork/teammate-adapters.js.map +1 -0
  161. package/dist/commands/teamwork/teammate-config.d.ts +19 -0
  162. package/dist/commands/teamwork/teammate-config.d.ts.map +1 -0
  163. package/dist/commands/teamwork/teammate-config.js +97 -0
  164. package/dist/commands/teamwork/teammate-config.js.map +1 -0
  165. package/dist/commands/teamwork/teammate-launch.d.ts +58 -0
  166. package/dist/commands/teamwork/teammate-launch.d.ts.map +1 -0
  167. package/dist/commands/teamwork/teammate-launch.js +125 -0
  168. package/dist/commands/teamwork/teammate-launch.js.map +1 -0
  169. package/dist/commands/teamwork/text-body.d.ts +13 -0
  170. package/dist/commands/teamwork/text-body.d.ts.map +1 -0
  171. package/dist/commands/{channel → teamwork}/text-body.js +3 -3
  172. package/dist/commands/teamwork/text-body.js.map +1 -0
  173. package/dist/commands/teamwork/threads.d.ts +35 -0
  174. package/dist/commands/teamwork/threads.d.ts.map +1 -0
  175. package/dist/commands/{channel → teamwork}/threads.js +19 -19
  176. package/dist/commands/teamwork/threads.js.map +1 -0
  177. package/dist/commands/teamwork/title.d.ts +12 -0
  178. package/dist/commands/teamwork/title.d.ts.map +1 -0
  179. package/dist/commands/teamwork/title.js +24 -0
  180. package/dist/commands/teamwork/title.js.map +1 -0
  181. package/dist/commands/{channel → teamwork}/wait.d.ts +1 -5
  182. package/dist/commands/teamwork/wait.d.ts.map +1 -0
  183. package/dist/commands/{channel → teamwork}/wait.js +8 -21
  184. package/dist/commands/teamwork/wait.js.map +1 -0
  185. package/dist/commands/update.d.ts.map +1 -1
  186. package/dist/commands/update.js +48 -89
  187. package/dist/commands/update.js.map +1 -1
  188. package/dist/commands/workflow.d.ts +1 -1
  189. package/dist/commands/workflow.d.ts.map +1 -1
  190. package/dist/commands/workflow.js +9 -10
  191. package/dist/commands/workflow.js.map +1 -1
  192. package/dist/configurators/claude.d.ts +0 -17
  193. package/dist/configurators/claude.d.ts.map +1 -1
  194. package/dist/configurators/claude.js +2 -61
  195. package/dist/configurators/claude.js.map +1 -1
  196. package/dist/configurators/shared.d.ts.map +1 -1
  197. package/dist/configurators/shared.js +2 -2
  198. package/dist/configurators/shared.js.map +1 -1
  199. package/dist/configurators/snow.js +1 -1
  200. package/dist/configurators/snow.js.map +1 -1
  201. package/dist/configurators/workflow.d.ts +1 -1
  202. package/dist/configurators/workflow.js +5 -5
  203. package/dist/configurators/workflow.js.map +1 -1
  204. package/dist/constants/paths.d.ts +1 -1
  205. package/dist/constants/paths.d.ts.map +1 -1
  206. package/dist/constants/paths.js +1 -1
  207. package/dist/constants/paths.js.map +1 -1
  208. package/dist/migrations/manifests/0.7.0-beta.10.json +9 -0
  209. package/dist/migrations/manifests/0.7.0-beta.11.json +9 -0
  210. package/dist/migrations/manifests/0.7.0-beta.12.json +9 -0
  211. package/dist/migrations/manifests/0.7.0-beta.13.json +9 -0
  212. package/dist/migrations/manifests/0.7.0-beta.14.json +9 -0
  213. package/dist/migrations/manifests/0.7.0-beta.15.json +9 -0
  214. package/dist/migrations/manifests/0.7.0-beta.16.json +9 -0
  215. package/dist/migrations/manifests/0.7.0-beta.17.json +9 -0
  216. package/dist/migrations/manifests/0.7.0-beta.18.json +9 -0
  217. package/dist/migrations/manifests/0.7.0-beta.19.json +9 -0
  218. package/dist/migrations/manifests/0.7.0-beta.20.json +9 -0
  219. package/dist/migrations/manifests/0.7.0-beta.21.json +17 -0
  220. package/dist/migrations/manifests/0.7.0-beta.22.json +9 -0
  221. package/dist/migrations/manifests/0.7.0-beta.23.json +9 -0
  222. package/dist/migrations/manifests/0.7.0-beta.24.json +9 -0
  223. package/dist/migrations/manifests/0.7.0-beta.25.json +9 -0
  224. package/dist/migrations/manifests/0.7.0-beta.26.json +9 -0
  225. package/dist/migrations/manifests/0.7.0-beta.27.json +9 -0
  226. package/dist/migrations/manifests/0.7.0-beta.28.json +9 -0
  227. package/dist/migrations/manifests/0.7.0-beta.29.json +9 -0
  228. package/dist/migrations/manifests/0.7.0-beta.30.json +9 -0
  229. package/dist/migrations/manifests/0.7.0-beta.31.json +9 -0
  230. package/dist/migrations/manifests/0.7.0-beta.32.json +9 -0
  231. package/dist/migrations/manifests/0.7.0-beta.33.json +9 -0
  232. package/dist/migrations/manifests/0.7.0-beta.34.json +9 -0
  233. package/dist/migrations/manifests/0.7.0-beta.35.json +9 -0
  234. package/dist/migrations/manifests/0.7.0-beta.36.json +9 -0
  235. package/dist/migrations/manifests/0.7.0-beta.37.json +9 -0
  236. package/dist/migrations/manifests/0.7.0-beta.38.json +9 -0
  237. package/dist/migrations/manifests/0.7.0-beta.39.json +9 -0
  238. package/dist/migrations/manifests/0.7.0-beta.40.json +9 -0
  239. package/dist/migrations/manifests/0.7.0-beta.41.json +9 -0
  240. package/dist/migrations/manifests/0.7.0-beta.42.json +9 -0
  241. package/dist/migrations/manifests/0.7.0-beta.43.json +9 -0
  242. package/dist/migrations/manifests/0.7.0-beta.44.json +9 -0
  243. package/dist/migrations/manifests/0.7.0-beta.45.json +9 -0
  244. package/dist/migrations/manifests/0.7.0-beta.46.json +9 -0
  245. package/dist/migrations/manifests/0.7.0-beta.47.json +9 -0
  246. package/dist/migrations/manifests/0.7.0-beta.48.json +9 -0
  247. package/dist/migrations/manifests/0.7.0-beta.49.json +13 -0
  248. package/dist/migrations/manifests/0.7.0-beta.50.json +12 -0
  249. package/dist/migrations/manifests/0.7.0-beta.51.json +9 -0
  250. package/dist/migrations/manifests/0.7.0-beta.52.json +9 -0
  251. package/dist/migrations/manifests/0.7.0-beta.53.json +27 -0
  252. package/dist/migrations/manifests/0.7.0-beta.54.json +8 -0
  253. package/dist/migrations/manifests/0.7.1.json +9 -0
  254. package/dist/templates/claude/hooks/statusline.py +0 -7
  255. package/dist/templates/codex/hooks/session-start.py +31 -38
  256. package/dist/templates/common/bundled-skills/trellis-decision-space-construction-and-merging/SKILL.md +10 -0
  257. package/dist/templates/common/bundled-skills/trellis-decision-space-construction-and-merging/workflow.md +40 -0
  258. package/dist/templates/common/bundled-skills/trellis-delivery-review-and-closeout/SKILL.md +10 -0
  259. package/dist/templates/common/bundled-skills/trellis-delivery-review-and-closeout/workflow.md +55 -0
  260. package/dist/templates/common/bundled-skills/trellis-goal-exploration-and-requirements/SKILL.md +10 -0
  261. package/dist/templates/common/bundled-skills/trellis-goal-exploration-and-requirements/workflow.md +33 -0
  262. package/dist/templates/common/bundled-skills/trellis-meta/SKILL.md +25 -25
  263. package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-agents.md +1 -1
  264. package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-skills-or-commands.md +13 -19
  265. package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-spec-structure.md +1 -1
  266. package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-task-lifecycle.md +27 -75
  267. package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-workflow.md +17 -56
  268. package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/overview.md +1 -1
  269. package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/bundled-skills.md +14 -11
  270. package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/generated-files.md +0 -33
  271. package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/multi-agent-teamwork.md +32 -0
  272. package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/spec-system.md +1 -1
  273. package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/task-system.md +25 -111
  274. package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/workflow.md +19 -60
  275. package/dist/templates/common/bundled-skills/trellis-meta/references/platform-files/skills-and-commands.md +1 -1
  276. package/dist/templates/common/bundled-skills/trellis-session-insight/SKILL.md +1 -1
  277. package/dist/templates/common/bundled-skills/trellis-session-insight/references/cli-quick-reference.md +1 -1
  278. package/dist/templates/common/bundled-skills/trellis-solution-design-and-convergence/SKILL.md +10 -0
  279. package/dist/templates/common/bundled-skills/trellis-solution-design-and-convergence/workflow.md +36 -0
  280. package/dist/templates/common/bundled-skills/trellis-task-implementation/SKILL.md +10 -0
  281. package/dist/templates/common/bundled-skills/trellis-task-implementation/workflow.md +29 -0
  282. package/dist/templates/common/bundled-skills/trellis-teamwork/SKILL.md +73 -0
  283. package/dist/templates/common/bundled-skills/trellis-teamwork/references/coordination-modes.md +29 -0
  284. package/dist/templates/common/bundled-skills/trellis-teamwork/references/teammate-configuration-matrix.md +31 -0
  285. package/dist/templates/common/bundled-skills/trellis-teamwork/references/teammate-processes.md +37 -0
  286. package/dist/templates/common/bundled-skills/trellis-teamwork/references/teamwork-plan-construction.md +78 -0
  287. package/dist/templates/common/commands/continue.md +9 -49
  288. package/dist/templates/common/commands/finish-work.md +10 -58
  289. package/dist/templates/common/commands/start.md +19 -49
  290. package/dist/templates/common/skills/brainstorm.md +11 -199
  291. package/dist/templates/copilot/hooks/session-start.py +30 -26
  292. package/dist/templates/copilot/prompts/brainstorm.prompt.md +7 -160
  293. package/dist/templates/copilot/prompts/finish-work.prompt.md +11 -88
  294. package/dist/templates/copilot/prompts/record-session.prompt.md +4 -12
  295. package/dist/templates/copilot/prompts/start.prompt.md +12 -10
  296. package/dist/templates/dsh/DSH.md +2 -2
  297. package/dist/templates/kiro/agents/trellis.json +2 -2
  298. package/dist/templates/markdown/agents.md +14 -3
  299. package/dist/templates/markdown/spec/guides/cross-layer-thinking-guide.md.txt +9 -8
  300. package/dist/templates/markdown/spec/guides/cross-platform-thinking-guide.md.txt +1 -1
  301. package/dist/templates/markdown/workspace-index.md +0 -1
  302. package/dist/templates/omp/extensions/trellis/index.ts.txt +28 -17
  303. package/dist/templates/opencode/lib/session-utils.js +20 -25
  304. package/dist/templates/opencode/lib/trellis-context.js +4 -76
  305. package/dist/templates/opencode/plugins/inject-subagent-context.js +5 -4
  306. package/dist/templates/opencode/plugins/inject-workflow-state.js +14 -17
  307. package/dist/templates/shared-hooks/inject-shell-session-context.py +3 -3
  308. package/dist/templates/shared-hooks/inject-subagent-context.py +445 -67
  309. package/dist/templates/shared-hooks/inject-workflow-state.py +44 -36
  310. package/dist/templates/shared-hooks/session-start.py +21 -33
  311. package/dist/templates/trellis/agents/delegated-worktree-primary-agent.md +18 -0
  312. package/dist/templates/trellis/agents/teammate-profiles.json +8 -0
  313. package/dist/templates/trellis/agents/teammate.md +63 -0
  314. package/dist/templates/trellis/config.yaml +29 -38
  315. package/dist/templates/trellis/gitignore.txt +1 -0
  316. package/dist/templates/trellis/index.d.ts +8 -12
  317. package/dist/templates/trellis/index.d.ts.map +1 -1
  318. package/dist/templates/trellis/index.js +14 -25
  319. package/dist/templates/trellis/index.js.map +1 -1
  320. package/dist/templates/trellis/scripts/add_session.py +1 -1
  321. package/dist/templates/trellis/scripts/common/__init__.py +0 -1
  322. package/dist/templates/trellis/scripts/common/active_task.py +136 -100
  323. package/dist/templates/trellis/scripts/common/config.py +7 -9
  324. package/dist/templates/trellis/scripts/common/git_context.py +1 -1
  325. package/dist/templates/trellis/scripts/common/io.py +19 -0
  326. package/dist/templates/trellis/scripts/common/paths.py +0 -1
  327. package/dist/templates/trellis/scripts/common/safe_commit.py +11 -90
  328. package/dist/templates/trellis/scripts/common/session_context.py +1 -8
  329. package/dist/templates/trellis/scripts/common/spec_match.py +1 -1
  330. package/dist/templates/trellis/scripts/common/task_context.py +152 -59
  331. package/dist/templates/trellis/scripts/common/task_continuity.py +563 -0
  332. package/dist/templates/trellis/scripts/common/task_store.py +70 -548
  333. package/dist/templates/trellis/scripts/common/task_utils.py +4 -98
  334. package/dist/templates/trellis/scripts/common/task_workflow.py +1096 -0
  335. package/dist/templates/trellis/scripts/common/tasks.py +5 -8
  336. package/dist/templates/trellis/scripts/common/workflow_phase.py +8 -7
  337. package/dist/templates/trellis/scripts/hooks/linear_sync.py +3 -3
  338. package/dist/templates/trellis/scripts/task.py +114 -377
  339. package/dist/templates/trellis/workflow.md +111 -706
  340. package/dist/utils/agent-refs.d.ts +5 -5
  341. package/dist/utils/agent-refs.js +6 -6
  342. package/dist/utils/file-writer.d.ts +3 -2
  343. package/dist/utils/file-writer.d.ts.map +1 -1
  344. package/dist/utils/file-writer.js +3 -2
  345. package/dist/utils/file-writer.js.map +1 -1
  346. package/dist/utils/managed-paths.d.ts +0 -7
  347. package/dist/utils/managed-paths.d.ts.map +1 -1
  348. package/dist/utils/managed-paths.js +0 -40
  349. package/dist/utils/managed-paths.js.map +1 -1
  350. package/dist/utils/manifest-prune.d.ts.map +1 -1
  351. package/dist/utils/manifest-prune.js +2 -11
  352. package/dist/utils/manifest-prune.js.map +1 -1
  353. package/dist/utils/uninstall-scrubbers.d.ts.map +1 -1
  354. package/dist/utils/uninstall-scrubbers.js +3 -3
  355. package/dist/utils/uninstall-scrubbers.js.map +1 -1
  356. package/dist/utils/workflow-resolver.d.ts +10 -16
  357. package/dist/utils/workflow-resolver.d.ts.map +1 -1
  358. package/dist/utils/workflow-resolver.js +25 -36
  359. package/dist/utils/workflow-resolver.js.map +1 -1
  360. package/package.json +2 -2
  361. package/dist/commands/channel/adapters/claude.d.ts.map +0 -1
  362. package/dist/commands/channel/adapters/claude.js.map +0 -1
  363. package/dist/commands/channel/adapters/codex.d.ts.map +0 -1
  364. package/dist/commands/channel/adapters/codex.js.map +0 -1
  365. package/dist/commands/channel/adapters/index.d.ts.map +0 -1
  366. package/dist/commands/channel/adapters/index.js.map +0 -1
  367. package/dist/commands/channel/adapters/types.d.ts.map +0 -1
  368. package/dist/commands/channel/adapters/types.js.map +0 -1
  369. package/dist/commands/channel/agent-loader.d.ts.map +0 -1
  370. package/dist/commands/channel/agent-loader.js.map +0 -1
  371. package/dist/commands/channel/context-loader.d.ts.map +0 -1
  372. package/dist/commands/channel/context-loader.js.map +0 -1
  373. package/dist/commands/channel/context-trust.d.ts.map +0 -1
  374. package/dist/commands/channel/context-trust.js.map +0 -1
  375. package/dist/commands/channel/context.d.ts +0 -16
  376. package/dist/commands/channel/context.d.ts.map +0 -1
  377. package/dist/commands/channel/context.js.map +0 -1
  378. package/dist/commands/channel/create.d.ts +0 -29
  379. package/dist/commands/channel/create.d.ts.map +0 -1
  380. package/dist/commands/channel/create.js +0 -47
  381. package/dist/commands/channel/create.js.map +0 -1
  382. package/dist/commands/channel/dev-parse-trace.d.ts.map +0 -1
  383. package/dist/commands/channel/dev-parse-trace.js.map +0 -1
  384. package/dist/commands/channel/guard.d.ts.map +0 -1
  385. package/dist/commands/channel/guard.js.map +0 -1
  386. package/dist/commands/channel/index.d.ts +0 -3
  387. package/dist/commands/channel/index.d.ts.map +0 -1
  388. package/dist/commands/channel/index.js.map +0 -1
  389. package/dist/commands/channel/interrupt.d.ts.map +0 -1
  390. package/dist/commands/channel/interrupt.js +0 -22
  391. package/dist/commands/channel/interrupt.js.map +0 -1
  392. package/dist/commands/channel/kill.d.ts +0 -7
  393. package/dist/commands/channel/kill.d.ts.map +0 -1
  394. package/dist/commands/channel/kill.js.map +0 -1
  395. package/dist/commands/channel/list.d.ts +0 -18
  396. package/dist/commands/channel/list.d.ts.map +0 -1
  397. package/dist/commands/channel/list.js.map +0 -1
  398. package/dist/commands/channel/messages.d.ts.map +0 -1
  399. package/dist/commands/channel/messages.js.map +0 -1
  400. package/dist/commands/channel/rm.d.ts +0 -27
  401. package/dist/commands/channel/rm.d.ts.map +0 -1
  402. package/dist/commands/channel/rm.js.map +0 -1
  403. package/dist/commands/channel/run.d.ts +0 -32
  404. package/dist/commands/channel/run.js +0 -141
  405. package/dist/commands/channel/run.js.map +0 -1
  406. package/dist/commands/channel/send.d.ts.map +0 -1
  407. package/dist/commands/channel/send.js.map +0 -1
  408. package/dist/commands/channel/spawn.d.ts.map +0 -1
  409. package/dist/commands/channel/spawn.js.map +0 -1
  410. package/dist/commands/channel/store/events.d.ts +0 -22
  411. package/dist/commands/channel/store/events.d.ts.map +0 -1
  412. package/dist/commands/channel/store/events.js.map +0 -1
  413. package/dist/commands/channel/store/filter.d.ts +0 -3
  414. package/dist/commands/channel/store/filter.d.ts.map +0 -1
  415. package/dist/commands/channel/store/filter.js +0 -2
  416. package/dist/commands/channel/store/filter.js.map +0 -1
  417. package/dist/commands/channel/store/lock.d.ts.map +0 -1
  418. package/dist/commands/channel/store/lock.js.map +0 -1
  419. package/dist/commands/channel/store/paths.d.ts.map +0 -1
  420. package/dist/commands/channel/store/paths.js.map +0 -1
  421. package/dist/commands/channel/store/schema.d.ts +0 -27
  422. package/dist/commands/channel/store/schema.d.ts.map +0 -1
  423. package/dist/commands/channel/store/schema.js +0 -34
  424. package/dist/commands/channel/store/schema.js.map +0 -1
  425. package/dist/commands/channel/store/thread-state.d.ts.map +0 -1
  426. package/dist/commands/channel/store/thread-state.js.map +0 -1
  427. package/dist/commands/channel/store/watch.d.ts +0 -19
  428. package/dist/commands/channel/store/watch.d.ts.map +0 -1
  429. package/dist/commands/channel/store/watch.js.map +0 -1
  430. package/dist/commands/channel/supervisor/idle.d.ts.map +0 -1
  431. package/dist/commands/channel/supervisor/idle.js.map +0 -1
  432. package/dist/commands/channel/supervisor/inbox.d.ts.map +0 -1
  433. package/dist/commands/channel/supervisor/inbox.js.map +0 -1
  434. package/dist/commands/channel/supervisor/shutdown.d.ts.map +0 -1
  435. package/dist/commands/channel/supervisor/shutdown.js.map +0 -1
  436. package/dist/commands/channel/supervisor/stdout.d.ts.map +0 -1
  437. package/dist/commands/channel/supervisor/stdout.js.map +0 -1
  438. package/dist/commands/channel/supervisor/turns.d.ts.map +0 -1
  439. package/dist/commands/channel/supervisor/turns.js.map +0 -1
  440. package/dist/commands/channel/supervisor/warning.d.ts.map +0 -1
  441. package/dist/commands/channel/supervisor/warning.js.map +0 -1
  442. package/dist/commands/channel/supervisor.d.ts.map +0 -1
  443. package/dist/commands/channel/supervisor.js.map +0 -1
  444. package/dist/commands/channel/text-body.d.ts +0 -13
  445. package/dist/commands/channel/text-body.d.ts.map +0 -1
  446. package/dist/commands/channel/text-body.js.map +0 -1
  447. package/dist/commands/channel/threads.d.ts +0 -39
  448. package/dist/commands/channel/threads.d.ts.map +0 -1
  449. package/dist/commands/channel/threads.js.map +0 -1
  450. package/dist/commands/channel/title.d.ts +0 -12
  451. package/dist/commands/channel/title.d.ts.map +0 -1
  452. package/dist/commands/channel/title.js +0 -24
  453. package/dist/commands/channel/title.js.map +0 -1
  454. package/dist/commands/channel/wait.d.ts.map +0 -1
  455. package/dist/commands/channel/wait.js.map +0 -1
  456. package/dist/commands/channel/workers.d.ts +0 -9
  457. package/dist/commands/channel/workers.d.ts.map +0 -1
  458. package/dist/commands/channel/workers.js +0 -31
  459. package/dist/commands/channel/workers.js.map +0 -1
  460. package/dist/templates/common/bundled-skills/trellis-channel/SKILL.md +0 -69
  461. package/dist/templates/common/bundled-skills/trellis-channel/references/command-reference.md +0 -549
  462. package/dist/templates/common/bundled-skills/trellis-channel/references/forum.md +0 -233
  463. package/dist/templates/common/bundled-skills/trellis-channel/references/progress-debugging.md +0 -237
  464. package/dist/templates/common/bundled-skills/trellis-channel/references/subnode-work.md +0 -222
  465. package/dist/templates/common/bundled-skills/trellis-channel/references/workers.md +0 -295
  466. package/dist/templates/common/bundled-skills/trellis-channel/references/workflows.md +0 -131
  467. package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/multi-agent-channel.md +0 -70
  468. package/dist/templates/common/bundled-skills/trellis-research-record/SKILL.md +0 -14
  469. package/dist/templates/copilot/prompts/parallel.prompt.md +0 -204
  470. package/dist/templates/trellis/agents/check.md +0 -70
  471. package/dist/templates/trellis/agents/implement.md +0 -71
  472. package/dist/templates/trellis/agents/subnode.env +0 -4
  473. package/dist/templates/trellis/agents/subnode.md +0 -76
  474. package/dist/templates/trellis/scripts/common/context_projection.py +0 -508
  475. package/dist/templates/trellis/scripts/common/continuation_record.py +0 -450
  476. package/dist/templates/trellis/scripts/common/ownership_record.py +0 -439
  477. package/dist/templates/trellis/scripts/subnode_artifact.py +0 -735
  478. package/dist/templates/trellis/scripts/workspace_note.py +0 -126
  479. /package/dist/commands/{channel → teamwork}/adapters/types.js +0 -0
  480. /package/dist/commands/{channel → teamwork}/supervisor/idle.d.ts +0 -0
  481. /package/dist/commands/{channel → teamwork}/supervisor/idle.js +0 -0
  482. /package/dist/commands/{channel → teamwork}/supervisor/turns.d.ts +0 -0
  483. /package/dist/commands/{channel → teamwork}/supervisor/turns.js +0 -0
@@ -1,450 +0,0 @@
1
- """Bounded, task-scoped Continuation Record runtime projection."""
2
-
3
- from __future__ import annotations
4
-
5
- import hashlib
6
- import json
7
- import os
8
- import re
9
- import tempfile
10
- from contextlib import contextmanager
11
- from datetime import datetime, timezone
12
- from pathlib import Path
13
- from typing import Any
14
-
15
- from .active_task import resolve_active_task
16
- from .git import run_git
17
- from .io import read_json_checked
18
- from .paths import FILE_TASK_JSON, get_repo_root
19
-
20
-
21
- SCHEMA_VERSION = 1
22
- KIND = "trellis-continuation-record"
23
- STATUS_ABSENT = "absent"
24
- STATUS_READY = "ready"
25
- STATUS_STALE = "stale"
26
- STATUS_WITHHELD = "withheld"
27
- MAX_RECORD_BYTES = 128 * 1024
28
- MAX_REQUEST_BYTES = 64 * 1024
29
- MAX_TEXT_BYTES = 4096
30
- MAX_LIST = 32
31
- MAX_EVIDENCE = 32
32
- MAX_EVIDENCE_BYTES = 2 * 1024 * 1024
33
- MAX_TASK_FILES = 512
34
- MAX_TASK_FILE_BYTES = 2 * 1024 * 1024
35
- SAFE_ID = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$")
36
- SHA256 = re.compile(r"^sha256:[0-9a-f]{64}$")
37
- SECRET = re.compile(
38
- r"(?:-----BEGIN [A-Z0-9 ]+ PRIVATE KEY-----|bearer\s+[A-Za-z0-9._~+/=-]{20,}|"
39
- r"\b(?:sk|th)-[A-Za-z0-9_-]{20,}|(?:api[_-]?key|password|secret)\s*[:=]\s*\S+)",
40
- re.IGNORECASE,
41
- )
42
-
43
-
44
- class ContinuationError(ValueError):
45
- """Raised when a Continuation Record cannot be safely read or written."""
46
-
47
-
48
- class NoDirectCurrentTask(ContinuationError):
49
- """Raised when the current session has no directly bound active task."""
50
-
51
-
52
- def _canonical(value: Any) -> bytes:
53
- return json.dumps(value, ensure_ascii=True, sort_keys=True, separators=(",", ":")).encode("utf-8")
54
-
55
-
56
- def _digest(value: Any) -> str:
57
- return "sha256:" + hashlib.sha256(_canonical(value)).hexdigest()
58
-
59
-
60
- def _text(value: Any, label: str, maximum: int = MAX_TEXT_BYTES) -> str:
61
- if not isinstance(value, str) or not value.strip() or len(value.encode("utf-8")) > maximum:
62
- raise ContinuationError(f"{label} is invalid")
63
- if any(ord(char) < 32 and char not in "\n\t" for char in value) or SECRET.search(value):
64
- raise ContinuationError(f"{label} is unsafe")
65
- return value
66
-
67
-
68
- def _list(value: Any, label: str) -> list[str]:
69
- if not isinstance(value, list) or len(value) > MAX_LIST:
70
- raise ContinuationError(f"{label} is invalid")
71
- return [_text(item, f"{label}[{index}]") for index, item in enumerate(value)]
72
-
73
-
74
- def _safe_task_id(task_id: Any) -> str:
75
- value = _text(task_id, "task.id", 128)
76
- if not SAFE_ID.fullmatch(value) or "/" in value or "\\" in value or value in {".", ".."}:
77
- raise ContinuationError("task.id is unsafe")
78
- return value
79
-
80
-
81
- def _record_path(root: Path, task_id: str) -> Path:
82
- safe_id = _safe_task_id(task_id)
83
- runtime = root / ".trellis" / ".runtime"
84
- records = runtime / "continuation-records"
85
- task_dir = records / safe_id
86
- if runtime.is_symlink() or records.is_symlink() or task_dir.is_symlink():
87
- raise ContinuationError("continuation record directory is unsafe")
88
- return task_dir / "record.json"
89
-
90
-
91
- def _regular(path: Path, label: str) -> Path:
92
- if path.is_symlink() or not path.is_file():
93
- raise ContinuationError(f"{label} is not a regular file")
94
- return path
95
-
96
-
97
- def _project_file(root: Path, value: Any, label: str) -> Path:
98
- relative = _text(value, label, 1024)
99
- candidate = Path(relative)
100
- if candidate.is_absolute() or ".." in candidate.parts or relative.startswith(".trellis/.runtime/"):
101
- raise ContinuationError(f"{label} must be project-relative")
102
- cursor = root
103
- for part in candidate.parts:
104
- cursor /= part
105
- if cursor.is_symlink():
106
- raise ContinuationError(f"{label} contains a symbolic path")
107
- resolved = (root / candidate).resolve()
108
- try:
109
- resolved.relative_to(root.resolve())
110
- except ValueError as exc:
111
- raise ContinuationError(f"{label} escapes project") from exc
112
- return resolved
113
-
114
-
115
- def _file_digest(path: Path, maximum: int = MAX_EVIDENCE_BYTES) -> tuple[str, int]:
116
- hasher = hashlib.sha256()
117
- size = 0
118
- with _regular(path, "evidence file").open("rb") as handle:
119
- while chunk := handle.read(256 * 1024):
120
- size += len(chunk)
121
- if size > maximum:
122
- raise ContinuationError("source file is too large")
123
- hasher.update(chunk)
124
- return "sha256:" + hasher.hexdigest(), size
125
-
126
-
127
- def _task_source(root: Path) -> dict[str, Any]:
128
- active = resolve_active_task(root, allow_single_session_fallback=True)
129
- if not active.task_path:
130
- raise NoDirectCurrentTask("no_direct_current_task")
131
- if active.source_type == "session-fallback":
132
- raise ContinuationError("session_fallback_untrusted")
133
- if active.source_type != "session" or not active.context_key:
134
- raise NoDirectCurrentTask("no_direct_current_task")
135
- if active.stale:
136
- raise ContinuationError("current_session_pointer_stale")
137
- task_path = _project_file(root, active.task_path, "task path")
138
- task_file = _regular(task_path / FILE_TASK_JSON, "task.json")
139
- data, reason = read_json_checked(task_file)
140
- if data is None:
141
- raise ContinuationError(f"task.json is {reason}")
142
- task_id = _safe_task_id(data.get("id") or data.get("name"))
143
- entries: list[dict[str, Any]] = []
144
- for item in sorted(task_path.rglob("*")):
145
- if item.is_symlink() or not item.is_file():
146
- continue
147
- if len(entries) >= MAX_TASK_FILES:
148
- raise ContinuationError("task has too many material files")
149
- item_digest, size = _file_digest(item, MAX_TASK_FILE_BYTES)
150
- entries.append({"path": item.relative_to(root).as_posix(), "bytes": size, "sha256": item_digest})
151
- return {
152
- "id": task_id,
153
- "path": active.task_path,
154
- "status": _text(data.get("status"), "task.status", 64),
155
- "material_digest": _digest(entries),
156
- "context_key": active.context_key,
157
- }
158
-
159
-
160
- def _git_source(root: Path) -> dict[str, Any]:
161
- def run(*args: str) -> str:
162
- rc, out, _ = run_git(list(args), cwd=root, timeout=20)
163
- if rc:
164
- raise ContinuationError("git source snapshot failed")
165
- return out.strip()
166
-
167
- dirty = [
168
- line
169
- for line in run("status", "--porcelain=v1").splitlines()
170
- if line and ".trellis/.runtime/continuation-records/" not in line[3:]
171
- ]
172
- branch = run("branch", "--show-current")
173
- head = run("rev-parse", "HEAD")
174
- return {
175
- "branch": branch or None,
176
- "head": _text(head, "git.head", 128),
177
- "worktree_state": "dirty" if dirty else "clean",
178
- "dirty_paths_digest": _digest(dirty),
179
- }
180
-
181
-
182
- def _evidence(root: Path, values: Any) -> list[dict[str, Any]]:
183
- if not isinstance(values, list) or len(values) > MAX_EVIDENCE:
184
- raise ContinuationError("evidence must be a bounded list")
185
- result = []
186
- seen: set[str] = set()
187
- for item in values:
188
- path = _project_file(root, item, "evidence path")
189
- relative = path.relative_to(root).as_posix()
190
- if relative in seen:
191
- raise ContinuationError("evidence paths must be unique")
192
- seen.add(relative)
193
- file_digest, size = _file_digest(path)
194
- result.append({"path": relative, "bytes": size, "sha256": file_digest})
195
- return result
196
-
197
-
198
- def _source(root: Path, task: dict[str, Any], evidence: list[dict[str, Any]]) -> dict[str, Any]:
199
- git = _git_source(root)
200
- source = {
201
- "task": {key: task[key] for key in ("id", "path", "status", "material_digest")},
202
- "git": git,
203
- "evidence": evidence,
204
- }
205
- source["source_digest"] = _digest(source)
206
- return source
207
-
208
-
209
- def _request(root: Path, path: Path) -> tuple[dict[str, Any], list[dict[str, Any]]]:
210
- _regular(path, "continuity request")
211
- if path.stat().st_size > MAX_REQUEST_BYTES:
212
- raise ContinuationError("continuity request is too large")
213
- try:
214
- value = json.loads(path.read_text(encoding="utf-8"))
215
- except (OSError, UnicodeError, json.JSONDecodeError) as exc:
216
- raise ContinuationError("continuity request is invalid") from exc
217
- if not isinstance(value, dict) or set(value) != {"objective", "decisions", "completed", "open_items", "blockers", "next_safe_action", "non_transferable_operations", "evidence"}:
218
- raise ContinuationError("continuity request fields are invalid")
219
- content = {key: _text(value[key], f"content.{key}") if key in {"objective", "next_safe_action"} else _list(value[key], f"content.{key}") for key in value if key != "evidence"}
220
- return content, _evidence(root, value["evidence"])
221
-
222
-
223
- def _request_path(root: Path, path: Path) -> Path:
224
- try:
225
- relative = path.relative_to(root)
226
- except ValueError as exc:
227
- raise ContinuationError("continuity request must be project-relative") from exc
228
- return _project_file(root, relative.as_posix(), "continuity request")
229
-
230
-
231
- def _expected_digest(value: str) -> str:
232
- if value == "absent" or SHA256.fullmatch(value):
233
- return value
234
- raise ContinuationError("expected record digest is invalid")
235
-
236
-
237
- def _ensure_record_parent(path: Path) -> None:
238
- if path.parent.is_symlink() or path.parent.parent.is_symlink():
239
- raise ContinuationError("continuation record directory is unsafe")
240
- path.parent.mkdir(parents=True, exist_ok=True)
241
- if path.parent.is_symlink() or path.parent.parent.is_symlink():
242
- raise ContinuationError("continuation record directory is unsafe")
243
- os.chmod(path.parent.parent, 0o700)
244
- os.chmod(path.parent, 0o700)
245
-
246
-
247
- @contextmanager
248
- def _record_lock(path: Path):
249
- _ensure_record_parent(path)
250
- lock = path.parent / ".lock"
251
- if lock.is_symlink():
252
- raise ContinuationError("continuation record lock is unsafe")
253
- # ponytail: one advisory lock per task record; a lock service is unnecessary unless distributed writers are introduced.
254
- with lock.open("a+", encoding="utf-8") as handle:
255
- try:
256
- import fcntl
257
- fcntl.flock(handle.fileno(), fcntl.LOCK_EX)
258
- except (ImportError, OSError) as exc:
259
- raise ContinuationError("continuation record lock unavailable") from exc
260
- try:
261
- yield
262
- finally:
263
- fcntl.flock(handle.fileno(), fcntl.LOCK_UN)
264
-
265
-
266
- def _record_digest(record: dict[str, Any]) -> str:
267
- return _digest({key: value for key, value in record.items() if key != "integrity"})
268
-
269
-
270
- def _decode(path: Path) -> dict[str, Any]:
271
- _regular(path, "continuation record")
272
- if path.stat().st_size > MAX_RECORD_BYTES:
273
- raise ContinuationError("continuation record is too large")
274
- data, reason = read_json_checked(path)
275
- if data is None:
276
- raise ContinuationError(f"continuation record is {reason}")
277
- expected = {"schema_version", "kind", "task", "source", "content", "revision", "updated_at", "integrity"}
278
- if set(data) != expected or data["schema_version"] != SCHEMA_VERSION or data["kind"] != KIND:
279
- raise ContinuationError("continuation record schema is unsupported")
280
- task = data["task"]
281
- if not isinstance(task, dict) or set(task) != {"id", "path", "status", "material_digest"}:
282
- raise ContinuationError("continuation record task is invalid")
283
- _safe_task_id(task["id"])
284
- task_path = _text(task["path"], "task.path", 1024)
285
- task_path_value = Path(task_path)
286
- if task_path_value.is_absolute() or ".." in task_path_value.parts or not task_path.startswith(".trellis/tasks/"):
287
- raise ContinuationError("task.path is unsafe")
288
- _text(task["status"], "task.status", 64)
289
- if not SHA256.fullmatch(task["material_digest"]):
290
- raise ContinuationError("task.material_digest is invalid")
291
- source = data["source"]
292
- if not isinstance(source, dict) or set(source) != {"task", "git", "evidence", "source_digest"}:
293
- raise ContinuationError("continuation record source is invalid")
294
- if source["task"] != task:
295
- raise ContinuationError("continuation record task source is invalid")
296
- git = source["git"]
297
- if not isinstance(git, dict) or set(git) != {"branch", "head", "worktree_state", "dirty_paths_digest"}:
298
- raise ContinuationError("continuation record git source is invalid")
299
- if git["branch"] is not None:
300
- _text(git["branch"], "git.branch", 512)
301
- _text(git["head"], "git.head", 128)
302
- if git["worktree_state"] not in {"clean", "dirty"} or not SHA256.fullmatch(git["dirty_paths_digest"]):
303
- raise ContinuationError("continuation record git source is invalid")
304
- if not isinstance(source["evidence"], list) or len(source["evidence"]) > MAX_EVIDENCE:
305
- raise ContinuationError("continuation record evidence is invalid")
306
- for index, item in enumerate(source["evidence"]):
307
- if not isinstance(item, dict) or set(item) != {"path", "bytes", "sha256"}:
308
- raise ContinuationError("continuation record evidence item is invalid")
309
- _text(item["path"], f"evidence[{index}].path", 1024)
310
- if not isinstance(item["bytes"], int) or item["bytes"] < 0 or not SHA256.fullmatch(item["sha256"]):
311
- raise ContinuationError("continuation record evidence item is invalid")
312
- if not SHA256.fullmatch(source["source_digest"]):
313
- raise ContinuationError("source digest is invalid")
314
- if source["source_digest"] != _digest({key: value for key, value in source.items() if key != "source_digest"}):
315
- raise ContinuationError("source digest does not match")
316
- content = data["content"]
317
- if not isinstance(content, dict) or set(content) != {"objective", "decisions", "completed", "open_items", "blockers", "next_safe_action", "non_transferable_operations"}:
318
- raise ContinuationError("continuation record content is invalid")
319
- _text(content["objective"], "content.objective")
320
- _text(content["next_safe_action"], "content.next_safe_action")
321
- for key in ("decisions", "completed", "open_items", "blockers", "non_transferable_operations"):
322
- _list(content[key], f"content.{key}")
323
- if not isinstance(data["revision"], int) or data["revision"] < 1:
324
- raise ContinuationError("revision is invalid")
325
- _text(data["updated_at"], "updated_at", 128)
326
- integrity = data["integrity"]
327
- if not isinstance(integrity, dict) or set(integrity) != {"record_digest"} or not SHA256.fullmatch(integrity["record_digest"]):
328
- raise ContinuationError("record digest is invalid")
329
- if integrity["record_digest"] != _record_digest(data):
330
- raise ContinuationError("record digest does not match")
331
- return data
332
-
333
-
334
- def status(root: Path | None = None) -> dict[str, Any]:
335
- root = (root or get_repo_root()).resolve()
336
- try:
337
- task = _task_source(root)
338
- except NoDirectCurrentTask:
339
- return {"status": STATUS_ABSENT, "reason": "no_direct_current_task"}
340
- except ContinuationError as exc:
341
- return {"status": STATUS_WITHHELD, "reason": str(exc)}
342
- try:
343
- path = _record_path(root, task["id"])
344
- except ContinuationError as exc:
345
- return {"status": STATUS_WITHHELD, "reason": str(exc)}
346
- if not path.exists():
347
- return {"status": STATUS_ABSENT, "task": {key: task[key] for key in ("id", "path", "status")}}
348
- try:
349
- record = _decode(path)
350
- current = _source(root, task, _evidence(root, [item["path"] for item in record["source"]["evidence"]]))
351
- state = STATUS_READY if record["source"] == current else STATUS_STALE
352
- result = {"status": state, "task": record["task"], "revision": record["revision"], "record_digest": record["integrity"]["record_digest"]}
353
- if state == STATUS_READY:
354
- result["content"] = record["content"]
355
- else:
356
- result["reason"] = "source_drift"
357
- return result
358
- except (ContinuationError, OSError) as exc:
359
- return {"status": STATUS_WITHHELD, "task": {key: task[key] for key in ("id", "path", "status")}, "reason": str(exc)}
360
-
361
-
362
- def seal(root: Path, request_path: Path, expected: str) -> dict[str, Any]:
363
- expected = _expected_digest(expected)
364
- request_path = _request_path(root, request_path)
365
- initial_task = _task_source(root)
366
- path = _record_path(root, initial_task["id"])
367
- with _record_lock(path):
368
- task = _task_source(root)
369
- if (
370
- (task["id"], task["path"], task["context_key"])
371
- != (initial_task["id"], initial_task["path"], initial_task["context_key"])
372
- ):
373
- raise ContinuationError("current task changed while acquiring record lock")
374
- current_digest: str | None = None
375
- current: dict[str, Any] | None = None
376
- if path.exists():
377
- current = _decode(path)
378
- current_digest = current["integrity"]["record_digest"]
379
- current_source = _source(root, task, _evidence(root, [item["path"] for item in current["source"]["evidence"]]))
380
- if current["source"] != current_source:
381
- raise ContinuationError("existing continuation record source drifted")
382
- if expected != "absent" and expected != current_digest:
383
- raise ContinuationError("expected record digest does not match")
384
- if expected == "absent" and current_digest is not None:
385
- raise ContinuationError("record already exists")
386
- content, evidence = _request(root, request_path)
387
- source = _source(root, task, evidence)
388
- current_task = _task_source(root)
389
- if task != current_task or source != _source(root, current_task, _evidence(root, [item["path"] for item in evidence])):
390
- raise ContinuationError("continuation source changed while sealing")
391
- record: dict[str, Any] = {
392
- "schema_version": SCHEMA_VERSION,
393
- "kind": KIND,
394
- "task": {key: task[key] for key in ("id", "path", "status", "material_digest")},
395
- "source": source,
396
- "content": content,
397
- "revision": 1 if current is None else current["revision"] + 1,
398
- "updated_at": datetime.now(timezone.utc).isoformat(timespec="microseconds").replace("+00:00", "Z"),
399
- }
400
- record["integrity"] = {"record_digest": _record_digest(record)}
401
- encoded = json.dumps(record, ensure_ascii=True, indent=2, sort_keys=True) + "\n"
402
- if len(encoded.encode("utf-8")) > MAX_RECORD_BYTES:
403
- raise ContinuationError("continuation record is too large")
404
- temporary: Path | None = None
405
- try:
406
- with tempfile.NamedTemporaryFile("w", encoding="utf-8", dir=path.parent, delete=False) as handle:
407
- temporary = Path(handle.name)
408
- handle.write(encoded)
409
- handle.flush()
410
- os.fsync(handle.fileno())
411
- os.chmod(temporary, 0o600)
412
- os.replace(temporary, path)
413
- directory_fd = os.open(path.parent, os.O_RDONLY)
414
- try:
415
- os.fsync(directory_fd)
416
- finally:
417
- os.close(directory_fd)
418
- except OSError as exc:
419
- if temporary is not None:
420
- temporary.unlink(missing_ok=True)
421
- raise ContinuationError("continuation record atomic write failed") from exc
422
- return {"status": STATUS_READY, "revision": record["revision"], "record_digest": record["integrity"]["record_digest"]}
423
-
424
-
425
- def clear(root: Path, expected: str) -> dict[str, Any]:
426
- expected = _expected_digest(expected)
427
- initial_task = _task_source(root)
428
- path = _record_path(root, initial_task["id"])
429
- with _record_lock(path):
430
- task = _task_source(root)
431
- if (
432
- (task["id"], task["path"], task["context_key"])
433
- != (initial_task["id"], initial_task["path"], initial_task["context_key"])
434
- ):
435
- raise ContinuationError("current task changed while acquiring record lock")
436
- if not path.exists():
437
- if expected == "absent":
438
- return {"status": STATUS_ABSENT}
439
- raise ContinuationError("record is absent")
440
- record = _decode(path)
441
- actual = record["integrity"]["record_digest"]
442
- if actual != expected:
443
- raise ContinuationError("expected record digest does not match")
444
- path.unlink()
445
- directory_fd = os.open(path.parent, os.O_RDONLY)
446
- try:
447
- os.fsync(directory_fd)
448
- finally:
449
- os.close(directory_fd)
450
- return {"status": STATUS_ABSENT, "cleared": actual}