@ibotor/smart-trellis 0.5.19

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 (520) hide show
  1. package/LICENSE +235 -0
  2. package/README.md +263 -0
  3. package/bin/smart-trellis.js +3 -0
  4. package/bin/trellis.js +3 -0
  5. package/dist/cli/index.d.ts +3 -0
  6. package/dist/cli/index.d.ts.map +1 -0
  7. package/dist/cli/index.js +132 -0
  8. package/dist/cli/index.js.map +1 -0
  9. package/dist/cli/smart.d.ts +2 -0
  10. package/dist/cli/smart.d.ts.map +1 -0
  11. package/dist/cli/smart.js +41 -0
  12. package/dist/cli/smart.js.map +1 -0
  13. package/dist/commands/init.d.ts +56 -0
  14. package/dist/commands/init.d.ts.map +1 -0
  15. package/dist/commands/init.js +1466 -0
  16. package/dist/commands/init.js.map +1 -0
  17. package/dist/commands/smart-init.d.ts +22 -0
  18. package/dist/commands/smart-init.d.ts.map +1 -0
  19. package/dist/commands/smart-init.js +122 -0
  20. package/dist/commands/smart-init.js.map +1 -0
  21. package/dist/commands/uninstall.d.ts +27 -0
  22. package/dist/commands/uninstall.d.ts.map +1 -0
  23. package/dist/commands/uninstall.js +339 -0
  24. package/dist/commands/uninstall.js.map +1 -0
  25. package/dist/commands/update.d.ts +72 -0
  26. package/dist/commands/update.d.ts.map +1 -0
  27. package/dist/commands/update.js +1926 -0
  28. package/dist/commands/update.js.map +1 -0
  29. package/dist/configurators/antigravity.d.ts +7 -0
  30. package/dist/configurators/antigravity.d.ts.map +1 -0
  31. package/dist/configurators/antigravity.js +19 -0
  32. package/dist/configurators/antigravity.js.map +1 -0
  33. package/dist/configurators/claude.d.ts +9 -0
  34. package/dist/configurators/claude.d.ts.map +1 -0
  35. package/dist/configurators/claude.js +72 -0
  36. package/dist/configurators/claude.js.map +1 -0
  37. package/dist/configurators/codebuddy.d.ts +10 -0
  38. package/dist/configurators/codebuddy.d.ts.map +1 -0
  39. package/dist/configurators/codebuddy.js +30 -0
  40. package/dist/configurators/codebuddy.js.map +1 -0
  41. package/dist/configurators/codex.d.ts +8 -0
  42. package/dist/configurators/codex.d.ts.map +1 -0
  43. package/dist/configurators/codex.js +85 -0
  44. package/dist/configurators/codex.js.map +1 -0
  45. package/dist/configurators/copilot.d.ts +10 -0
  46. package/dist/configurators/copilot.d.ts.map +1 -0
  47. package/dist/configurators/copilot.js +51 -0
  48. package/dist/configurators/copilot.js.map +1 -0
  49. package/dist/configurators/cursor.d.ts +10 -0
  50. package/dist/configurators/cursor.d.ts.map +1 -0
  51. package/dist/configurators/cursor.js +29 -0
  52. package/dist/configurators/cursor.js.map +1 -0
  53. package/dist/configurators/droid.d.ts +10 -0
  54. package/dist/configurators/droid.d.ts.map +1 -0
  55. package/dist/configurators/droid.js +30 -0
  56. package/dist/configurators/droid.js.map +1 -0
  57. package/dist/configurators/gemini.d.ts +16 -0
  58. package/dist/configurators/gemini.d.ts.map +1 -0
  59. package/dist/configurators/gemini.js +38 -0
  60. package/dist/configurators/gemini.js.map +1 -0
  61. package/dist/configurators/index.d.ts +65 -0
  62. package/dist/configurators/index.d.ts.map +1 -0
  63. package/dist/configurators/index.js +367 -0
  64. package/dist/configurators/index.js.map +1 -0
  65. package/dist/configurators/kilo.d.ts +7 -0
  66. package/dist/configurators/kilo.d.ts.map +1 -0
  67. package/dist/configurators/kilo.js +19 -0
  68. package/dist/configurators/kilo.js.map +1 -0
  69. package/dist/configurators/kiro.d.ts +8 -0
  70. package/dist/configurators/kiro.d.ts.map +1 -0
  71. package/dist/configurators/kiro.js +24 -0
  72. package/dist/configurators/kiro.js.map +1 -0
  73. package/dist/configurators/opencode.d.ts +14 -0
  74. package/dist/configurators/opencode.d.ts.map +1 -0
  75. package/dist/configurators/opencode.js +96 -0
  76. package/dist/configurators/opencode.js.map +1 -0
  77. package/dist/configurators/pi.d.ts +3 -0
  78. package/dist/configurators/pi.d.ts.map +1 -0
  79. package/dist/configurators/pi.js +45 -0
  80. package/dist/configurators/pi.js.map +1 -0
  81. package/dist/configurators/qoder.d.ts +11 -0
  82. package/dist/configurators/qoder.d.ts.map +1 -0
  83. package/dist/configurators/qoder.js +31 -0
  84. package/dist/configurators/qoder.js.map +1 -0
  85. package/dist/configurators/shared.d.ts +178 -0
  86. package/dist/configurators/shared.d.ts.map +1 -0
  87. package/dist/configurators/shared.js +538 -0
  88. package/dist/configurators/shared.js.map +1 -0
  89. package/dist/configurators/windsurf.d.ts +7 -0
  90. package/dist/configurators/windsurf.d.ts.map +1 -0
  91. package/dist/configurators/windsurf.js +19 -0
  92. package/dist/configurators/windsurf.js.map +1 -0
  93. package/dist/configurators/workflow.d.ts +29 -0
  94. package/dist/configurators/workflow.d.ts.map +1 -0
  95. package/dist/configurators/workflow.js +163 -0
  96. package/dist/configurators/workflow.js.map +1 -0
  97. package/dist/constants/paths.d.ts +70 -0
  98. package/dist/constants/paths.d.ts.map +1 -0
  99. package/dist/constants/paths.js +79 -0
  100. package/dist/constants/paths.js.map +1 -0
  101. package/dist/constants/version.d.ts +9 -0
  102. package/dist/constants/version.d.ts.map +1 -0
  103. package/dist/constants/version.js +15 -0
  104. package/dist/constants/version.js.map +1 -0
  105. package/dist/index.d.ts +10 -0
  106. package/dist/index.d.ts.map +1 -0
  107. package/dist/index.js +10 -0
  108. package/dist/index.js.map +1 -0
  109. package/dist/migrations/index.d.ts +62 -0
  110. package/dist/migrations/index.d.ts.map +1 -0
  111. package/dist/migrations/index.js +187 -0
  112. package/dist/migrations/index.js.map +1 -0
  113. package/dist/migrations/manifests/0.1.9.json +30 -0
  114. package/dist/migrations/manifests/0.2.0.json +49 -0
  115. package/dist/migrations/manifests/0.2.12.json +9 -0
  116. package/dist/migrations/manifests/0.2.13.json +9 -0
  117. package/dist/migrations/manifests/0.2.14.json +175 -0
  118. package/dist/migrations/manifests/0.2.15.json +33 -0
  119. package/dist/migrations/manifests/0.3.0-beta.0.json +297 -0
  120. package/dist/migrations/manifests/0.3.0-beta.1.json +9 -0
  121. package/dist/migrations/manifests/0.3.0-beta.10.json +9 -0
  122. package/dist/migrations/manifests/0.3.0-beta.11.json +9 -0
  123. package/dist/migrations/manifests/0.3.0-beta.12.json +9 -0
  124. package/dist/migrations/manifests/0.3.0-beta.13.json +9 -0
  125. package/dist/migrations/manifests/0.3.0-beta.14.json +9 -0
  126. package/dist/migrations/manifests/0.3.0-beta.15.json +9 -0
  127. package/dist/migrations/manifests/0.3.0-beta.16.json +9 -0
  128. package/dist/migrations/manifests/0.3.0-beta.2.json +9 -0
  129. package/dist/migrations/manifests/0.3.0-beta.3.json +9 -0
  130. package/dist/migrations/manifests/0.3.0-beta.4.json +9 -0
  131. package/dist/migrations/manifests/0.3.0-beta.5.json +9 -0
  132. package/dist/migrations/manifests/0.3.0-beta.6.json +9 -0
  133. package/dist/migrations/manifests/0.3.0-beta.7.json +11 -0
  134. package/dist/migrations/manifests/0.3.0-beta.8.json +9 -0
  135. package/dist/migrations/manifests/0.3.0-beta.9.json +9 -0
  136. package/dist/migrations/manifests/0.3.0-rc.0.json +9 -0
  137. package/dist/migrations/manifests/0.3.0-rc.1.json +9 -0
  138. package/dist/migrations/manifests/0.3.0-rc.2.json +9 -0
  139. package/dist/migrations/manifests/0.3.0-rc.3.json +9 -0
  140. package/dist/migrations/manifests/0.3.0-rc.4.json +9 -0
  141. package/dist/migrations/manifests/0.3.0-rc.5.json +9 -0
  142. package/dist/migrations/manifests/0.3.0-rc.6.json +9 -0
  143. package/dist/migrations/manifests/0.3.0.json +11 -0
  144. package/dist/migrations/manifests/0.3.1.json +9 -0
  145. package/dist/migrations/manifests/0.3.10.json +9 -0
  146. package/dist/migrations/manifests/0.3.2.json +9 -0
  147. package/dist/migrations/manifests/0.3.3.json +9 -0
  148. package/dist/migrations/manifests/0.3.4.json +21 -0
  149. package/dist/migrations/manifests/0.3.5.json +9 -0
  150. package/dist/migrations/manifests/0.3.6.json +9 -0
  151. package/dist/migrations/manifests/0.3.7.json +9 -0
  152. package/dist/migrations/manifests/0.3.8.json +9 -0
  153. package/dist/migrations/manifests/0.3.9.json +9 -0
  154. package/dist/migrations/manifests/0.4.0-beta.1.json +228 -0
  155. package/dist/migrations/manifests/0.4.0-beta.10.json +9 -0
  156. package/dist/migrations/manifests/0.4.0-beta.2.json +9 -0
  157. package/dist/migrations/manifests/0.4.0-beta.3.json +9 -0
  158. package/dist/migrations/manifests/0.4.0-beta.4.json +9 -0
  159. package/dist/migrations/manifests/0.4.0-beta.5.json +9 -0
  160. package/dist/migrations/manifests/0.4.0-beta.6.json +9 -0
  161. package/dist/migrations/manifests/0.4.0-beta.7.json +9 -0
  162. package/dist/migrations/manifests/0.4.0-beta.8.json +34 -0
  163. package/dist/migrations/manifests/0.4.0-beta.9.json +9 -0
  164. package/dist/migrations/manifests/0.4.0-rc.0.json +9 -0
  165. package/dist/migrations/manifests/0.4.0-rc.1.json +9 -0
  166. package/dist/migrations/manifests/0.4.0.json +9 -0
  167. package/dist/migrations/manifests/0.5.0-beta.0.json +1646 -0
  168. package/dist/migrations/manifests/0.5.0-beta.1.json +9 -0
  169. package/dist/migrations/manifests/0.5.0-beta.10.json +9 -0
  170. package/dist/migrations/manifests/0.5.0-beta.11.json +9 -0
  171. package/dist/migrations/manifests/0.5.0-beta.12.json +9 -0
  172. package/dist/migrations/manifests/0.5.0-beta.13.json +9 -0
  173. package/dist/migrations/manifests/0.5.0-beta.14.json +9 -0
  174. package/dist/migrations/manifests/0.5.0-beta.15.json +116 -0
  175. package/dist/migrations/manifests/0.5.0-beta.16.json +9 -0
  176. package/dist/migrations/manifests/0.5.0-beta.17.json +9 -0
  177. package/dist/migrations/manifests/0.5.0-beta.18.json +9 -0
  178. package/dist/migrations/manifests/0.5.0-beta.19.json +9 -0
  179. package/dist/migrations/manifests/0.5.0-beta.2.json +9 -0
  180. package/dist/migrations/manifests/0.5.0-beta.3.json +9 -0
  181. package/dist/migrations/manifests/0.5.0-beta.4.json +9 -0
  182. package/dist/migrations/manifests/0.5.0-beta.5.json +222 -0
  183. package/dist/migrations/manifests/0.5.0-beta.6.json +9 -0
  184. package/dist/migrations/manifests/0.5.0-beta.7.json +9 -0
  185. package/dist/migrations/manifests/0.5.0-beta.8.json +9 -0
  186. package/dist/migrations/manifests/0.5.0-beta.9.json +48 -0
  187. package/dist/migrations/manifests/0.5.0-rc.0.json +9 -0
  188. package/dist/migrations/manifests/0.5.0-rc.1.json +9 -0
  189. package/dist/migrations/manifests/0.5.0-rc.2.json +9 -0
  190. package/dist/migrations/manifests/0.5.0-rc.3.json +9 -0
  191. package/dist/migrations/manifests/0.5.0-rc.4.json +9 -0
  192. package/dist/migrations/manifests/0.5.0-rc.5.json +9 -0
  193. package/dist/migrations/manifests/0.5.0-rc.6.json +9 -0
  194. package/dist/migrations/manifests/0.5.0-rc.7.json +9 -0
  195. package/dist/migrations/manifests/0.5.0.json +9 -0
  196. package/dist/migrations/manifests/0.5.1.json +9 -0
  197. package/dist/migrations/manifests/0.5.10.json +9 -0
  198. package/dist/migrations/manifests/0.5.11.json +16 -0
  199. package/dist/migrations/manifests/0.5.12.json +9 -0
  200. package/dist/migrations/manifests/0.5.13.json +9 -0
  201. package/dist/migrations/manifests/0.5.14.json +9 -0
  202. package/dist/migrations/manifests/0.5.15.json +9 -0
  203. package/dist/migrations/manifests/0.5.16.json +9 -0
  204. package/dist/migrations/manifests/0.5.17.json +9 -0
  205. package/dist/migrations/manifests/0.5.18.json +9 -0
  206. package/dist/migrations/manifests/0.5.19.json +9 -0
  207. package/dist/migrations/manifests/0.5.2.json +9 -0
  208. package/dist/migrations/manifests/0.5.3.json +9 -0
  209. package/dist/migrations/manifests/0.5.4.json +9 -0
  210. package/dist/migrations/manifests/0.5.5.json +9 -0
  211. package/dist/migrations/manifests/0.5.6.json +9 -0
  212. package/dist/migrations/manifests/0.5.7.json +16 -0
  213. package/dist/migrations/manifests/0.5.8.json +9 -0
  214. package/dist/migrations/manifests/0.5.9.json +9 -0
  215. package/dist/migrations/manifests/0.6.0-beta.0.json +16 -0
  216. package/dist/migrations/manifests/0.6.0-beta.1.json +9 -0
  217. package/dist/migrations/manifests/0.6.0-beta.10.json +9 -0
  218. package/dist/migrations/manifests/0.6.0-beta.11.json +9 -0
  219. package/dist/migrations/manifests/0.6.0-beta.12.json +9 -0
  220. package/dist/migrations/manifests/0.6.0-beta.13.json +9 -0
  221. package/dist/migrations/manifests/0.6.0-beta.14.json +9 -0
  222. package/dist/migrations/manifests/0.6.0-beta.15.json +9 -0
  223. package/dist/migrations/manifests/0.6.0-beta.16.json +9 -0
  224. package/dist/migrations/manifests/0.6.0-beta.17.json +9 -0
  225. package/dist/migrations/manifests/0.6.0-beta.18.json +16 -0
  226. package/dist/migrations/manifests/0.6.0-beta.19.json +9 -0
  227. package/dist/migrations/manifests/0.6.0-beta.2.json +9 -0
  228. package/dist/migrations/manifests/0.6.0-beta.20.json +9 -0
  229. package/dist/migrations/manifests/0.6.0-beta.21.json +9 -0
  230. package/dist/migrations/manifests/0.6.0-beta.3.json +9 -0
  231. package/dist/migrations/manifests/0.6.0-beta.4.json +9 -0
  232. package/dist/migrations/manifests/0.6.0-beta.5.json +9 -0
  233. package/dist/migrations/manifests/0.6.0-beta.6.json +16 -0
  234. package/dist/migrations/manifests/0.6.0-beta.7.json +9 -0
  235. package/dist/migrations/manifests/0.6.0-beta.8.json +9 -0
  236. package/dist/migrations/manifests/0.6.0-beta.9.json +9 -0
  237. package/dist/templates/claude/agents/trellis-check.md +109 -0
  238. package/dist/templates/claude/agents/trellis-implement.md +109 -0
  239. package/dist/templates/claude/agents/trellis-research.md +137 -0
  240. package/dist/templates/claude/index.d.ts +22 -0
  241. package/dist/templates/claude/index.d.ts.map +1 -0
  242. package/dist/templates/claude/index.js +46 -0
  243. package/dist/templates/claude/index.js.map +1 -0
  244. package/dist/templates/claude/settings.json +73 -0
  245. package/dist/templates/codebuddy/agents/trellis-check.md +109 -0
  246. package/dist/templates/codebuddy/agents/trellis-implement.md +109 -0
  247. package/dist/templates/codebuddy/agents/trellis-research.md +137 -0
  248. package/dist/templates/codebuddy/index.d.ts +15 -0
  249. package/dist/templates/codebuddy/index.d.ts.map +1 -0
  250. package/dist/templates/codebuddy/index.js +15 -0
  251. package/dist/templates/codebuddy/index.js.map +1 -0
  252. package/dist/templates/codebuddy/settings.json +59 -0
  253. package/dist/templates/codex/agents/trellis-check.toml +84 -0
  254. package/dist/templates/codex/agents/trellis-implement.toml +65 -0
  255. package/dist/templates/codex/agents/trellis-research.toml +73 -0
  256. package/dist/templates/codex/config.toml +28 -0
  257. package/dist/templates/codex/hooks/session-start.py +481 -0
  258. package/dist/templates/codex/hooks.json +15 -0
  259. package/dist/templates/codex/index.d.ts +39 -0
  260. package/dist/templates/codex/index.d.ts.map +1 -0
  261. package/dist/templates/codex/index.js +85 -0
  262. package/dist/templates/codex/index.js.map +1 -0
  263. package/dist/templates/codex/skills/before-dev/SKILL.md +34 -0
  264. package/dist/templates/codex/skills/brainstorm/SKILL.md +500 -0
  265. package/dist/templates/codex/skills/break-loop/SKILL.md +130 -0
  266. package/dist/templates/codex/skills/check/SKILL.md +30 -0
  267. package/dist/templates/codex/skills/check-cross-layer/SKILL.md +158 -0
  268. package/dist/templates/codex/skills/create-command/SKILL.md +101 -0
  269. package/dist/templates/codex/skills/finish-work/SKILL.md +90 -0
  270. package/dist/templates/codex/skills/improve-ut/SKILL.md +69 -0
  271. package/dist/templates/codex/skills/integrate-skill/SKILL.md +221 -0
  272. package/dist/templates/codex/skills/onboard/SKILL.md +363 -0
  273. package/dist/templates/codex/skills/record-session/SKILL.md +67 -0
  274. package/dist/templates/codex/skills/start/SKILL.md +354 -0
  275. package/dist/templates/codex/skills/update-spec/SKILL.md +335 -0
  276. package/dist/templates/common/bundled-skills/trellis-meta/SKILL.md +73 -0
  277. package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/add-project-local-conventions.md +83 -0
  278. package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-agents.md +54 -0
  279. package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-context-loading.md +81 -0
  280. package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-hooks.md +57 -0
  281. package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-skills-or-commands.md +78 -0
  282. package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-spec-structure.md +83 -0
  283. package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-task-lifecycle.md +90 -0
  284. package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-workflow.md +64 -0
  285. package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/overview.md +55 -0
  286. package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/context-injection.md +68 -0
  287. package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/generated-files.md +80 -0
  288. package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/overview.md +51 -0
  289. package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/spec-system.md +102 -0
  290. package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/task-system.md +101 -0
  291. package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/workflow.md +75 -0
  292. package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/workspace-memory.md +71 -0
  293. package/dist/templates/common/bundled-skills/trellis-meta/references/platform-files/agents.md +79 -0
  294. package/dist/templates/common/bundled-skills/trellis-meta/references/platform-files/hooks-and-settings.md +69 -0
  295. package/dist/templates/common/bundled-skills/trellis-meta/references/platform-files/overview.md +59 -0
  296. package/dist/templates/common/bundled-skills/trellis-meta/references/platform-files/platform-map.md +74 -0
  297. package/dist/templates/common/bundled-skills/trellis-meta/references/platform-files/skills-and-commands.md +83 -0
  298. package/dist/templates/common/bundled-skills/trellis-spec-bootstarp/SKILL.md +41 -0
  299. package/dist/templates/common/bundled-skills/trellis-spec-bootstarp/references/mcp-setup.md +90 -0
  300. package/dist/templates/common/bundled-skills/trellis-spec-bootstarp/references/repository-analysis.md +59 -0
  301. package/dist/templates/common/bundled-skills/trellis-spec-bootstarp/references/spec-task-planning.md +61 -0
  302. package/dist/templates/common/bundled-skills/trellis-spec-bootstarp/references/spec-writing.md +70 -0
  303. package/dist/templates/common/commands/continue.md +55 -0
  304. package/dist/templates/common/commands/finish-work.md +66 -0
  305. package/dist/templates/common/commands/start.md +58 -0
  306. package/dist/templates/common/index.d.ts +48 -0
  307. package/dist/templates/common/index.d.ts.map +1 -0
  308. package/dist/templates/common/index.js +104 -0
  309. package/dist/templates/common/index.js.map +1 -0
  310. package/dist/templates/common/skills/before-dev.md +29 -0
  311. package/dist/templates/common/skills/brainstorm.md +543 -0
  312. package/dist/templates/common/skills/break-loop.md +125 -0
  313. package/dist/templates/common/skills/check.md +87 -0
  314. package/dist/templates/common/skills/update-spec.md +351 -0
  315. package/dist/templates/copilot/hooks/session-start.py +454 -0
  316. package/dist/templates/copilot/hooks.json +19 -0
  317. package/dist/templates/copilot/index.d.ts +23 -0
  318. package/dist/templates/copilot/index.d.ts.map +1 -0
  319. package/dist/templates/copilot/index.js +54 -0
  320. package/dist/templates/copilot/index.js.map +1 -0
  321. package/dist/templates/copilot/prompts/before-dev.prompt.md +33 -0
  322. package/dist/templates/copilot/prompts/brainstorm.prompt.md +499 -0
  323. package/dist/templates/copilot/prompts/break-loop.prompt.md +129 -0
  324. package/dist/templates/copilot/prompts/check-cross-layer.prompt.md +157 -0
  325. package/dist/templates/copilot/prompts/check.prompt.md +29 -0
  326. package/dist/templates/copilot/prompts/create-command.prompt.md +116 -0
  327. package/dist/templates/copilot/prompts/finish-work.prompt.md +99 -0
  328. package/dist/templates/copilot/prompts/integrate-skill.prompt.md +223 -0
  329. package/dist/templates/copilot/prompts/onboard.prompt.md +362 -0
  330. package/dist/templates/copilot/prompts/parallel.prompt.md +196 -0
  331. package/dist/templates/copilot/prompts/record-session.prompt.md +66 -0
  332. package/dist/templates/copilot/prompts/start.prompt.md +397 -0
  333. package/dist/templates/copilot/prompts/update-spec.prompt.md +358 -0
  334. package/dist/templates/cursor/agents/trellis-check.md +108 -0
  335. package/dist/templates/cursor/agents/trellis-implement.md +108 -0
  336. package/dist/templates/cursor/agents/trellis-research.md +136 -0
  337. package/dist/templates/cursor/hooks.json +24 -0
  338. package/dist/templates/cursor/index.d.ts +13 -0
  339. package/dist/templates/cursor/index.d.ts.map +1 -0
  340. package/dist/templates/cursor/index.js +13 -0
  341. package/dist/templates/cursor/index.js.map +1 -0
  342. package/dist/templates/droid/droids/trellis-check.md +101 -0
  343. package/dist/templates/droid/droids/trellis-implement.md +101 -0
  344. package/dist/templates/droid/droids/trellis-research.md +137 -0
  345. package/dist/templates/droid/index.d.ts +15 -0
  346. package/dist/templates/droid/index.d.ts.map +1 -0
  347. package/dist/templates/droid/index.js +15 -0
  348. package/dist/templates/droid/index.js.map +1 -0
  349. package/dist/templates/droid/settings.json +59 -0
  350. package/dist/templates/extract.d.ts +40 -0
  351. package/dist/templates/extract.d.ts.map +1 -0
  352. package/dist/templates/extract.js +106 -0
  353. package/dist/templates/extract.js.map +1 -0
  354. package/dist/templates/gemini/agents/trellis-check.md +101 -0
  355. package/dist/templates/gemini/agents/trellis-implement.md +101 -0
  356. package/dist/templates/gemini/agents/trellis-research.md +136 -0
  357. package/dist/templates/gemini/index.d.ts +13 -0
  358. package/dist/templates/gemini/index.d.ts.map +1 -0
  359. package/dist/templates/gemini/index.js +13 -0
  360. package/dist/templates/gemini/index.js.map +1 -0
  361. package/dist/templates/gemini/settings.json +28 -0
  362. package/dist/templates/kiro/agents/trellis-check.json +26 -0
  363. package/dist/templates/kiro/agents/trellis-implement.json +26 -0
  364. package/dist/templates/kiro/agents/trellis-research.json +30 -0
  365. package/dist/templates/kiro/index.d.ts +18 -0
  366. package/dist/templates/kiro/index.d.ts.map +1 -0
  367. package/dist/templates/kiro/index.js +18 -0
  368. package/dist/templates/kiro/index.js.map +1 -0
  369. package/dist/templates/markdown/agents.md +21 -0
  370. package/dist/templates/markdown/gitignore.txt +15 -0
  371. package/dist/templates/markdown/index.d.ts +27 -0
  372. package/dist/templates/markdown/index.d.ts.map +1 -0
  373. package/dist/templates/markdown/index.js +52 -0
  374. package/dist/templates/markdown/index.js.map +1 -0
  375. package/dist/templates/markdown/spec/backend/database-guidelines.md.txt +51 -0
  376. package/dist/templates/markdown/spec/backend/directory-structure.md.txt +54 -0
  377. package/dist/templates/markdown/spec/backend/error-handling.md.txt +51 -0
  378. package/dist/templates/markdown/spec/backend/index.md.txt +38 -0
  379. package/dist/templates/markdown/spec/backend/logging-guidelines.md.txt +51 -0
  380. package/dist/templates/markdown/spec/backend/quality-guidelines.md.txt +51 -0
  381. package/dist/templates/markdown/spec/frontend/component-guidelines.md.txt +59 -0
  382. package/dist/templates/markdown/spec/frontend/directory-structure.md.txt +54 -0
  383. package/dist/templates/markdown/spec/frontend/hook-guidelines.md.txt +51 -0
  384. package/dist/templates/markdown/spec/frontend/index.md.txt +39 -0
  385. package/dist/templates/markdown/spec/frontend/quality-guidelines.md.txt +51 -0
  386. package/dist/templates/markdown/spec/frontend/state-management.md.txt +51 -0
  387. package/dist/templates/markdown/spec/frontend/type-safety.md.txt +51 -0
  388. package/dist/templates/markdown/spec/guides/code-reuse-thinking-guide.md.txt +105 -0
  389. package/dist/templates/markdown/spec/guides/cross-layer-thinking-guide.md.txt +162 -0
  390. package/dist/templates/markdown/spec/guides/cross-platform-thinking-guide.md.txt +595 -0
  391. package/dist/templates/markdown/spec/guides/index.md.txt +79 -0
  392. package/dist/templates/markdown/workspace-index.md +125 -0
  393. package/dist/templates/markdown/worktree.yaml.txt +58 -0
  394. package/dist/templates/opencode/agents/trellis-check.md +116 -0
  395. package/dist/templates/opencode/agents/trellis-implement.md +117 -0
  396. package/dist/templates/opencode/agents/trellis-research.md +145 -0
  397. package/dist/templates/opencode/lib/session-utils.js +432 -0
  398. package/dist/templates/opencode/lib/trellis-context.js +381 -0
  399. package/dist/templates/opencode/package.json +5 -0
  400. package/dist/templates/opencode/plugins/inject-subagent-context.js +497 -0
  401. package/dist/templates/opencode/plugins/inject-workflow-state.js +162 -0
  402. package/dist/templates/opencode/plugins/session-start.js +101 -0
  403. package/dist/templates/pi/agents/trellis-check.md +36 -0
  404. package/dist/templates/pi/agents/trellis-implement.md +41 -0
  405. package/dist/templates/pi/agents/trellis-research.md +25 -0
  406. package/dist/templates/pi/extensions/trellis/index.ts.txt +1172 -0
  407. package/dist/templates/pi/index.d.ts +5 -0
  408. package/dist/templates/pi/index.d.ts.map +1 -0
  409. package/dist/templates/pi/index.js +12 -0
  410. package/dist/templates/pi/index.js.map +1 -0
  411. package/dist/templates/pi/settings.json +21 -0
  412. package/dist/templates/qoder/agents/trellis-check.md +102 -0
  413. package/dist/templates/qoder/agents/trellis-implement.md +102 -0
  414. package/dist/templates/qoder/agents/trellis-research.md +137 -0
  415. package/dist/templates/qoder/index.d.ts +15 -0
  416. package/dist/templates/qoder/index.d.ts.map +1 -0
  417. package/dist/templates/qoder/index.js +15 -0
  418. package/dist/templates/qoder/index.js.map +1 -0
  419. package/dist/templates/qoder/settings.json +47 -0
  420. package/dist/templates/shared-hooks/index.d.ts +50 -0
  421. package/dist/templates/shared-hooks/index.d.ts.map +1 -0
  422. package/dist/templates/shared-hooks/index.js +88 -0
  423. package/dist/templates/shared-hooks/index.js.map +1 -0
  424. package/dist/templates/shared-hooks/inject-shell-session-context.py +183 -0
  425. package/dist/templates/shared-hooks/inject-subagent-context.py +749 -0
  426. package/dist/templates/shared-hooks/inject-workflow-state.py +387 -0
  427. package/dist/templates/shared-hooks/session-start.py +797 -0
  428. package/dist/templates/template-utils.d.ts +26 -0
  429. package/dist/templates/template-utils.d.ts.map +1 -0
  430. package/dist/templates/template-utils.js +60 -0
  431. package/dist/templates/template-utils.js.map +1 -0
  432. package/dist/templates/trellis/config.yaml +90 -0
  433. package/dist/templates/trellis/gitignore.txt +32 -0
  434. package/dist/templates/trellis/index.d.ts +52 -0
  435. package/dist/templates/trellis/index.d.ts.map +1 -0
  436. package/dist/templates/trellis/index.js +97 -0
  437. package/dist/templates/trellis/index.js.map +1 -0
  438. package/dist/templates/trellis/scripts/__init__.py +5 -0
  439. package/dist/templates/trellis/scripts/add_session.py +547 -0
  440. package/dist/templates/trellis/scripts/common/__init__.py +92 -0
  441. package/dist/templates/trellis/scripts/common/active_task.py +626 -0
  442. package/dist/templates/trellis/scripts/common/cli_adapter.py +811 -0
  443. package/dist/templates/trellis/scripts/common/config.py +445 -0
  444. package/dist/templates/trellis/scripts/common/developer.py +190 -0
  445. package/dist/templates/trellis/scripts/common/git.py +31 -0
  446. package/dist/templates/trellis/scripts/common/git_context.py +106 -0
  447. package/dist/templates/trellis/scripts/common/io.py +37 -0
  448. package/dist/templates/trellis/scripts/common/log.py +45 -0
  449. package/dist/templates/trellis/scripts/common/packages_context.py +238 -0
  450. package/dist/templates/trellis/scripts/common/paths.py +447 -0
  451. package/dist/templates/trellis/scripts/common/safe_commit.py +285 -0
  452. package/dist/templates/trellis/scripts/common/session_context.py +821 -0
  453. package/dist/templates/trellis/scripts/common/task_context.py +223 -0
  454. package/dist/templates/trellis/scripts/common/task_queue.py +188 -0
  455. package/dist/templates/trellis/scripts/common/task_store.py +697 -0
  456. package/dist/templates/trellis/scripts/common/task_utils.py +274 -0
  457. package/dist/templates/trellis/scripts/common/tasks.py +112 -0
  458. package/dist/templates/trellis/scripts/common/trellis_config.py +131 -0
  459. package/dist/templates/trellis/scripts/common/types.py +110 -0
  460. package/dist/templates/trellis/scripts/common/workflow_phase.py +215 -0
  461. package/dist/templates/trellis/scripts/get_context.py +16 -0
  462. package/dist/templates/trellis/scripts/get_developer.py +26 -0
  463. package/dist/templates/trellis/scripts/hooks/linear_sync.py +243 -0
  464. package/dist/templates/trellis/scripts/init_developer.py +51 -0
  465. package/dist/templates/trellis/scripts/task.py +500 -0
  466. package/dist/templates/trellis/tasks/.gitkeep +0 -0
  467. package/dist/templates/trellis/workflow.md +690 -0
  468. package/dist/types/ai-tools.d.ts +95 -0
  469. package/dist/types/ai-tools.d.ts.map +1 -0
  470. package/dist/types/ai-tools.js +280 -0
  471. package/dist/types/ai-tools.js.map +1 -0
  472. package/dist/types/migration.d.ts +125 -0
  473. package/dist/types/migration.d.ts.map +1 -0
  474. package/dist/types/migration.js +8 -0
  475. package/dist/types/migration.js.map +1 -0
  476. package/dist/utils/compare-versions.d.ts +12 -0
  477. package/dist/utils/compare-versions.d.ts.map +1 -0
  478. package/dist/utils/compare-versions.js +86 -0
  479. package/dist/utils/compare-versions.js.map +1 -0
  480. package/dist/utils/cwd-guard.d.ts +38 -0
  481. package/dist/utils/cwd-guard.d.ts.map +1 -0
  482. package/dist/utils/cwd-guard.js +62 -0
  483. package/dist/utils/cwd-guard.js.map +1 -0
  484. package/dist/utils/file-writer.d.ts +36 -0
  485. package/dist/utils/file-writer.d.ts.map +1 -0
  486. package/dist/utils/file-writer.js +203 -0
  487. package/dist/utils/file-writer.js.map +1 -0
  488. package/dist/utils/manifest-prune.d.ts +61 -0
  489. package/dist/utils/manifest-prune.d.ts.map +1 -0
  490. package/dist/utils/manifest-prune.js +136 -0
  491. package/dist/utils/manifest-prune.js.map +1 -0
  492. package/dist/utils/posix.d.ts +13 -0
  493. package/dist/utils/posix.d.ts.map +1 -0
  494. package/dist/utils/posix.js +15 -0
  495. package/dist/utils/posix.js.map +1 -0
  496. package/dist/utils/project-detector.d.ts +46 -0
  497. package/dist/utils/project-detector.d.ts.map +1 -0
  498. package/dist/utils/project-detector.js +666 -0
  499. package/dist/utils/project-detector.js.map +1 -0
  500. package/dist/utils/proxy.d.ts +25 -0
  501. package/dist/utils/proxy.d.ts.map +1 -0
  502. package/dist/utils/proxy.js +60 -0
  503. package/dist/utils/proxy.js.map +1 -0
  504. package/dist/utils/task-json.d.ts +46 -0
  505. package/dist/utils/task-json.d.ts.map +1 -0
  506. package/dist/utils/task-json.js +49 -0
  507. package/dist/utils/task-json.js.map +1 -0
  508. package/dist/utils/template-fetcher.d.ts +150 -0
  509. package/dist/utils/template-fetcher.d.ts.map +1 -0
  510. package/dist/utils/template-fetcher.js +907 -0
  511. package/dist/utils/template-fetcher.js.map +1 -0
  512. package/dist/utils/template-hash.d.ts +123 -0
  513. package/dist/utils/template-hash.d.ts.map +1 -0
  514. package/dist/utils/template-hash.js +334 -0
  515. package/dist/utils/template-hash.js.map +1 -0
  516. package/dist/utils/uninstall-scrubbers.d.ts +66 -0
  517. package/dist/utils/uninstall-scrubbers.d.ts.map +1 -0
  518. package/dist/utils/uninstall-scrubbers.js +342 -0
  519. package/dist/utils/uninstall-scrubbers.js.map +1 -0
  520. package/package.json +89 -0
