@blulotus/trellis 0.7.0 → 0.7.2

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 (343) hide show
  1. package/README.md +2 -2
  2. package/dist/cli/index.js +2 -29
  3. package/dist/cli/index.js.map +1 -1
  4. package/dist/commands/init.d.ts +0 -22
  5. package/dist/commands/init.d.ts.map +1 -1
  6. package/dist/commands/init.js +5 -12
  7. package/dist/commands/init.js.map +1 -1
  8. package/dist/commands/mem.d.ts +2 -2
  9. package/dist/commands/mem.d.ts.map +1 -1
  10. package/dist/commands/mem.js +7 -40
  11. package/dist/commands/mem.js.map +1 -1
  12. package/dist/commands/uninstall.d.ts +1 -1
  13. package/dist/commands/uninstall.d.ts.map +1 -1
  14. package/dist/commands/uninstall.js +4 -34
  15. package/dist/commands/uninstall.js.map +1 -1
  16. package/dist/commands/update.d.ts.map +1 -1
  17. package/dist/commands/update.js +2 -28
  18. package/dist/commands/update.js.map +1 -1
  19. package/dist/configurators/index.d.ts.map +1 -1
  20. package/dist/configurators/index.js +7 -73
  21. package/dist/configurators/index.js.map +1 -1
  22. package/dist/configurators/shared.d.ts +18 -70
  23. package/dist/configurators/shared.d.ts.map +1 -1
  24. package/dist/configurators/shared.js +20 -299
  25. package/dist/configurators/shared.js.map +1 -1
  26. package/dist/configurators/workflow.d.ts +0 -14
  27. package/dist/configurators/workflow.d.ts.map +1 -1
  28. package/dist/configurators/workflow.js +1 -37
  29. package/dist/configurators/workflow.js.map +1 -1
  30. package/dist/migrations/manifests/0.1.9.json +0 -12
  31. package/dist/migrations/manifests/0.2.14.json +0 -84
  32. package/dist/migrations/manifests/0.3.0-beta.0.json +0 -87
  33. package/dist/migrations/manifests/0.3.4.json +1 -13
  34. package/dist/migrations/manifests/0.4.0-beta.1.json +24 -176
  35. package/dist/migrations/manifests/0.5.0-beta.0.json +67 -1203
  36. package/dist/migrations/manifests/0.5.0-beta.15.json +0 -96
  37. package/dist/migrations/manifests/0.5.0-beta.5.json +0 -168
  38. package/dist/migrations/manifests/0.5.0-beta.9.json +0 -20
  39. package/dist/migrations/manifests/0.6.0-beta.23.json +0 -66
  40. package/dist/migrations/manifests/0.6.3.json +1 -16
  41. package/dist/migrations/manifests/0.6.6.json +1 -9
  42. package/dist/migrations/manifests/0.7.0.json +9 -0
  43. package/dist/migrations/manifests/0.7.1.json +9 -0
  44. package/dist/migrations/manifests/0.7.2.json +9 -0
  45. package/dist/migrations/manifests/0.8.0.json +9 -0
  46. package/dist/templates/codex/hooks/session-start.py +37 -13
  47. package/dist/templates/codex/hooks.json +5 -0
  48. package/dist/templates/common/bundled-skills/trellis-meta/SKILL.md +5 -5
  49. package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-agents.md +0 -10
  50. package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-hooks.md +1 -1
  51. package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-skills-or-commands.md +1 -13
  52. package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/overview.md +2 -2
  53. package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/bundled-skills.md +9 -28
  54. package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/generated-files.md +5 -5
  55. package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/overview.md +1 -1
  56. package/dist/templates/common/bundled-skills/trellis-meta/references/platform-files/agents.md +1 -17
  57. package/dist/templates/common/bundled-skills/trellis-meta/references/platform-files/hooks-and-settings.md +4 -12
  58. package/dist/templates/common/bundled-skills/trellis-meta/references/platform-files/overview.md +6 -6
  59. package/dist/templates/common/bundled-skills/trellis-meta/references/platform-files/platform-map.md +5 -75
  60. package/dist/templates/common/bundled-skills/trellis-meta/references/platform-files/skills-and-commands.md +0 -16
  61. package/dist/templates/common/bundled-skills/trellis-session-insight/SKILL.md +1 -1
  62. package/dist/templates/common/bundled-skills/trellis-session-insight/references/cli-quick-reference.md +2 -3
  63. package/dist/templates/common/commands/continue.md +24 -4
  64. package/dist/templates/common/commands/finish-work.md +19 -9
  65. package/dist/templates/common/commands/start.md +38 -29
  66. package/dist/templates/extract.d.ts +0 -10
  67. package/dist/templates/extract.d.ts.map +1 -1
  68. package/dist/templates/extract.js +0 -24
  69. package/dist/templates/extract.js.map +1 -1
  70. package/dist/templates/markdown/spec/guides/code-reuse-thinking-guide.md.txt +7 -9
  71. package/dist/templates/markdown/spec/guides/cross-layer-thinking-guide.md.txt +4 -8
  72. package/dist/templates/markdown/spec/guides/cross-platform-thinking-guide.md.txt +5 -5
  73. package/dist/templates/shared-hooks/index.d.ts +11 -43
  74. package/dist/templates/shared-hooks/index.d.ts.map +1 -1
  75. package/dist/templates/shared-hooks/index.js +9 -86
  76. package/dist/templates/shared-hooks/index.js.map +1 -1
  77. package/dist/templates/shared-hooks/inject-spec-context.py +1 -1
  78. package/dist/templates/shared-hooks/inject-subagent-context.py +23 -85
  79. package/dist/templates/shared-hooks/inject-workflow-state.py +56 -86
  80. package/dist/templates/shared-hooks/session-start.py +76 -98
  81. package/dist/templates/trellis/config.yaml +8 -8
  82. package/dist/templates/trellis/index.d.ts +1 -4
  83. package/dist/templates/trellis/index.d.ts.map +1 -1
  84. package/dist/templates/trellis/index.js +1 -5
  85. package/dist/templates/trellis/index.js.map +1 -1
  86. package/dist/templates/trellis/scripts/add_session.py +12 -11
  87. package/dist/templates/trellis/scripts/common/__init__.py +2 -0
  88. package/dist/templates/trellis/scripts/common/active_task.py +80 -152
  89. package/dist/templates/trellis/scripts/common/config.py +7 -7
  90. package/dist/templates/trellis/scripts/common/git_context.py +1 -1
  91. package/dist/templates/trellis/scripts/common/session_context.py +2 -2
  92. package/dist/templates/trellis/scripts/common/spec_inject.py +2 -2
  93. package/dist/templates/trellis/scripts/common/task_store.py +8 -28
  94. package/dist/templates/trellis/scripts/common/workflow_phase.py +4 -8
  95. package/dist/templates/trellis/scripts/get_context.py +2 -2
  96. package/dist/templates/trellis/scripts/init_developer.py +1 -1
  97. package/dist/templates/trellis/scripts/task.py +90 -57
  98. package/dist/templates/trellis/workflow.md +44 -66
  99. package/dist/types/ai-tools.d.ts +10 -10
  100. package/dist/types/ai-tools.d.ts.map +1 -1
  101. package/dist/types/ai-tools.js +3 -434
  102. package/dist/types/ai-tools.js.map +1 -1
  103. package/dist/utils/uninstall-scrubbers.d.ts +3 -18
  104. package/dist/utils/uninstall-scrubbers.d.ts.map +1 -1
  105. package/dist/utils/uninstall-scrubbers.js +24 -154
  106. package/dist/utils/uninstall-scrubbers.js.map +1 -1
  107. package/package.json +2 -2
  108. package/dist/configurators/antigravity.d.ts +0 -7
  109. package/dist/configurators/antigravity.d.ts.map +0 -1
  110. package/dist/configurators/antigravity.js +0 -11
  111. package/dist/configurators/antigravity.js.map +0 -1
  112. package/dist/configurators/codebuddy.d.ts +0 -10
  113. package/dist/configurators/codebuddy.d.ts.map +0 -1
  114. package/dist/configurators/codebuddy.js +0 -24
  115. package/dist/configurators/codebuddy.js.map +0 -1
  116. package/dist/configurators/copilot.d.ts +0 -14
  117. package/dist/configurators/copilot.d.ts.map +0 -1
  118. package/dist/configurators/copilot.js +0 -46
  119. package/dist/configurators/copilot.js.map +0 -1
  120. package/dist/configurators/cursor.d.ts +0 -10
  121. package/dist/configurators/cursor.d.ts.map +0 -1
  122. package/dist/configurators/cursor.js +0 -23
  123. package/dist/configurators/cursor.js.map +0 -1
  124. package/dist/configurators/devin.d.ts +0 -8
  125. package/dist/configurators/devin.d.ts.map +0 -1
  126. package/dist/configurators/devin.js +0 -12
  127. package/dist/configurators/devin.js.map +0 -1
  128. package/dist/configurators/droid.d.ts +0 -10
  129. package/dist/configurators/droid.d.ts.map +0 -1
  130. package/dist/configurators/droid.js +0 -24
  131. package/dist/configurators/droid.js.map +0 -1
  132. package/dist/configurators/dsh.d.ts +0 -26
  133. package/dist/configurators/dsh.d.ts.map +0 -1
  134. package/dist/configurators/dsh.js +0 -76
  135. package/dist/configurators/dsh.js.map +0 -1
  136. package/dist/configurators/gemini.d.ts +0 -18
  137. package/dist/configurators/gemini.d.ts.map +0 -1
  138. package/dist/configurators/gemini.js +0 -39
  139. package/dist/configurators/gemini.js.map +0 -1
  140. package/dist/configurators/grok.d.ts +0 -17
  141. package/dist/configurators/grok.d.ts.map +0 -1
  142. package/dist/configurators/grok.js +0 -38
  143. package/dist/configurators/grok.js.map +0 -1
  144. package/dist/configurators/kilo.d.ts +0 -7
  145. package/dist/configurators/kilo.d.ts.map +0 -1
  146. package/dist/configurators/kilo.js +0 -11
  147. package/dist/configurators/kilo.js.map +0 -1
  148. package/dist/configurators/kimi.d.ts +0 -26
  149. package/dist/configurators/kimi.d.ts.map +0 -1
  150. package/dist/configurators/kimi.js +0 -74
  151. package/dist/configurators/kimi.js.map +0 -1
  152. package/dist/configurators/kiro.d.ts +0 -11
  153. package/dist/configurators/kiro.d.ts.map +0 -1
  154. package/dist/configurators/kiro.js +0 -30
  155. package/dist/configurators/kiro.js.map +0 -1
  156. package/dist/configurators/omp.d.ts +0 -6
  157. package/dist/configurators/omp.d.ts.map +0 -1
  158. package/dist/configurators/omp.js +0 -28
  159. package/dist/configurators/omp.js.map +0 -1
  160. package/dist/configurators/opencode.d.ts +0 -5
  161. package/dist/configurators/opencode.d.ts.map +0 -1
  162. package/dist/configurators/opencode.js +0 -84
  163. package/dist/configurators/opencode.js.map +0 -1
  164. package/dist/configurators/pi.d.ts +0 -5
  165. package/dist/configurators/pi.d.ts.map +0 -1
  166. package/dist/configurators/pi.js +0 -39
  167. package/dist/configurators/pi.js.map +0 -1
  168. package/dist/configurators/qoder.d.ts +0 -12
  169. package/dist/configurators/qoder.d.ts.map +0 -1
  170. package/dist/configurators/qoder.js +0 -30
  171. package/dist/configurators/qoder.js.map +0 -1
  172. package/dist/configurators/reasonix.d.ts +0 -17
  173. package/dist/configurators/reasonix.d.ts.map +0 -1
  174. package/dist/configurators/reasonix.js +0 -37
  175. package/dist/configurators/reasonix.js.map +0 -1
  176. package/dist/configurators/snow.d.ts +0 -25
  177. package/dist/configurators/snow.d.ts.map +0 -1
  178. package/dist/configurators/snow.js +0 -69
  179. package/dist/configurators/snow.js.map +0 -1
  180. package/dist/configurators/trae.d.ts +0 -16
  181. package/dist/configurators/trae.d.ts.map +0 -1
  182. package/dist/configurators/trae.js +0 -34
  183. package/dist/configurators/trae.js.map +0 -1
  184. package/dist/configurators/zcode.d.ts +0 -23
  185. package/dist/configurators/zcode.d.ts.map +0 -1
  186. package/dist/configurators/zcode.js +0 -63
  187. package/dist/configurators/zcode.js.map +0 -1
  188. package/dist/templates/codebuddy/agents/trellis-check.md +0 -115
  189. package/dist/templates/codebuddy/agents/trellis-implement.md +0 -115
  190. package/dist/templates/codebuddy/agents/trellis-research.md +0 -137
  191. package/dist/templates/codebuddy/index.d.ts +0 -15
  192. package/dist/templates/codebuddy/index.d.ts.map +0 -1
  193. package/dist/templates/codebuddy/index.js +0 -15
  194. package/dist/templates/codebuddy/index.js.map +0 -1
  195. package/dist/templates/codebuddy/settings.json +0 -69
  196. package/dist/templates/copilot/copilot-instructions.md +0 -11
  197. package/dist/templates/copilot/hooks/session-start.py +0 -564
  198. package/dist/templates/copilot/hooks.json +0 -19
  199. package/dist/templates/copilot/index.d.ts +0 -28
  200. package/dist/templates/copilot/index.d.ts.map +0 -1
  201. package/dist/templates/copilot/index.js +0 -61
  202. package/dist/templates/copilot/index.js.map +0 -1
  203. package/dist/templates/copilot/prompts/before-dev.prompt.md +0 -39
  204. package/dist/templates/copilot/prompts/brainstorm.prompt.md +0 -173
  205. package/dist/templates/copilot/prompts/break-loop.prompt.md +0 -129
  206. package/dist/templates/copilot/prompts/check-cross-layer.prompt.md +0 -170
  207. package/dist/templates/copilot/prompts/check.prompt.md +0 -110
  208. package/dist/templates/copilot/prompts/create-command.prompt.md +0 -116
  209. package/dist/templates/copilot/prompts/finish-work.prompt.md +0 -99
  210. package/dist/templates/copilot/prompts/integrate-skill.prompt.md +0 -223
  211. package/dist/templates/copilot/prompts/onboard.prompt.md +0 -362
  212. package/dist/templates/copilot/prompts/parallel.prompt.md +0 -204
  213. package/dist/templates/copilot/prompts/record-session.prompt.md +0 -66
  214. package/dist/templates/copilot/prompts/start.prompt.md +0 -63
  215. package/dist/templates/copilot/prompts/update-spec.prompt.md +0 -358
  216. package/dist/templates/cursor/agents/trellis-check.md +0 -114
  217. package/dist/templates/cursor/agents/trellis-implement.md +0 -114
  218. package/dist/templates/cursor/agents/trellis-research.md +0 -136
  219. package/dist/templates/cursor/hooks.json +0 -24
  220. package/dist/templates/cursor/index.d.ts +0 -13
  221. package/dist/templates/cursor/index.d.ts.map +0 -1
  222. package/dist/templates/cursor/index.js +0 -13
  223. package/dist/templates/cursor/index.js.map +0 -1
  224. package/dist/templates/droid/droids/trellis-check.md +0 -107
  225. package/dist/templates/droid/droids/trellis-implement.md +0 -107
  226. package/dist/templates/droid/droids/trellis-research.md +0 -137
  227. package/dist/templates/droid/index.d.ts +0 -15
  228. package/dist/templates/droid/index.d.ts.map +0 -1
  229. package/dist/templates/droid/index.js +0 -15
  230. package/dist/templates/droid/index.js.map +0 -1
  231. package/dist/templates/droid/settings.json +0 -69
  232. package/dist/templates/dsh/DSH.md +0 -80
  233. package/dist/templates/dsh/agents/trellis-check.md +0 -99
  234. package/dist/templates/dsh/agents/trellis-implement.md +0 -106
  235. package/dist/templates/dsh/agents/trellis-research.md +0 -134
  236. package/dist/templates/dsh/index.d.ts +0 -22
  237. package/dist/templates/dsh/index.d.ts.map +0 -1
  238. package/dist/templates/dsh/index.js +0 -27
  239. package/dist/templates/dsh/index.js.map +0 -1
  240. package/dist/templates/gemini/agents/trellis-check.md +0 -107
  241. package/dist/templates/gemini/agents/trellis-implement.md +0 -107
  242. package/dist/templates/gemini/agents/trellis-research.md +0 -136
  243. package/dist/templates/gemini/index.d.ts +0 -13
  244. package/dist/templates/gemini/index.d.ts.map +0 -1
  245. package/dist/templates/gemini/index.js +0 -13
  246. package/dist/templates/gemini/index.js.map +0 -1
  247. package/dist/templates/gemini/settings.json +0 -40
  248. package/dist/templates/grok/agents/trellis-check.md +0 -106
  249. package/dist/templates/grok/agents/trellis-implement.md +0 -121
  250. package/dist/templates/grok/agents/trellis-research.md +0 -146
  251. package/dist/templates/grok/index.d.ts +0 -16
  252. package/dist/templates/grok/index.d.ts.map +0 -1
  253. package/dist/templates/grok/index.js +0 -19
  254. package/dist/templates/grok/index.js.map +0 -1
  255. package/dist/templates/kimi/agents/trellis-check.md +0 -104
  256. package/dist/templates/kimi/agents/trellis-implement.md +0 -120
  257. package/dist/templates/kimi/agents/trellis-research.md +0 -144
  258. package/dist/templates/kimi/index.d.ts +0 -20
  259. package/dist/templates/kimi/index.d.ts.map +0 -1
  260. package/dist/templates/kimi/index.js +0 -23
  261. package/dist/templates/kimi/index.js.map +0 -1
  262. package/dist/templates/kiro/agents/trellis-check.json +0 -26
  263. package/dist/templates/kiro/agents/trellis-implement.json +0 -26
  264. package/dist/templates/kiro/agents/trellis-research.json +0 -30
  265. package/dist/templates/kiro/agents/trellis.json +0 -34
  266. package/dist/templates/kiro/hooks/trellis-workflow-state.kiro.hook +0 -14
  267. package/dist/templates/kiro/index.d.ts +0 -32
  268. package/dist/templates/kiro/index.d.ts.map +0 -1
  269. package/dist/templates/kiro/index.js +0 -28
  270. package/dist/templates/kiro/index.js.map +0 -1
  271. package/dist/templates/omp/agents/trellis-check.md +0 -41
  272. package/dist/templates/omp/agents/trellis-implement.md +0 -55
  273. package/dist/templates/omp/agents/trellis-research.md +0 -30
  274. package/dist/templates/omp/extensions/trellis/index.ts.txt +0 -592
  275. package/dist/templates/omp/index.d.ts +0 -4
  276. package/dist/templates/omp/index.d.ts.map +0 -1
  277. package/dist/templates/omp/index.js +0 -9
  278. package/dist/templates/omp/index.js.map +0 -1
  279. package/dist/templates/opencode/agents/trellis-check.md +0 -122
  280. package/dist/templates/opencode/agents/trellis-implement.md +0 -123
  281. package/dist/templates/opencode/agents/trellis-research.md +0 -145
  282. package/dist/templates/opencode/lib/context-visibility.js +0 -86
  283. package/dist/templates/opencode/lib/session-utils.js +0 -526
  284. package/dist/templates/opencode/lib/trellis-context.js +0 -627
  285. package/dist/templates/opencode/package.json +0 -5
  286. package/dist/templates/opencode/plugins/inject-spec-context.js +0 -121
  287. package/dist/templates/opencode/plugins/inject-subagent-context.js +0 -569
  288. package/dist/templates/opencode/plugins/inject-workflow-state.js +0 -340
  289. package/dist/templates/opencode/plugins/session-start.js +0 -74
  290. package/dist/templates/pi/agents/trellis-check.md +0 -37
  291. package/dist/templates/pi/agents/trellis-implement.md +0 -52
  292. package/dist/templates/pi/agents/trellis-research.md +0 -25
  293. package/dist/templates/pi/extensions/trellis/index.ts.txt +0 -2033
  294. package/dist/templates/pi/index.d.ts +0 -5
  295. package/dist/templates/pi/index.d.ts.map +0 -1
  296. package/dist/templates/pi/index.js +0 -12
  297. package/dist/templates/pi/index.js.map +0 -1
  298. package/dist/templates/pi/settings.json +0 -9
  299. package/dist/templates/qoder/agents/trellis-check.md +0 -108
  300. package/dist/templates/qoder/agents/trellis-implement.md +0 -108
  301. package/dist/templates/qoder/agents/trellis-research.md +0 -137
  302. package/dist/templates/qoder/index.d.ts +0 -15
  303. package/dist/templates/qoder/index.d.ts.map +0 -1
  304. package/dist/templates/qoder/index.js +0 -15
  305. package/dist/templates/qoder/index.js.map +0 -1
  306. package/dist/templates/qoder/settings.json +0 -59
  307. package/dist/templates/reasonix/agents/trellis-check.md +0 -36
  308. package/dist/templates/reasonix/agents/trellis-implement.md +0 -51
  309. package/dist/templates/reasonix/index.d.ts +0 -13
  310. package/dist/templates/reasonix/index.d.ts.map +0 -1
  311. package/dist/templates/reasonix/index.js +0 -16
  312. package/dist/templates/reasonix/index.js.map +0 -1
  313. package/dist/templates/shared-hooks/inject-shell-session-context.py +0 -292
  314. package/dist/templates/snow/SNOW.md +0 -86
  315. package/dist/templates/snow/agents/trellis-check.md +0 -136
  316. package/dist/templates/snow/agents/trellis-implement.md +0 -158
  317. package/dist/templates/snow/agents/trellis-research.md +0 -114
  318. package/dist/templates/snow/hooks/beforeSubAgentStart.json +0 -15
  319. package/dist/templates/snow/hooks/onSessionStart.json +0 -15
  320. package/dist/templates/snow/hooks/onUserMessage.json +0 -15
  321. package/dist/templates/snow/hooks/write-trellis-context.py +0 -644
  322. package/dist/templates/snow/index.d.ts +0 -26
  323. package/dist/templates/snow/index.d.ts.map +0 -1
  324. package/dist/templates/snow/index.js +0 -36
  325. package/dist/templates/snow/index.js.map +0 -1
  326. package/dist/templates/trae/agents/trellis-check.md +0 -108
  327. package/dist/templates/trae/agents/trellis-implement.md +0 -108
  328. package/dist/templates/trae/agents/trellis-research.md +0 -137
  329. package/dist/templates/trae/hooks.json +0 -39
  330. package/dist/templates/trae/index.d.ts +0 -15
  331. package/dist/templates/trae/index.d.ts.map +0 -1
  332. package/dist/templates/trae/index.js +0 -15
  333. package/dist/templates/trae/index.js.map +0 -1
  334. package/dist/templates/trellis/gitattributes.txt +0 -9
  335. package/dist/templates/trellis/scripts/common/cli_adapter.py +0 -982
  336. package/dist/templates/zcode/agents/trellis-check.md +0 -110
  337. package/dist/templates/zcode/agents/trellis-implement.md +0 -116
  338. package/dist/templates/zcode/agents/trellis-research.md +0 -133
  339. package/dist/templates/zcode/config.json +0 -52
  340. package/dist/templates/zcode/index.d.ts +0 -25
  341. package/dist/templates/zcode/index.d.ts.map +0 -1
  342. package/dist/templates/zcode/index.js +0 -29
  343. package/dist/templates/zcode/index.js.map +0 -1