@@ -0,0 +1,821 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Session context generation (default + record modes).
4
+
5
+ Provides:
6
+ get_context_json - JSON output for default mode
7
+ get_context_text - Text output for default mode
8
+ get_context_record_json - JSON for record mode
9
+ get_context_text_record - Text for record mode
10
+ output_json - Print JSON
11
+ output_text - Print text
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ import json
17
+ import os
18
+ import re
19
+ import subprocess
20
+ from pathlib import Path
21
+
22
+ from .active_task import resolve_context_key
23
+ from .config import get_git_packages
24
+ from .git import run_git
25
+ from .packages_context import get_packages_section
26
+ from .tasks import iter_active_tasks, load_task, get_all_statuses, children_progress
27
+ from .paths import (
28
+ DIR_SCRIPTS,
29
+ DIR_SPEC,
30
+ DIR_TASKS,
31
+ DIR_WORKFLOW,
32
+ DIR_WORKSPACE,
33
+ count_lines,
34
+ get_active_journal_file,
35
+ get_current_task,
36
+ get_current_task_source,
37
+ get_developer,
38
+ get_repo_root,
39
+ get_tasks_dir,
40
+ )
41
+
42
+
43
+ # =============================================================================
44
+ # Helpers
45
+ # =============================================================================
46
+
47
+ _PACKAGE_NAME = "@mindfoldhq/trellis"
48
+ _UPDATE_CHECK_TIMEOUT_SECONDS = 1.0
49
+ _VERSION_RE = re.compile(
50
+ r"^\s*(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:-([0-9A-Za-z.-]+))?\s*$"
51
+ )
52
+ _VERSION_TOKEN_RE = re.compile(r"\b\d+(?:\.\d+){1,2}(?:-[0-9A-Za-z.-]+)?\b")
53
+ _POLYREPO_IGNORED_DIRS = {
54
+ "node_modules",
55
+ "target",
56
+ "dist",
57
+ "build",
58
+ "out",
59
+ "bin",
60
+ "obj",
61
+ "vendor",
62
+ "coverage",
63
+ "tmp",
64
+ "__pycache__",
65
+ }
66
+ _POLYREPO_SCAN_MAX_DEPTH = 2
67
+
68
+
69
+ def _is_git_worktree(path: Path) -> bool:
70
+ """Return True when path is inside a Git worktree."""
71
+ rc, out, _ = run_git(["rev-parse", "--is-inside-work-tree"], cwd=path)
72
+ return rc == 0 and out.strip().lower() == "true"
73
+
74
+
75
+ def _parse_recent_commits(log_output: str) -> list[dict]:
76
+ """Parse `git log --oneline` output into structured commit entries."""
77
+ commits = []
78
+ for line in log_output.splitlines():
79
+ if not line.strip():
80
+ continue
81
+ parts = line.split(" ", 1)
82
+ if len(parts) >= 2:
83
+ commits.append({"hash": parts[0], "message": parts[1]})
84
+ elif len(parts) == 1:
85
+ commits.append({"hash": parts[0], "message": ""})
86
+ return commits
87
+
88
+
89
+ def _collect_git_repo_info(name: str, rel_path: str, repo_dir: Path) -> dict | None:
90
+ """Collect Git status for one known repository directory."""
91
+ if not (repo_dir / ".git").exists():
92
+ return None
93
+
94
+ _, branch_out, _ = run_git(["branch", "--show-current"], cwd=repo_dir)
95
+ branch = branch_out.strip() or "unknown"
96
+
97
+ _, status_out, _ = run_git(["status", "--porcelain"], cwd=repo_dir)
98
+ changes = len([l for l in status_out.splitlines() if l.strip()])
99
+
100
+ _, log_out, _ = run_git(["log", "--oneline", "-5"], cwd=repo_dir)
101
+
102
+ return {
103
+ "name": name,
104
+ "path": rel_path,
105
+ "branch": branch,
106
+ "isClean": changes == 0,
107
+ "uncommittedChanges": changes,
108
+ "recentCommits": _parse_recent_commits(log_out),
109
+ }
110
+
111
+
112
+ def _collect_root_git_info(repo_root: Path) -> dict:
113
+ """Collect root Git info without pretending a non-Git root is clean."""
114
+ if not _is_git_worktree(repo_root):
115
+ return {
116
+ "isRepo": False,
117
+ "branch": "",
118
+ "isClean": False,
119
+ "uncommittedChanges": 0,
120
+ "recentCommits": [],
121
+ }
122
+
123
+ _, branch_out, _ = run_git(["branch", "--show-current"], cwd=repo_root)
124
+ branch = branch_out.strip() or "unknown"
125
+
126
+ _, status_out, _ = run_git(["status", "--porcelain"], cwd=repo_root)
127
+ status_lines = [line for line in status_out.splitlines() if line.strip()]
128
+
129
+ _, short_out, _ = run_git(["status", "--short"], cwd=repo_root)
130
+
131
+ _, log_out, _ = run_git(["log", "--oneline", "-5"], cwd=repo_root)
132
+
133
+ return {
134
+ "isRepo": True,
135
+ "branch": branch,
136
+ "isClean": len(status_lines) == 0,
137
+ "uncommittedChanges": len(status_lines),
138
+ "statusShort": short_out.splitlines(),
139
+ "recentCommits": _parse_recent_commits(log_out),
140
+ }
141
+
142
+
143
+ def _discover_child_git_repos(repo_root: Path) -> list[tuple[str, str]]:
144
+ """Discover child Git repositories using the init-time polyrepo heuristic."""
145
+ found: list[str] = []
146
+
147
+ def is_candidate_dir(path: Path) -> bool:
148
+ name = path.name
149
+ return not name.startswith(".") and name not in _POLYREPO_IGNORED_DIRS
150
+
151
+ def scan(rel_dir: Path, depth: int) -> None:
152
+ if depth >= _POLYREPO_SCAN_MAX_DEPTH:
153
+ return
154
+ abs_dir = repo_root / rel_dir
155
+ try:
156
+ children = sorted(abs_dir.iterdir(), key=lambda p: p.name)
157
+ except OSError:
158
+ return
159
+
160
+ for child in children:
161
+ if not child.is_dir() or not is_candidate_dir(child):
162
+ continue
163
+
164
+ child_rel = (
165
+ rel_dir / child.name if rel_dir != Path(".") else Path(child.name)
166
+ )
167
+ if (child / ".git").exists():
168
+ found.append(child_rel.as_posix())
169
+ continue
170
+ scan(child_rel, depth + 1)
171
+
172
+ scan(Path("."), 0)
173
+ if len(found) < 2:
174
+ return []
175
+ return [(path.replace("/", "_"), path) for path in sorted(found)]
176
+
177
+
178
+ def _collect_package_git_info(
179
+ repo_root: Path,
180
+ discover_unconfigured: bool = False,
181
+ ) -> list[dict]:
182
+ """Collect Git status for independent package repositories.
183
+
184
+ Packages marked with ``git: true`` in config.yaml are authoritative.
185
+ When the Trellis root is not a Git repo and no configured package repos are
186
+ available, optionally fall back to the bounded polyrepo child scan.
187
+
188
+ Returns:
189
+ List of dicts with keys: name, path, branch, isClean,
190
+ uncommittedChanges, recentCommits.
191
+ Empty list if no git-repo packages are configured.
192
+ """
193
+ git_pkgs = get_git_packages(repo_root)
194
+ result = []
195
+ for pkg_name, pkg_path in git_pkgs.items():
196
+ pkg_dir = repo_root / pkg_path
197
+ info = _collect_git_repo_info(pkg_name, pkg_path, pkg_dir)
198
+ if info is not None:
199
+ result.append(info)
200
+
201
+ if result or not discover_unconfigured:
202
+ return result
203
+
204
+ discovered = []
205
+ for pkg_name, pkg_path in _discover_child_git_repos(repo_root):
206
+ info = _collect_git_repo_info(pkg_name, pkg_path, repo_root / pkg_path)
207
+ if info is not None:
208
+ discovered.append(info)
209
+ return discovered
210
+
211
+
212
+ def _append_root_git_context(lines: list[str], root_git_info: dict) -> None:
213
+ """Append root Git status without misleading non-Git roots."""
214
+ lines.append("## GIT STATUS")
215
+ if not root_git_info["isRepo"]:
216
+ lines.append("Root is not a Git repository.")
217
+ lines.append("Run Git commands from the package repository paths listed below.")
218
+ else:
219
+ lines.append(f"Branch: {root_git_info['branch']}")
220
+ if root_git_info["isClean"]:
221
+ lines.append("Working directory: Clean")
222
+ else:
223
+ lines.append(
224
+ f"Working directory: {root_git_info['uncommittedChanges']} "
225
+ "uncommitted change(s)"
226
+ )
227
+ lines.append("")
228
+ lines.append("Changes:")
229
+ for line in root_git_info.get("statusShort", [])[:10]:
230
+ lines.append(line)
231
+ lines.append("")
232
+
233
+ lines.append("## RECENT COMMITS")
234
+ if not root_git_info["isRepo"]:
235
+ lines.append(
236
+ "Root has no Git commit history because it is not a Git repository."
237
+ )
238
+ elif root_git_info["recentCommits"]:
239
+ for commit in root_git_info["recentCommits"]:
240
+ lines.append(f"{commit['hash']} {commit['message']}")
241
+ else:
242
+ lines.append("(no commits)")
243
+ lines.append("")
244
+
245
+
246
+ def _append_package_git_context(lines: list[str], package_git_info: list[dict]) -> None:
247
+ """Append Git status and recent commits for package repositories."""
248
+ for pkg in package_git_info:
249
+ lines.append(f"## GIT STATUS ({pkg['name']}: {pkg['path']})")
250
+ lines.append(f"Branch: {pkg['branch']}")
251
+ if pkg["isClean"]:
252
+ lines.append("Working directory: Clean")
253
+ else:
254
+ lines.append(
255
+ f"Working directory: {pkg['uncommittedChanges']} uncommitted change(s)"
256
+ )
257
+ lines.append("")
258
+ lines.append(f"## RECENT COMMITS ({pkg['name']}: {pkg['path']})")
259
+ if pkg["recentCommits"]:
260
+ for commit in pkg["recentCommits"]:
261
+ lines.append(f"{commit['hash']} {commit['message']}")
262
+ else:
263
+ lines.append("(no commits)")
264
+ lines.append("")
265
+
266
+
267
+ def _read_project_version(repo_root: Path) -> str | None:
268
+ try:
269
+ version = (repo_root / DIR_WORKFLOW / ".version").read_text(
270
+ encoding="utf-8"
271
+ ).strip()
272
+ except OSError:
273
+ return None
274
+ return version or None
275
+
276
+
277
+ def _fetch_trellis_version_output() -> str | None:
278
+ try:
279
+ result = subprocess.run(
280
+ ["trellis", "--version"],
281
+ capture_output=True,
282
+ text=True,
283
+ encoding="utf-8",
284
+ errors="replace",
285
+ timeout=_UPDATE_CHECK_TIMEOUT_SECONDS,
286
+ )
287
+ except (OSError, subprocess.SubprocessError, TimeoutError):
288
+ return None
289
+
290
+ if result.returncode != 0:
291
+ return None
292
+ output = f"{result.stdout}\n{result.stderr}".strip()
293
+ return output or None
294
+
295
+
296
+ def _extract_available_update_version(output: str) -> str | None:
297
+ update_match = re.search(
298
+ r"Trellis update available:\s*"
299
+ r"(?P<current>\S+)\s*(?:→|->)\s*(?P<latest>\S+)",
300
+ output,
301
+ )
302
+ if update_match:
303
+ return update_match.group("latest").strip()
304
+ candidates = _VERSION_TOKEN_RE.findall(output)
305
+ return candidates[-1] if candidates else None
306
+
307
+
308
+ def _resolve_available_update_version() -> str | None:
309
+ output = _fetch_trellis_version_output()
310
+ if not output:
311
+ return None
312
+ return _extract_available_update_version(output)
313
+
314
+
315
+ def _parse_version(version: str) -> tuple[tuple[int, int, int], tuple[str, ...] | None] | None:
316
+ match = _VERSION_RE.match(version)
317
+ if not match:
318
+ return None
319
+ major, minor, patch, prerelease = match.groups()
320
+ numbers = (int(major), int(minor or "0"), int(patch or "0"))
321
+ prerelease_parts = tuple(prerelease.split(".")) if prerelease else None
322
+ return numbers, prerelease_parts
323
+
324
+
325
+ def _compare_prerelease(
326
+ left: tuple[str, ...] | None,
327
+ right: tuple[str, ...] | None,
328
+ ) -> int:
329
+ if left is None and right is None:
330
+ return 0
331
+ if left is None:
332
+ return 1
333
+ if right is None:
334
+ return -1
335
+
336
+ for left_part, right_part in zip(left, right):
337
+ if left_part == right_part:
338
+ continue
339
+ left_numeric = left_part.isdigit()
340
+ right_numeric = right_part.isdigit()
341
+ if left_numeric and right_numeric:
342
+ left_int = int(left_part)
343
+ right_int = int(right_part)
344
+ return (left_int > right_int) - (left_int < right_int)
345
+ if left_numeric:
346
+ return -1
347
+ if right_numeric:
348
+ return 1
349
+ return (left_part > right_part) - (left_part < right_part)
350
+
351
+ return (len(left) > len(right)) - (len(left) < len(right))
352
+
353
+
354
+ def _compare_versions(left: str, right: str) -> int | None:
355
+ parsed_left = _parse_version(left)
356
+ parsed_right = _parse_version(right)
357
+ if parsed_left is None or parsed_right is None:
358
+ return None
359
+
360
+ left_numbers, left_prerelease = parsed_left
361
+ right_numbers, right_prerelease = parsed_right
362
+ if left_numbers != right_numbers:
363
+ return (left_numbers > right_numbers) - (left_numbers < right_numbers)
364
+ return _compare_prerelease(left_prerelease, right_prerelease)
365
+
366
+
367
+ def _update_marker_path(repo_root: Path) -> Path:
368
+ context_key = resolve_context_key()
369
+ if not context_key:
370
+ terminal_key = os.environ.get("TERM_SESSION_ID", "").strip()
371
+ context_key = terminal_key or f"ppid-{os.getppid()}"
372
+ safe_key = re.sub(r"[^A-Za-z0-9._-]+", "_", context_key).strip("._-")
373
+ if not safe_key:
374
+ safe_key = "session"
375
+ return (
376
+ repo_root
377
+ / DIR_WORKFLOW
378
+ / ".runtime"
379
+ / f"update-check-{safe_key[:160]}.marker"
380
+ )
381
+
382
+
383
+ def _mark_update_check_attempted(repo_root: Path) -> bool:
384
+ marker_path = _update_marker_path(repo_root)
385
+ if marker_path.exists():
386
+ return False
387
+ try:
388
+ marker_path.parent.mkdir(parents=True, exist_ok=True)
389
+ marker_path.write_text("checked\n", encoding="utf-8")
390
+ except OSError:
391
+ pass
392
+ return True
393
+
394
+
395
+ def _get_update_hint(repo_root: Path) -> str | None:
396
+ marker_path = _update_marker_path(repo_root)
397
+ if marker_path.exists():
398
+ return None
399
+
400
+ current_version = _read_project_version(repo_root)
401
+ if not current_version:
402
+ return None
403
+
404
+ latest_version = _resolve_available_update_version()
405
+ if not latest_version:
406
+ return None
407
+
408
+ _mark_update_check_attempted(repo_root)
409
+ comparison = _compare_versions(current_version, latest_version)
410
+ if comparison is None or comparison >= 0:
411
+ return None
412
+
413
+ return (
414
+ f"Trellis update available: {current_version} -> {latest_version}, "
415
+ f"run npm install -g {_PACKAGE_NAME}@latest"
416
+ )
417
+
418
+
419
+ # =============================================================================
420
+ # JSON Output
421
+ # =============================================================================
422
+
423
+ def get_context_json(repo_root: Path | None = None) -> dict:
424
+ """Get context as a dictionary.
425
+
426
+ Args:
427
+ repo_root: Repository root path. Defaults to auto-detected.
428
+
429
+ Returns:
430
+ Context dictionary.
431
+ """
432
+ if repo_root is None:
433
+ repo_root = get_repo_root()
434
+
435
+ developer = get_developer(repo_root)
436
+ tasks_dir = get_tasks_dir(repo_root)
437
+ journal_file = get_active_journal_file(repo_root)
438
+
439
+ journal_lines = 0
440
+ journal_relative = ""
441
+ if journal_file and developer:
442
+ journal_lines = count_lines(journal_file)
443
+ journal_relative = (
444
+ f"{DIR_WORKFLOW}/{DIR_WORKSPACE}/{developer}/{journal_file.name}"
445
+ )
446
+
447
+ root_git_info = _collect_root_git_info(repo_root)
448
+
449
+ # Tasks
450
+ tasks = [
451
+ {
452
+ "dir": t.dir_name,
453
+ "name": t.name,
454
+ "status": t.status,
455
+ "children": list(t.children),
456
+ "parent": t.parent,
457
+ }
458
+ for t in iter_active_tasks(tasks_dir)
459
+ ]
460
+
461
+ # Package git repos (independent sub-repositories)
462
+ pkg_git_info = _collect_package_git_info(
463
+ repo_root,
464
+ discover_unconfigured=not root_git_info["isRepo"],
465
+ )
466
+
467
+ result = {
468
+ "developer": developer or "",
469
+ "git": {
470
+ "isRepo": root_git_info["isRepo"],
471
+ "branch": root_git_info["branch"],
472
+ "isClean": root_git_info["isClean"],
473
+ "uncommittedChanges": root_git_info["uncommittedChanges"],
474
+ "recentCommits": root_git_info["recentCommits"],
475
+ },
476
+ "tasks": {
477
+ "active": tasks,
478
+ "directory": f"{DIR_WORKFLOW}/{DIR_TASKS}",
479
+ },
480
+ "journal": {
481
+ "file": journal_relative,
482
+ "lines": journal_lines,
483
+ "nearLimit": journal_lines > 1800,
484
+ },
485
+ }
486
+
487
+ if pkg_git_info:
488
+ result["packageGit"] = pkg_git_info
489
+
490
+ return result
491
+
492
+
493
+ def output_json(repo_root: Path | None = None) -> None:
494
+ """Output context in JSON format.
495
+
496
+ Args:
497
+ repo_root: Repository root path. Defaults to auto-detected.
498
+ """
499
+ context = get_context_json(repo_root)
500
+ print(json.dumps(context, indent=2, ensure_ascii=False))
501
+
502
+
503
+ # =============================================================================
504
+ # Text Output
505
+ # =============================================================================
506
+
507
+ def get_context_text(repo_root: Path | None = None) -> str:
508
+ """Get context as formatted text.
509
+
510
+ Args:
511
+ repo_root: Repository root path. Defaults to auto-detected.
512
+
513
+ Returns:
514
+ Formatted text output.
515
+ """
516
+ if repo_root is None:
517
+ repo_root = get_repo_root()
518
+
519
+ lines = []
520
+ lines.append("========================================")
521
+ lines.append("SESSION CONTEXT")
522
+ lines.append("========================================")
523
+ lines.append("")
524
+
525
+ developer = get_developer(repo_root)
526
+
527
+ # Developer section
528
+ lines.append("## DEVELOPER")
529
+ if not developer:
530
+ lines.append(
531
+ f"ERROR: Not initialized. Run: python3 ./{DIR_WORKFLOW}/{DIR_SCRIPTS}/init_developer.py <name>"
532
+ )
533
+ return "\n".join(lines)
534
+
535
+ lines.append(f"Name: {developer}")
536
+ lines.append("")
537
+
538
+ root_git_info = _collect_root_git_info(repo_root)
539
+ _append_root_git_context(lines, root_git_info)
540
+
541
+ # Package git repos — independent sub-repositories
542
+ _append_package_git_context(
543
+ lines,
544
+ _collect_package_git_info(
545
+ repo_root,
546
+ discover_unconfigured=not root_git_info["isRepo"],
547
+ ),
548
+ )
549
+
550
+ # Current task
551
+ lines.append("## CURRENT TASK")
552
+ current_task = get_current_task(repo_root)
553
+ if current_task:
554
+ current_task_dir = repo_root / current_task
555
+ source_type, context_key, _ = get_current_task_source(repo_root)
556
+ lines.append(f"Path: {current_task}")
557
+ lines.append(
558
+ f"Source: {source_type}" + (f":{context_key}" if context_key else "")
559
+ )
560
+
561
+ ct = load_task(current_task_dir)
562
+ if ct:
563
+ lines.append(f"Name: {ct.name}")
564
+ lines.append(f"Status: {ct.status}")
565
+ lines.append(f"Created: {ct.raw.get('createdAt', 'unknown')}")
566
+ if ct.description:
567
+ lines.append(f"Description: {ct.description}")
568
+
569
+ # Check for prd.md
570
+ prd_file = current_task_dir / "prd.md"
571
+ if prd_file.is_file():
572
+ lines.append("")
573
+ lines.append("[!] This task has prd.md - read it for task details")
574
+ else:
575
+ lines.append("(none)")
576
+ lines.append("")
577
+
578
+ # Active tasks
579
+ lines.append("## ACTIVE TASKS")
580
+ tasks_dir = get_tasks_dir(repo_root)
581
+ task_count = 0
582
+
583
+ # Collect all task data for hierarchy display
584
+ all_tasks = {t.dir_name: t for t in iter_active_tasks(tasks_dir)}
585
+ all_statuses = {name: t.status for name, t in all_tasks.items()}
586
+
587
+ def _print_task_tree(name: str, indent: int = 0) -> None:
588
+ nonlocal task_count
589
+ t = all_tasks[name]
590
+ progress = children_progress(t.children, all_statuses)
591
+ prefix = " " * indent
592
+ lines.append(f"{prefix}- {name}/ ({t.status}){progress} @{t.assignee or '-'}")
593
+ task_count += 1
594
+ for child in t.children:
595
+ if child in all_tasks:
596
+ _print_task_tree(child, indent + 1)
597
+
598
+ for dir_name in sorted(all_tasks.keys()):
599
+ if not all_tasks[dir_name].parent:
600
+ _print_task_tree(dir_name)
601
+
602
+ if task_count == 0:
603
+ lines.append("(no active tasks)")
604
+ lines.append(f"Total: {task_count} active task(s)")
605
+ lines.append("")
606
+
607
+ # My tasks
608
+ lines.append("## MY TASKS (Assigned to me)")
609
+ my_task_count = 0
610
+
611
+ for t in all_tasks.values():
612
+ if t.assignee == developer and t.status != "done":
613
+ progress = children_progress(t.children, all_statuses)
614
+ lines.append(f"- [{t.priority}] {t.title} ({t.status}){progress}")
615
+ my_task_count += 1
616
+
617
+ if my_task_count == 0:
618
+ lines.append("(no tasks assigned to you)")
619
+ lines.append("")
620
+
621
+ # Journal file
622
+ lines.append("## JOURNAL FILE")
623
+ journal_file = get_active_journal_file(repo_root)
624
+ if journal_file:
625
+ journal_lines = count_lines(journal_file)
626
+ relative = f"{DIR_WORKFLOW}/{DIR_WORKSPACE}/{developer}/{journal_file.name}"
627
+ lines.append(f"Active file: {relative}")
628
+ lines.append(f"Line count: {journal_lines} / 2000")
629
+ if journal_lines > 1800:
630
+ lines.append("[!] WARNING: Approaching 2000 line limit!")
631
+ else:
632
+ lines.append("No journal file found")
633
+ lines.append("")
634
+
635
+ # Packages
636
+ packages_text = get_packages_section(repo_root)
637
+ if packages_text:
638
+ lines.append(packages_text)
639
+ lines.append("")
640
+
641
+ # Paths
642
+ lines.append("## PATHS")
643
+ lines.append(f"Workspace: {DIR_WORKFLOW}/{DIR_WORKSPACE}/{developer}/")
644
+ lines.append(f"Tasks: {DIR_WORKFLOW}/{DIR_TASKS}/")
645
+ lines.append(f"Spec: {DIR_WORKFLOW}/{DIR_SPEC}/")
646
+ lines.append("")
647
+
648
+ lines.append("========================================")
649
+
650
+ return "\n".join(lines)
651
+
652
+
653
+ # =============================================================================
654
+ # Record Mode
655
+ # =============================================================================
656
+
657
+ def get_context_record_json(repo_root: Path | None = None) -> dict:
658
+ """Get record-mode context as a dictionary.
659
+
660
+ Focused on: my active tasks, git status, current task.
661
+ """
662
+ if repo_root is None:
663
+ repo_root = get_repo_root()
664
+
665
+ developer = get_developer(repo_root)
666
+ tasks_dir = get_tasks_dir(repo_root)
667
+
668
+ root_git_info = _collect_root_git_info(repo_root)
669
+
670
+ # My tasks (single pass — collect statuses and filter by assignee)
671
+ all_tasks_list = list(iter_active_tasks(tasks_dir))
672
+ all_statuses = {t.dir_name: t.status for t in all_tasks_list}
673
+
674
+ my_tasks = []
675
+ for t in all_tasks_list:
676
+ if t.assignee == developer:
677
+ done = sum(
678
+ 1 for c in t.children
679
+ if all_statuses.get(c) in ("completed", "done")
680
+ )
681
+ my_tasks.append({
682
+ "dir": t.dir_name,
683
+ "title": t.title,
684
+ "status": t.status,
685
+ "priority": t.priority,
686
+ "children": list(t.children),
687
+ "childrenDone": done,
688
+ "parent": t.parent,
689
+ "meta": t.meta,
690
+ })
691
+
692
+ # Current task
693
+ current_task_info = None
694
+ current_task = get_current_task(repo_root)
695
+ if current_task:
696
+ source_type, context_key, _ = get_current_task_source(repo_root)
697
+ ct = load_task(repo_root / current_task)
698
+ if ct:
699
+ current_task_info = {
700
+ "path": current_task,
701
+ "name": ct.name,
702
+ "status": ct.status,
703
+ "source": source_type,
704
+ "contextKey": context_key,
705
+ }
706
+
707
+ # Package git repos
708
+ pkg_git_info = _collect_package_git_info(
709
+ repo_root,
710
+ discover_unconfigured=not root_git_info["isRepo"],
711
+ )
712
+
713
+ result = {
714
+ "developer": developer or "",
715
+ "git": {
716
+ "isRepo": root_git_info["isRepo"],
717
+ "branch": root_git_info["branch"],
718
+ "isClean": root_git_info["isClean"],
719
+ "uncommittedChanges": root_git_info["uncommittedChanges"],
720
+ "recentCommits": root_git_info["recentCommits"],
721
+ },
722
+ "myTasks": my_tasks,
723
+ "currentTask": current_task_info,
724
+ }
725
+
726
+ if pkg_git_info:
727
+ result["packageGit"] = pkg_git_info
728
+
729
+ return result
730
+
731
+
732
+ def get_context_text_record(repo_root: Path | None = None) -> str:
733
+ """Get context as formatted text for record-session mode.
734
+
735
+ Focused output: MY ACTIVE TASKS first (with [!!!] emphasis),
736
+ then GIT STATUS, RECENT COMMITS, CURRENT TASK.
737
+ """
738
+ if repo_root is None:
739
+ repo_root = get_repo_root()
740
+
741
+ lines: list[str] = []
742
+ lines.append("========================================")
743
+ lines.append("SESSION CONTEXT (RECORD MODE)")
744
+ lines.append("========================================")
745
+ lines.append("")
746
+
747
+ developer = get_developer(repo_root)
748
+ if not developer:
749
+ lines.append(
750
+ f"ERROR: Not initialized. Run: python3 ./{DIR_WORKFLOW}/{DIR_SCRIPTS}/init_developer.py <name>"
751
+ )
752
+ return "\n".join(lines)
753
+
754
+ # MY ACTIVE TASKS — first and prominent
755
+ lines.append(f"## [!!!] MY ACTIVE TASKS (Assigned to {developer})")
756
+ lines.append("[!] Review whether any should be archived before recording this session.")
757
+ lines.append("")
758
+
759
+ tasks_dir = get_tasks_dir(repo_root)
760
+ my_task_count = 0
761
+
762
+ # Single pass — collect all tasks and filter by assignee
763
+ all_statuses = get_all_statuses(tasks_dir)
764
+
765
+ for t in iter_active_tasks(tasks_dir):
766
+ if t.assignee == developer:
767
+ progress = children_progress(t.children, all_statuses)
768
+ lines.append(f"- [{t.priority}] {t.title} ({t.status}){progress} — {t.dir_name}")
769
+ my_task_count += 1
770
+
771
+ if my_task_count == 0:
772
+ lines.append("(no active tasks assigned to you)")
773
+ lines.append("")
774
+
775
+ root_git_info = _collect_root_git_info(repo_root)
776
+ _append_root_git_context(lines, root_git_info)
777
+
778
+ # Package git repos — independent sub-repositories
779
+ _append_package_git_context(
780
+ lines,
781
+ _collect_package_git_info(
782
+ repo_root,
783
+ discover_unconfigured=not root_git_info["isRepo"],
784
+ ),
785
+ )
786
+
787
+ # CURRENT TASK
788
+ lines.append("## CURRENT TASK")
789
+ current_task = get_current_task(repo_root)
790
+ if current_task:
791
+ source_type, context_key, _ = get_current_task_source(repo_root)
792
+ lines.append(f"Path: {current_task}")
793
+ lines.append(
794
+ f"Source: {source_type}" + (f":{context_key}" if context_key else "")
795
+ )
796
+ ct = load_task(repo_root / current_task)
797
+ if ct:
798
+ lines.append(f"Name: {ct.name}")
799
+ lines.append(f"Status: {ct.status}")
800
+ else:
801
+ lines.append("(none)")
802
+ lines.append("")
803
+
804
+ lines.append("========================================")
805
+
806
+ return "\n".join(lines)
807
+
808
+
809
+ def output_text(repo_root: Path | None = None) -> None:
810
+ """Output context in text format.
811
+
812
+ Args:
813
+ repo_root: Repository root path. Defaults to auto-detected.
814
+ """
815
+ if repo_root is None:
816
+ repo_root = get_repo_root()
817
+ update_hint = _get_update_hint(repo_root)
818
+ if update_hint:
819
+ print(update_hint)
820
+ print("")
821
+ print(get_context_text(repo_root))