@@ -1,627 +0,0 @@
1
- /**
2
- * Trellis Context Manager
3
- *
4
- * Utility class for OpenCode plugins providing file reading,
5
- * JSONL parsing, and context building capabilities.
6
- */
7
-
8
- import { existsSync, readFileSync, appendFileSync, readdirSync, realpathSync, statSync } from "fs"
9
- import { isAbsolute, join, relative } from "path"
10
- import { platform } from "os"
11
- import { execSync } from "child_process"
12
- import { createHash } from "crypto"
13
- import { Buffer, isUtf8 } from "buffer"
14
- import process from "process"
15
-
16
- const PYTHON_CMD = platform() === "win32" ? "python" : "python3"
17
- // Debug logging
18
- const DEBUG_LOG = "/tmp/trellis-plugin-debug.log"
19
-
20
- function debugLog(prefix, ...args) {
21
- const timestamp = new Date().toISOString()
22
- const msg = `[${timestamp}] [${prefix}] ${args.map(a => typeof a === "object" ? JSON.stringify(a) : a).join(" ")}\n`
23
- try {
24
- appendFileSync(DEBUG_LOG, msg)
25
- } catch {
26
- // ignore
27
- }
28
- }
29
-
30
- function stringValue(value) {
31
- return typeof value === "string" && value.trim() ? value.trim() : null
32
- }
33
-
34
- function sanitizeKey(raw) {
35
- const safe = raw.trim().replace(/[^A-Za-z0-9._-]+/g, "_").replace(/^[._-]+|[._-]+$/g, "")
36
- return safe ? safe.slice(0, 160) : ""
37
- }
38
-
39
- function hashValue(raw) {
40
- return createHash("sha256").update(raw).digest("hex").slice(0, 24)
41
- }
42
-
43
- function lookupString(data, keys) {
44
- if (!data || typeof data !== "object") return null
45
- for (const key of keys) {
46
- const value = stringValue(data[key])
47
- if (value) return value
48
- }
49
- for (const nestedKey of ["input", "properties", "event", "hook_input", "hookInput"]) {
50
- const nested = data[nestedKey]
51
- if (nested && typeof nested === "object") {
52
- const value = lookupString(nested, keys)
53
- if (value) return value
54
- }
55
- }
56
- return null
57
- }
58
-
59
- function buildContextKey(platformName, kind, value) {
60
- if (kind === "transcript") {
61
- return `${platformName}_transcript_${hashValue(value)}`
62
- }
63
- const safeValue = sanitizeKey(value)
64
- return safeValue ? `${platformName}_${safeValue}` : `${platformName}_${hashValue(value)}`
65
- }
66
-
67
- // Matches `trellis-implement`, `trellis-check`, `trellis-research` exactly.
68
- // Used by chat.message plugins to skip injection inside Trellis sub-agent turns.
69
- const TRELLIS_SUBAGENT_RE = /^trellis-(implement|check|research)$/
70
-
71
- /**
72
- * Return true when the OpenCode `chat.message` input represents a Trellis
73
- * sub-agent turn. `input.agent` is set by OpenCode when a Task tool spawns a
74
- * child session with a custom agent (see `packages/opencode/src/tool/task.ts`).
75
- */
76
- export function isTrellisSubagent(input) {
77
- if (!input || typeof input !== "object") return false
78
- const agent = typeof input.agent === "string" ? input.agent.trim() : ""
79
- return TRELLIS_SUBAGENT_RE.test(agent)
80
- }
81
-
82
- // ============================================================
83
- // Context Injection Limits (issue #441)
84
- //
85
- // Notice text and behavior mirrored byte-for-byte from the shared-hooks
86
- // Python sub-agent context injection hook and the Pi extension. Changing
87
- // wording here requires changing it there too.
88
- // ============================================================
89
-
90
- const DEFAULT_CONTEXT_INJECTION_LIMITS = {
91
- max_file_bytes: 32768,
92
- max_artifact_bytes: 65536,
93
- max_total_bytes: 131072,
94
- }
95
-
96
- /**
97
- * Truncate `buf` to at most `cap` bytes without splitting a UTF-8
98
- * multi-byte sequence. `cap <= 0` means "no limit".
99
- */
100
- function truncateUtf8(buf, cap) {
101
- if (cap <= 0 || buf.length <= cap) return buf
102
- let i = cap
103
- // Back off over continuation bytes (10xxxxxx) to find the lead byte.
104
- while (i > 0 && (buf[i - 1] & 0xc0) === 0x80) i--
105
- if (i === 0) return Buffer.alloc(0)
106
- const lead = buf[i - 1]
107
- if (lead & 0x80) {
108
- let seqLen = 1
109
- if ((lead & 0xe0) === 0xc0) seqLen = 2
110
- else if ((lead & 0xf0) === 0xe0) seqLen = 3
111
- else if ((lead & 0xf8) === 0xf0) seqLen = 4
112
- // Drop the lead byte too if its full sequence didn't fit.
113
- if (i - 1 + seqLen > cap) i--
114
- }
115
- return buf.subarray(0, i)
116
- }
117
-
118
- function stripInlineComment(value) {
119
- let inQuote = null
120
- for (let idx = 0; idx < value.length; idx++) {
121
- const ch = value[idx]
122
- if (inQuote) {
123
- if (ch === inQuote) inQuote = null
124
- continue
125
- }
126
- if (ch === '"' || ch === "'") {
127
- inQuote = ch
128
- continue
129
- }
130
- if (ch === "#" && (idx === 0 || /\s/.test(value[idx - 1]))) {
131
- return value.slice(0, idx)
132
- }
133
- }
134
- return value
135
- }
136
-
137
- function unquoteYaml(s) {
138
- if (s.length >= 2 && s[0] === s[s.length - 1] && (s[0] === '"' || s[0] === "'")) {
139
- return s.slice(1, -1)
140
- }
141
- return s
142
- }
143
-
144
- /**
145
- * Line-based parser for ONLY the `context_injection:` block of
146
- * `.trellis/config.yaml`. Not a general YAML parser — mirrors
147
- * `common.config.get_context_injection_limits()` semantics for this
148
- * section only (missing keys keep the default; invalid/negative values
149
- * fall back to the default for that key with a debugLog warning).
150
- */
151
- function readContextInjectionLimits(repoRoot) {
152
- const limits = { ...DEFAULT_CONTEXT_INJECTION_LIMITS }
153
- let text = null
154
- try {
155
- text = readFileSync(join(repoRoot, ".trellis", "config.yaml"), "utf-8")
156
- } catch {
157
- return limits
158
- }
159
- if (!text) return limits
160
-
161
- let inSection = false
162
- let sectionIndent = -1
163
- for (const rawLine of text.split(/\r?\n/)) {
164
- const trimmed = rawLine.trim()
165
- if (!inSection) {
166
- if (/^context_injection\s*:\s*(#.*)?$/.test(trimmed)) {
167
- inSection = true
168
- sectionIndent = rawLine.length - rawLine.trimStart().length
169
- }
170
- continue
171
- }
172
- if (!trimmed || trimmed.startsWith("#")) continue
173
- const indent = rawLine.length - rawLine.trimStart().length
174
- if (indent <= sectionIndent) break
175
- const m = trimmed.match(/^([A-Za-z_][A-Za-z0-9_]*)\s*:\s*(.*)$/)
176
- if (!m) continue
177
- const key = m[1]
178
- if (!(key in limits)) continue
179
- const raw = unquoteYaml(stripInlineComment(m[2]).trim()).trim()
180
- if (!/^-?\d+$/.test(raw) || parseInt(raw, 10) < 0) {
181
- // invalid/negative -> keep default (Python warns on stderr)
182
- debugLog("context", `invalid context_injection.${key} value: ${raw}; using default ${limits[key]}`)
183
- continue
184
- }
185
- limits[key] = parseInt(raw, 10)
186
- }
187
- return limits
188
- }
189
-
190
- /** Tracks the running total of bytes emitted into the sub-agent context. */
191
- class ContextBudget {
192
- constructor(maxTotalBytes) {
193
- this.maxTotalBytes = maxTotalBytes
194
- this.used = 0
195
- }
196
-
197
- hasRoom(size) {
198
- if (this.maxTotalBytes <= 0) return true
199
- return this.used + size <= this.maxTotalBytes
200
- }
201
-
202
- add(size) {
203
- this.used += size
204
- }
205
- }
206
-
207
- function truncateNotice(path, cap) {
208
- return `\n[Trellis: truncated at ${cap} bytes — read ${path} for the full content]`
209
- }
210
-
211
- function isBinaryContent(data) {
212
- return data.includes(0) || !isUtf8(data)
213
- }
214
-
215
- function binaryNotice(path, size, reason) {
216
- return `[Trellis: not inlined (binary file) — ${path} (${size} bytes): ${reason}]`
217
- }
218
-
219
- function indexNotice(path, size, reason) {
220
- return `[Trellis: not inlined (total context limit reached) — ${path} (${size} bytes): ${reason}]`
221
- }
222
-
223
- /**
224
- * Return an inlined `=== header ===` block, or degrade to an index
225
- * notice once the total context budget is exhausted.
226
- */
227
- function budgetedBlock(budget, header, plainPath, content, reason, sizeForIndex) {
228
- const block = `=== ${header} ===\n${content}`
229
- const blockBytes = Buffer.byteLength(block, "utf-8")
230
- if (!budget.hasRoom(blockBytes)) {
231
- const notice = indexNotice(plainPath, sizeForIndex, reason)
232
- budget.add(Buffer.byteLength(notice, "utf-8"))
233
- return notice
234
- }
235
- budget.add(blockBytes)
236
- return block
237
- }
238
-
239
- /** Read raw file bytes, return null if file doesn't exist. */
240
- function readFileBytes(basePath, filePath) {
241
- const fullPath = isAbsolute(filePath) ? filePath : join(basePath, filePath)
242
- try {
243
- if (!statSync(fullPath).isFile()) return null
244
- } catch {
245
- return null
246
- }
247
- try {
248
- return readFileSync(fullPath)
249
- } catch {
250
- return null
251
- }
252
- }
253
-
254
- /** Read a JSONL-referenced file, apply the per-file cap, then budget it. */
255
- function materializeFile(basePath, filePath, reason, limits, budget) {
256
- const data = readFileBytes(basePath, filePath)
257
- if (data === null) return null
258
-
259
- const size = data.length
260
- if (isBinaryContent(data)) {
261
- const notice = binaryNotice(filePath, size, reason)
262
- budget.add(Buffer.byteLength(notice, "utf-8"))
263
- return notice
264
- }
265
- const cap = limits.max_file_bytes
266
- const truncated = truncateUtf8(data, cap)
267
- let content = truncated.toString("utf-8")
268
- if (truncated.length < size) content += truncateNotice(filePath, cap)
269
-
270
- return budgetedBlock(budget, filePath, filePath, content, reason, size)
271
- }
272
-
273
- /**
274
- * Read all .md files in a directory, applying the same per-file and
275
- * total caps as a single-file JSONL entry.
276
- */
277
- function materializeDirectory(basePath, dirPath, reason, limits, budget, maxFiles = 20) {
278
- const blocks = []
279
- const fullPath = isAbsolute(dirPath) ? dirPath : join(basePath, dirPath)
280
-
281
- let files
282
- try {
283
- if (!statSync(fullPath).isDirectory()) return blocks
284
- files = readdirSync(fullPath)
285
- .filter(f => f.endsWith(".md") && statSync(join(fullPath, f)).isFile())
286
- .sort()
287
- } catch {
288
- return blocks
289
- }
290
-
291
- for (const filename of files.slice(0, maxFiles)) {
292
- const relativePath = join(dirPath, filename)
293
- const block = materializeFile(basePath, relativePath, reason, limits, budget)
294
- if (block) blocks.push(block)
295
- }
296
- return blocks
297
- }
298
-
299
- /**
300
- * Read a task artifact (prd/design/implement.md), apply the per-artifact
301
- * cap, then budget it.
302
- */
303
- function materializeArtifact(basePath, filePath, headerLabel, reason, limits, budget) {
304
- const data = readFileBytes(basePath, filePath)
305
- if (data === null) return null
306
-
307
- const size = data.length
308
- const cap = limits.max_artifact_bytes
309
- const truncated = truncateUtf8(data, cap)
310
- let content = truncated.toString("utf-8")
311
- if (truncated.length < size) content += truncateNotice(filePath, cap)
312
-
313
- return budgetedBlock(budget, headerLabel, filePath, content, reason, size)
314
- }
315
-
316
- /**
317
- * Trellis Context Manager
318
- */
319
- export class TrellisContext {
320
- constructor(directory) {
321
- this.directory = directory
322
- debugLog("context", "TrellisContext initialized", { directory })
323
- }
324
-
325
- // ============================================================
326
- // Trellis Project Detection
327
- // ============================================================
328
-
329
- isTrellisProject() {
330
- return existsSync(join(this.directory, ".trellis"))
331
- }
332
-
333
- // OpenCode exports no session identity into the environment: no OPENCODE_*
334
- // name in the 1.17.18 binary or the 1.18.13 source carries one. The plugin
335
- // hook input is the only source, and the `export TRELLIS_CONTEXT_ID=` prefix
336
- // this plugin adds to Bash commands is the only way that identity reaches a
337
- // child process. TRELLIS_CONTEXT_ID is honored here so a nested Trellis run
338
- // inherits its parent's key; do not add platform-native env names next to it
339
- // without evidence a vendor sets them.
340
- getContextKey(platformInput = null) {
341
- const override = stringValue(process.env.TRELLIS_CONTEXT_ID)
342
- if (override) {
343
- return sanitizeKey(override) || hashValue(override)
344
- }
345
-
346
- const input = platformInput && typeof platformInput === "object" ? platformInput : null
347
- if (!input) return null
348
-
349
- const sessionID = lookupString(input, ["session_id", "sessionId", "sessionID"])
350
- if (sessionID) return buildContextKey("opencode", "session", sessionID)
351
-
352
- const conversationID = lookupString(input, ["conversation_id", "conversationId", "conversationID"])
353
- if (conversationID) return buildContextKey("opencode", "conversation", conversationID)
354
-
355
- const transcriptPath = lookupString(input, ["transcript_path", "transcriptPath", "transcript"])
356
- if (transcriptPath) return buildContextKey("opencode", "transcript", transcriptPath)
357
-
358
- return null
359
- }
360
-
361
- readContext(contextKey) {
362
- try {
363
- const contextPath = join(this.directory, ".trellis", ".runtime", "sessions", `${contextKey}.json`)
364
- if (!existsSync(contextPath)) return null
365
- return JSON.parse(readFileSync(contextPath, "utf-8"))
366
- } catch {
367
- return null
368
- }
369
- }
370
-
371
- /**
372
- * Get active task from session runtime context.
373
- *
374
- * Resolution order (mirrors Python `active_task.resolve_active_task`):
375
- * 1. Lookup the runtime file for the input-derived context key.
376
- * 2. If that misses and exactly one session runtime file exists locally,
377
- * use it (`_resolveSingleSessionFallback`). Refuses to guess when 0 or
378
- * ≥2 files exist so multi-window isolation holds.
379
- */
380
- getActiveTask(platformInput = null) {
381
- const contextKey = this.getContextKey(platformInput)
382
- if (contextKey) {
383
- const context = this.readContext(contextKey)
384
- const taskRef = this.normalizeTaskRef(context?.current_task || "")
385
- if (taskRef) {
386
- const taskDir = this.resolveTaskDir(taskRef)
387
- return {
388
- taskPath: taskRef,
389
- source: `session:${contextKey}`,
390
- stale: !taskDir || !existsSync(taskDir),
391
- }
392
- }
393
- }
394
-
395
- const fallback = this._resolveSingleSessionFallback()
396
- if (fallback) {
397
- return fallback
398
- }
399
-
400
- return { taskPath: null, source: "none", stale: false }
401
- }
402
-
403
- /**
404
- * Mirror of Python `_resolve_single_session_fallback`. Returns the task
405
- * pointed at by the sole session runtime file when exactly one exists,
406
- * else null.
407
- */
408
- _resolveSingleSessionFallback() {
409
- const sessionsDir = join(this.directory, ".trellis", ".runtime", "sessions")
410
- if (!existsSync(sessionsDir)) return null
411
-
412
- let files
413
- try {
414
- files = readdirSync(sessionsDir)
415
- .filter(name => name.endsWith(".json"))
416
- .sort()
417
- } catch {
418
- return null
419
- }
420
- if (files.length !== 1) return null
421
-
422
- const sessionFile = join(sessionsDir, files[0])
423
- let context
424
- try {
425
- context = JSON.parse(readFileSync(sessionFile, "utf-8"))
426
- } catch {
427
- return null
428
- }
429
- const taskRef = this.normalizeTaskRef(context?.current_task || "")
430
- if (!taskRef) return null
431
-
432
- const taskDir = this.resolveTaskDir(taskRef)
433
- const fallbackKey = files[0].replace(/\.json$/, "")
434
- return {
435
- taskPath: taskRef,
436
- source: `session-fallback:${fallbackKey}`,
437
- stale: !taskDir || !existsSync(taskDir),
438
- }
439
- }
440
-
441
- getCurrentTask(platformInput = null) {
442
- return this.getActiveTask(platformInput).taskPath
443
- }
444
-
445
- normalizeTaskRef(taskRef) {
446
- if (!taskRef) {
447
- return ""
448
- }
449
-
450
- if (isAbsolute(taskRef)) {
451
- return taskRef.trim()
452
- }
453
-
454
- let normalized = taskRef.trim().replace(/\\/g, "/")
455
- while (normalized.startsWith("./")) {
456
- normalized = normalized.slice(2)
457
- }
458
-
459
- if (normalized.startsWith("tasks/")) {
460
- return `.trellis/${normalized}`
461
- }
462
-
463
- return normalized
464
- }
465
-
466
- /**
467
- * Return `candidate` when it lands inside the project, else null.
468
- *
469
- * A task ref is not always something the user typed. `task.py` now refuses
470
- * to store one that leaves the project, but a session file written before
471
- * that fix can still hold one, and `trellis update` does not rewrite session
472
- * files — so a poisoned pointer outlives the upgrade that closed the writer.
473
- * Both sides are resolved so a task directory symlinked outside is refused
474
- * too, but the original `candidate` is returned on success: callers build
475
- * paths relative to `this.directory`, and handing them a realpath would break
476
- * that whenever the project itself sits behind a symlink.
477
- */
478
- containInProject(candidate) {
479
- try {
480
- const rel = relative(realpathSync(this.directory), realpathSync(candidate))
481
- if (rel !== "" && (rel.startsWith("..") || isAbsolute(rel))) {
482
- return null
483
- }
484
- return candidate
485
- } catch {
486
- return null
487
- }
488
- }
489
-
490
- resolveTaskDir(taskRef) {
491
- const normalized = this.normalizeTaskRef(taskRef)
492
- if (!normalized) {
493
- return null
494
- }
495
-
496
- const candidate = isAbsolute(normalized)
497
- ? normalized
498
- : normalized.startsWith(".trellis/")
499
- ? join(this.directory, normalized)
500
- : join(this.directory, ".trellis", "tasks", normalized)
501
-
502
- return this.containInProject(candidate)
503
- }
504
-
505
- // ============================================================
506
- // File Reading Utilities
507
- // ============================================================
508
-
509
- readFile(filePath) {
510
- try {
511
- if (existsSync(filePath)) {
512
- return readFileSync(filePath, "utf-8")
513
- }
514
- } catch {
515
- // Ignore read errors
516
- }
517
- return null
518
- }
519
-
520
- readProjectFile(relativePath) {
521
- return this.readFile(join(this.directory, relativePath))
522
- }
523
-
524
- runScript(scriptPath, cwd = null, contextKey = null) {
525
- try {
526
- const result = execSync(`${PYTHON_CMD} "${scriptPath}"`, {
527
- cwd: cwd || this.directory,
528
- timeout: 10000,
529
- encoding: "utf-8",
530
- stdio: ["pipe", "pipe", "pipe"],
531
- env: {
532
- ...process.env,
533
- ...(contextKey ? { TRELLIS_CONTEXT_ID: contextKey } : {}),
534
- },
535
- })
536
- return result || ""
537
- } catch {
538
- return ""
539
- }
540
- }
541
-
542
- // ============================================================
543
- // JSONL Reading
544
- // ============================================================
545
-
546
- /**
547
- * Read a JSONL file and materialize referenced files/directories into
548
- * context blocks, applying per-file caps and the shared total budget
549
- * (issue #441). Mirrors Python `_materialize_jsonl_entries`.
550
- * Supports:
551
- * {"file": "path/to/file.md", "reason": "..."}
552
- * {"file": "path/to/dir/", "type": "directory", "reason": "..."}
553
- *
554
- * Missing referenced files are skipped silently (Python `_materialize_file`
555
- * returns None for them).
556
- */
557
- readJsonlWithFiles(jsonlPath, limits, budget) {
558
- const blocks = []
559
- const content = this.readFile(jsonlPath)
560
- if (!content) return blocks
561
-
562
- for (const line of content.split("\n")) {
563
- if (!line.trim()) continue
564
- try {
565
- const item = JSON.parse(line)
566
- const file = item.file || item.path
567
- const entryType = item.type || "file"
568
- const reason = item.reason || "-"
569
-
570
- if (!file) continue
571
-
572
- if (entryType === "directory") {
573
- blocks.push(...materializeDirectory(this.directory, file, reason, limits, budget))
574
- } else {
575
- const block = materializeFile(this.directory, file, reason, limits, budget)
576
- if (block) blocks.push(block)
577
- }
578
- } catch {
579
- // Ignore parse errors for individual lines
580
- }
581
- }
582
- return blocks
583
- }
584
-
585
- buildContextFromEntries(blocks) {
586
- return blocks.join("\n\n")
587
- }
588
- }
589
-
590
- // ============================================================
591
- // Context Collector (for session deduplication)
592
- // ============================================================
593
-
594
- class ContextCollector {
595
- constructor() {
596
- this.processed = new Set()
597
- }
598
-
599
- markProcessed(sessionID) {
600
- this.processed.add(sessionID)
601
- }
602
-
603
- isProcessed(sessionID) {
604
- return this.processed.has(sessionID)
605
- }
606
-
607
- clear(sessionID) {
608
- this.processed.delete(sessionID)
609
- }
610
- }
611
-
612
- // Singleton instance
613
- export const contextCollector = new ContextCollector()
614
-
615
- // Export debug log for plugins
616
- export { debugLog }
617
-
618
- // Context injection limits (issue #441) — exported for plugins and tests
619
- export {
620
- DEFAULT_CONTEXT_INJECTION_LIMITS,
621
- truncateUtf8,
622
- readContextInjectionLimits,
623
- ContextBudget,
624
- materializeFile,
625
- materializeDirectory,
626
- materializeArtifact,
627
- }
@@ -1,5 +0,0 @@
1
- {
2
- "dependencies": {
3
- "@opencode-ai/plugin": "^1.14.39"
4
- }
5
- }