@blxzer/cursor-trellis 0.4.1 → 0.4.3

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 (337) hide show
  1. package/CHANGELOG.md +49 -0
  2. package/README.md +25 -14
  3. package/README.zh-CN.md +14 -10
  4. package/bin/smart-search.js +10 -2
  5. package/dist/cli/index.d.ts.map +1 -1
  6. package/dist/cli/index.js +36 -15
  7. package/dist/cli/index.js.map +1 -1
  8. package/dist/commands/init.d.ts +1 -1
  9. package/dist/commands/init.d.ts.map +1 -1
  10. package/dist/commands/init.js +42 -153
  11. package/dist/commands/init.js.map +1 -1
  12. package/dist/commands/migrate.d.ts +10 -0
  13. package/dist/commands/migrate.d.ts.map +1 -0
  14. package/dist/commands/migrate.js +38 -0
  15. package/dist/commands/migrate.js.map +1 -0
  16. package/dist/commands/update.d.ts +5 -0
  17. package/dist/commands/update.d.ts.map +1 -1
  18. package/dist/commands/update.js +144 -41
  19. package/dist/commands/update.js.map +1 -1
  20. package/dist/configurators/cursor.d.ts +2 -1
  21. package/dist/configurators/cursor.d.ts.map +1 -1
  22. package/dist/configurators/cursor.js +4 -2
  23. package/dist/configurators/cursor.js.map +1 -1
  24. package/dist/configurators/index.js +4 -4
  25. package/dist/configurators/index.js.map +1 -1
  26. package/dist/configurators/shared.d.ts +8 -0
  27. package/dist/configurators/shared.d.ts.map +1 -1
  28. package/dist/configurators/shared.js +27 -1
  29. package/dist/configurators/shared.js.map +1 -1
  30. package/dist/configurators/workflow.d.ts +0 -9
  31. package/dist/configurators/workflow.d.ts.map +1 -1
  32. package/dist/configurators/workflow.js +12 -45
  33. package/dist/configurators/workflow.js.map +1 -1
  34. package/dist/constants/paths.d.ts +13 -0
  35. package/dist/constants/paths.d.ts.map +1 -1
  36. package/dist/constants/paths.js +17 -0
  37. package/dist/constants/paths.js.map +1 -1
  38. package/dist/migrations/manifests/0.3.0.json +1 -1
  39. package/dist/templates/common/bundled-skills/cstl-check/SKILL.md +1 -1
  40. package/dist/templates/common/bundled-skills/cstl-meta/references/platform-files/hooks-and-settings.md +2 -2
  41. package/dist/templates/common/bundled-skills/cstl-meta/references/platform-files/overview.md +1 -1
  42. package/dist/templates/common/bundled-skills/cstl-meta/references/platform-files/skills-and-commands.md +2 -4
  43. package/dist/templates/common/bundled-skills/cstl-skill-creator/SKILL.md +10 -7
  44. package/dist/templates/common/bundled-skills/cstl-skill-creator/references/authoring-rules.md +93 -19
  45. package/dist/templates/common/bundled-skills/cstl-skill-creator/references/general-authoring.md +189 -0
  46. package/dist/templates/common/bundled-skills/cstl-skill-creator/references/review-checklist.md +10 -5
  47. package/dist/templates/common/commands/continue.md +6 -5
  48. package/dist/templates/common/index.d.ts +20 -0
  49. package/dist/templates/common/index.d.ts.map +1 -1
  50. package/dist/templates/common/index.js +21 -2
  51. package/dist/templates/common/index.js.map +1 -1
  52. package/dist/templates/common/optional-skills/chrome-cdp/SKILL.md +179 -0
  53. package/dist/templates/common/optional-skills/chrome-cdp/examples/fetch-hook-api-capture.md +149 -0
  54. package/dist/templates/common/optional-skills/chrome-cdp/scripts/cdp.mjs +903 -0
  55. package/dist/templates/common/skills/brainstorm.md +2 -2
  56. package/dist/templates/cursor/agents/cstl-check.md +2 -2
  57. package/dist/templates/cursor/agents/cstl-implement.md +1 -1
  58. package/dist/templates/cursor/agents/cstl-research.md +2 -2
  59. package/dist/templates/cursor/fixtures/expected-rules.d.ts +1 -1
  60. package/dist/templates/cursor/fixtures/expected-rules.d.ts.map +1 -1
  61. package/dist/templates/cursor/fixtures/expected-rules.js +7 -24
  62. package/dist/templates/cursor/fixtures/expected-rules.js.map +1 -1
  63. package/dist/templates/cursor/hooks.json +4 -0
  64. package/dist/templates/cursor/index.d.ts +1 -1
  65. package/dist/templates/cursor/index.js +1 -1
  66. package/dist/templates/cursor/rules/cstl-bootstrap.mdc +17 -0
  67. package/dist/templates/markdown/agents.md +36 -3
  68. package/dist/templates/markdown/{spec/guides → framework}/cursor-native-modes-guide.md.txt +13 -10
  69. package/dist/templates/markdown/{spec/guides → framework}/cursor-subagent-policy.md.txt +63 -127
  70. package/dist/templates/markdown/framework/dogfood-only-surfaces.md.txt +29 -0
  71. package/dist/templates/markdown/{spec/guides → framework}/execution-strategy.md.txt +2 -0
  72. package/dist/templates/markdown/framework/index.md.txt +31 -0
  73. package/dist/templates/markdown/framework/internal-skills-cursor-reachability.md.txt +45 -0
  74. package/dist/templates/markdown/framework/middleware-protocol.md.txt +83 -0
  75. package/dist/templates/markdown/framework/parallel-first-execution.md.txt +78 -0
  76. package/dist/templates/markdown/framework/prd-grill-frontier.md.txt +83 -0
  77. package/dist/templates/markdown/framework/release-boundary.md.txt +30 -0
  78. package/dist/templates/markdown/{spec/guides → framework}/retrieval-daily-guide.md.txt +1 -1
  79. package/dist/templates/markdown/framework/upgrade.md.txt +15 -0
  80. package/dist/templates/markdown/{spec/guides → framework}/verification-strength-guide.md.txt +2 -2
  81. package/dist/templates/markdown/index.d.ts +26 -10
  82. package/dist/templates/markdown/index.d.ts.map +1 -1
  83. package/dist/templates/markdown/index.js +87 -11
  84. package/dist/templates/markdown/index.js.map +1 -1
  85. package/dist/templates/markdown/prompts/run-semantic-slice-12.md.txt +1 -1
  86. package/dist/templates/markdown/spec/guides/index.md.txt +5 -19
  87. package/dist/templates/markdown/spec/guides/prototype-guide.md.txt +10 -10
  88. package/dist/templates/markdown/spec/guides/test-discipline-guide.md.txt +1 -1
  89. package/dist/templates/shared-hooks/event-bridge.py +60 -0
  90. package/dist/templates/shared-hooks/index.d.ts +1 -1
  91. package/dist/templates/shared-hooks/index.d.ts.map +1 -1
  92. package/dist/templates/shared-hooks/index.js +1 -0
  93. package/dist/templates/shared-hooks/index.js.map +1 -1
  94. package/dist/templates/shared-hooks/session-start.py +19 -2
  95. package/dist/templates/trellis/CONTEXT.md +63 -3
  96. package/dist/templates/trellis/config.yaml +1 -1
  97. package/dist/templates/trellis/gitignore.txt +39 -39
  98. package/dist/templates/trellis/index.d.ts +12 -0
  99. package/dist/templates/trellis/index.d.ts.map +1 -1
  100. package/dist/templates/trellis/index.js +73 -15
  101. package/dist/templates/trellis/index.js.map +1 -1
  102. package/dist/templates/trellis/pool/README.md +12 -3
  103. package/dist/templates/trellis/pool/plan.md +24 -5
  104. package/dist/templates/trellis/scripts/common/adapter_middleware.py +158 -0
  105. package/dist/templates/trellis/scripts/common/artifact_locale.py +12 -3
  106. package/dist/templates/trellis/scripts/common/cursor_retrieval_env.py +5 -2
  107. package/dist/templates/trellis/scripts/common/full_quality.py +237 -0
  108. package/dist/templates/trellis/scripts/common/git.py +6 -0
  109. package/dist/templates/trellis/scripts/common/git_context.py +17 -5
  110. package/dist/templates/trellis/scripts/common/kernel_command.py +367 -0
  111. package/dist/templates/trellis/scripts/common/lite_context.py +280 -0
  112. package/dist/templates/trellis/scripts/common/ondemand_topology.py +225 -0
  113. package/dist/templates/trellis/scripts/common/parent_orchestration.py +1 -1
  114. package/dist/templates/trellis/scripts/common/pool_store.py +16 -4
  115. package/dist/templates/trellis/scripts/common/retrieval_agent_instructions.py +2 -2
  116. package/dist/templates/trellis/scripts/common/session_context.py +28 -2
  117. package/dist/templates/trellis/scripts/common/task_gates.py +73 -12
  118. package/dist/templates/trellis/scripts/common/task_map.py +42 -9
  119. package/dist/templates/trellis/scripts/common/task_store.py +293 -61
  120. package/dist/templates/trellis/scripts/common/test_adapter_middleware.py +56 -0
  121. package/dist/templates/trellis/scripts/common/test_depends_mode_block.py +9 -0
  122. package/dist/templates/trellis/scripts/common/test_full_quality.py +197 -0
  123. package/dist/templates/trellis/scripts/common/test_kernel_command.py +326 -0
  124. package/dist/templates/trellis/scripts/common/test_lite_path.py +190 -0
  125. package/dist/templates/trellis/scripts/common/test_observable_defaults.py +10 -4
  126. package/dist/templates/trellis/scripts/common/test_ondemand_topology.py +122 -0
  127. package/dist/templates/trellis/scripts/common/test_pool_store.py +17 -0
  128. package/dist/templates/trellis/scripts/common/test_task_store_kernel_patch.py +163 -0
  129. package/dist/templates/trellis/scripts/common/workflow_phase.py +3 -0
  130. package/dist/templates/trellis/scripts/cursor_retrieval_probe_prompt.md +4 -5
  131. package/dist/templates/trellis/scripts/get_context.py +1 -0
  132. package/dist/templates/trellis/scripts/pool.py +5 -0
  133. package/dist/templates/trellis/scripts/task.py +83 -7
  134. package/dist/templates/trellis/workflow.md +109 -23
  135. package/dist/utils/cursor-retrieval-env.d.ts +2 -1
  136. package/dist/utils/cursor-retrieval-env.d.ts.map +1 -1
  137. package/dist/utils/cursor-retrieval-env.js +3 -2
  138. package/dist/utils/cursor-retrieval-env.js.map +1 -1
  139. package/dist/utils/cursor2plus-residue-cleanup.d.ts +38 -0
  140. package/dist/utils/cursor2plus-residue-cleanup.d.ts.map +1 -0
  141. package/dist/utils/cursor2plus-residue-cleanup.js +145 -0
  142. package/dist/utils/cursor2plus-residue-cleanup.js.map +1 -0
  143. package/dist/utils/manifest-prune.d.ts.map +1 -1
  144. package/dist/utils/manifest-prune.js +7 -2
  145. package/dist/utils/manifest-prune.js.map +1 -1
  146. package/dist/utils/p36-upgrade.d.ts +62 -0
  147. package/dist/utils/p36-upgrade.d.ts.map +1 -0
  148. package/dist/utils/p36-upgrade.js +160 -0
  149. package/dist/utils/p36-upgrade.js.map +1 -0
  150. package/dist/utils/project-capabilities.d.ts +1 -12
  151. package/dist/utils/project-capabilities.d.ts.map +1 -1
  152. package/dist/utils/project-capabilities.js +3 -100
  153. package/dist/utils/project-capabilities.js.map +1 -1
  154. package/dist/utils/readiness.d.ts +10 -0
  155. package/dist/utils/readiness.d.ts.map +1 -1
  156. package/dist/utils/readiness.js +58 -26
  157. package/dist/utils/readiness.js.map +1 -1
  158. package/dist/utils/retrieval-agent-instructions.js +1 -1
  159. package/dist/utils/retrieval-agent-instructions.js.map +1 -1
  160. package/dist/utils/retrieval-execution-telemetry.js +1 -1
  161. package/dist/utils/retrieval-execution-telemetry.js.map +1 -1
  162. package/dist/utils/template-hash.d.ts.map +1 -1
  163. package/dist/utils/template-hash.js +11 -1
  164. package/dist/utils/template-hash.js.map +1 -1
  165. package/dist/utils/uninstall-scrubbers.d.ts.map +1 -1
  166. package/dist/utils/uninstall-scrubbers.js +52 -14
  167. package/dist/utils/uninstall-scrubbers.js.map +1 -1
  168. package/dist/utils/update-rollout-report.d.ts +14 -0
  169. package/dist/utils/update-rollout-report.d.ts.map +1 -1
  170. package/dist/utils/update-rollout-report.js +1 -0
  171. package/dist/utils/update-rollout-report.js.map +1 -1
  172. package/dist/utils/workflow-ownership.d.ts +1 -1
  173. package/dist/utils/workflow-ownership.js +4 -4
  174. package/dist/utils/workflow-ownership.js.map +1 -1
  175. package/package.json +5 -4
  176. package/scripts/postinstall.js +6 -7
  177. package/dist/commands/campaign/canvas-render.d.ts +0 -68
  178. package/dist/commands/campaign/canvas-render.d.ts.map +0 -1
  179. package/dist/commands/campaign/canvas-render.js +0 -414
  180. package/dist/commands/campaign/canvas-render.js.map +0 -1
  181. package/dist/commands/campaign/compose.d.ts +0 -15
  182. package/dist/commands/campaign/compose.d.ts.map +0 -1
  183. package/dist/commands/campaign/compose.js +0 -110
  184. package/dist/commands/campaign/compose.js.map +0 -1
  185. package/dist/commands/campaign/index.d.ts +0 -13
  186. package/dist/commands/campaign/index.d.ts.map +0 -1
  187. package/dist/commands/campaign/index.js +0 -134
  188. package/dist/commands/campaign/index.js.map +0 -1
  189. package/dist/commands/campaign/kind-map.d.ts +0 -6
  190. package/dist/commands/campaign/kind-map.d.ts.map +0 -1
  191. package/dist/commands/campaign/kind-map.js +0 -21
  192. package/dist/commands/campaign/kind-map.js.map +0 -1
  193. package/dist/commands/campaign/mcp-server.d.ts +0 -6
  194. package/dist/commands/campaign/mcp-server.d.ts.map +0 -1
  195. package/dist/commands/campaign/mcp-server.js +0 -167
  196. package/dist/commands/campaign/mcp-server.js.map +0 -1
  197. package/dist/commands/campaign/render.d.ts +0 -5
  198. package/dist/commands/campaign/render.d.ts.map +0 -1
  199. package/dist/commands/campaign/render.js +0 -103
  200. package/dist/commands/campaign/render.js.map +0 -1
  201. package/dist/commands/campaign/trellis-load.d.ts +0 -13
  202. package/dist/commands/campaign/trellis-load.d.ts.map +0 -1
  203. package/dist/commands/campaign/trellis-load.js +0 -122
  204. package/dist/commands/campaign/trellis-load.js.map +0 -1
  205. package/dist/commands/campaign/types.d.ts +0 -61
  206. package/dist/commands/campaign/types.d.ts.map +0 -1
  207. package/dist/commands/campaign/types.js +0 -3
  208. package/dist/commands/campaign/types.js.map +0 -1
  209. package/dist/commands/goal/index.d.ts +0 -3
  210. package/dist/commands/goal/index.d.ts.map +0 -1
  211. package/dist/commands/goal/index.js +0 -159
  212. package/dist/commands/goal/index.js.map +0 -1
  213. package/dist/commands/goal/review.d.ts +0 -6
  214. package/dist/commands/goal/review.d.ts.map +0 -1
  215. package/dist/commands/goal/review.js +0 -28
  216. package/dist/commands/goal/review.js.map +0 -1
  217. package/dist/commands/rpc/client.d.ts +0 -51
  218. package/dist/commands/rpc/client.d.ts.map +0 -1
  219. package/dist/commands/rpc/client.js +0 -147
  220. package/dist/commands/rpc/client.js.map +0 -1
  221. package/dist/commands/rpc/index.d.ts +0 -10
  222. package/dist/commands/rpc/index.d.ts.map +0 -1
  223. package/dist/commands/rpc/index.js +0 -157
  224. package/dist/commands/rpc/index.js.map +0 -1
  225. package/dist/commands/rpc/serve.d.ts +0 -23
  226. package/dist/commands/rpc/serve.d.ts.map +0 -1
  227. package/dist/commands/rpc/serve.js +0 -60
  228. package/dist/commands/rpc/serve.js.map +0 -1
  229. package/dist/commands/rpc/smoke.d.ts +0 -16
  230. package/dist/commands/rpc/smoke.d.ts.map +0 -1
  231. package/dist/commands/rpc/smoke.js +0 -92
  232. package/dist/commands/rpc/smoke.js.map +0 -1
  233. package/dist/commands/sdk/index.d.ts +0 -9
  234. package/dist/commands/sdk/index.d.ts.map +0 -1
  235. package/dist/commands/sdk/index.js +0 -58
  236. package/dist/commands/sdk/index.js.map +0 -1
  237. package/dist/commands/sdk/run.d.ts +0 -42
  238. package/dist/commands/sdk/run.d.ts.map +0 -1
  239. package/dist/commands/sdk/run.js +0 -220
  240. package/dist/commands/sdk/run.js.map +0 -1
  241. package/dist/commands/sdk/status.d.ts +0 -14
  242. package/dist/commands/sdk/status.d.ts.map +0 -1
  243. package/dist/commands/sdk/status.js +0 -69
  244. package/dist/commands/sdk/status.js.map +0 -1
  245. package/dist/configurators/cursor2plus-local.d.ts +0 -6
  246. package/dist/configurators/cursor2plus-local.d.ts.map +0 -1
  247. package/dist/configurators/cursor2plus-local.js +0 -21
  248. package/dist/configurators/cursor2plus-local.js.map +0 -1
  249. package/dist/goal/action-packet.d.ts +0 -89
  250. package/dist/goal/action-packet.d.ts.map +0 -1
  251. package/dist/goal/action-packet.js +0 -113
  252. package/dist/goal/action-packet.js.map +0 -1
  253. package/dist/goal/audit.d.ts +0 -14
  254. package/dist/goal/audit.d.ts.map +0 -1
  255. package/dist/goal/audit.js +0 -12
  256. package/dist/goal/audit.js.map +0 -1
  257. package/dist/goal/constants.d.ts +0 -26
  258. package/dist/goal/constants.d.ts.map +0 -1
  259. package/dist/goal/constants.js +0 -24
  260. package/dist/goal/constants.js.map +0 -1
  261. package/dist/goal/paths.d.ts +0 -8
  262. package/dist/goal/paths.d.ts.map +0 -1
  263. package/dist/goal/paths.js +0 -36
  264. package/dist/goal/paths.js.map +0 -1
  265. package/dist/goal/preflight.d.ts +0 -21
  266. package/dist/goal/preflight.d.ts.map +0 -1
  267. package/dist/goal/preflight.js +0 -97
  268. package/dist/goal/preflight.js.map +0 -1
  269. package/dist/goal/review-seam.d.ts +0 -44
  270. package/dist/goal/review-seam.d.ts.map +0 -1
  271. package/dist/goal/review-seam.js +0 -153
  272. package/dist/goal/review-seam.js.map +0 -1
  273. package/dist/goal/reviewer-rules.d.ts +0 -13
  274. package/dist/goal/reviewer-rules.d.ts.map +0 -1
  275. package/dist/goal/reviewer-rules.js +0 -153
  276. package/dist/goal/reviewer-rules.js.map +0 -1
  277. package/dist/goal/reviewer.d.ts +0 -9
  278. package/dist/goal/reviewer.d.ts.map +0 -1
  279. package/dist/goal/reviewer.js +0 -54
  280. package/dist/goal/reviewer.js.map +0 -1
  281. package/dist/goal/runtime.d.ts +0 -32
  282. package/dist/goal/runtime.d.ts.map +0 -1
  283. package/dist/goal/runtime.js +0 -242
  284. package/dist/goal/runtime.js.map +0 -1
  285. package/dist/goal/sdk-client.d.ts +0 -10
  286. package/dist/goal/sdk-client.d.ts.map +0 -1
  287. package/dist/goal/sdk-client.js +0 -34
  288. package/dist/goal/sdk-client.js.map +0 -1
  289. package/dist/goal/state.d.ts +0 -40
  290. package/dist/goal/state.d.ts.map +0 -1
  291. package/dist/goal/state.js +0 -37
  292. package/dist/goal/state.js.map +0 -1
  293. package/dist/goal/task-triggers.d.ts +0 -15
  294. package/dist/goal/task-triggers.d.ts.map +0 -1
  295. package/dist/goal/task-triggers.js +0 -23
  296. package/dist/goal/task-triggers.js.map +0 -1
  297. package/dist/goal/walls.d.ts +0 -12
  298. package/dist/goal/walls.d.ts.map +0 -1
  299. package/dist/goal/walls.js +0 -65
  300. package/dist/goal/walls.js.map +0 -1
  301. package/dist/goal/worker-prompt.d.ts +0 -13
  302. package/dist/goal/worker-prompt.d.ts.map +0 -1
  303. package/dist/goal/worker-prompt.js +0 -41
  304. package/dist/goal/worker-prompt.js.map +0 -1
  305. package/dist/goal/worker.d.ts +0 -42
  306. package/dist/goal/worker.d.ts.map +0 -1
  307. package/dist/goal/worker.js +0 -180
  308. package/dist/goal/worker.js.map +0 -1
  309. package/dist/templates/common/bundled-skills/cstl-cursor2plus-setup/SKILL.md +0 -63
  310. package/dist/templates/cursor/commands/cursor2plus-setup.md +0 -82
  311. package/dist/templates/cursor/rules/cstl-cursor-modes.mdc +0 -32
  312. package/dist/templates/cursor/rules/cstl-session-rename.mdc +0 -31
  313. package/dist/templates/cursor/rules/cstl-subagent-dispatch.mdc +0 -34
  314. package/dist/templates/cursor/rules/cstl-triage.mdc +0 -36
  315. package/dist/templates/cursor/rules/retrieval-routing.mdc +0 -78
  316. package/dist/templates/markdown/spec/guides/cursor-trellis-release-coexistence-guide.md.txt +0 -166
  317. package/dist/templates/markdown/spec/guides/goal-release-regression-runbook.md.txt +0 -132
  318. package/dist/templates/trellis/local/README.md +0 -85
  319. package/dist/templates/trellis/local/config.local.json.example +0 -6
  320. package/dist/templates/trellis/local/index.d.ts +0 -8
  321. package/dist/templates/trellis/local/index.d.ts.map +0 -1
  322. package/dist/templates/trellis/local/index.js +0 -15
  323. package/dist/templates/trellis/local/index.js.map +0 -1
  324. package/dist/templates/trellis/local/patch_wpelc8.py +0 -421
  325. package/dist/templates/trellis/local/smoke.py +0 -94
  326. package/dist/templates/trellis/local/subagent-models.json.example +0 -4
  327. package/dist/templates/trellis/local/trellis-task-models.json.example +0 -10
  328. package/dist/templates/trellis/local/trellis-task-models.json5.example +0 -45
  329. package/dist/templates/trellis/local/trellis_task_models_config.py +0 -200
  330. package/dist/utils/cursor-sdk-gate.d.ts +0 -10
  331. package/dist/utils/cursor-sdk-gate.d.ts.map +0 -1
  332. package/dist/utils/cursor-sdk-gate.js +0 -32
  333. package/dist/utils/cursor-sdk-gate.js.map +0 -1
  334. /package/dist/templates/markdown/{spec/guides → framework}/artifact-locale-guide.md.txt +0 -0
  335. /package/dist/templates/markdown/{spec/guides → framework}/cursor-context-injection-guide.md.txt +0 -0
  336. /package/dist/templates/markdown/{spec/guides → framework}/cursor-semantic-compliance.md.txt +0 -0
  337. /package/dist/templates/markdown/{spec/guides → framework}/injection-budget-guide.md.txt +0 -0
@@ -0,0 +1,367 @@
1
+ """JSON CLI bridge to the TypeScript Kernel (P30 Stage 2 strangler).
2
+
3
+ Python create / start-execution / record-gate / archive / patch submit a
4
+ Kernel Command. `task.json` status, meta, and gate extras are a same-command
5
+ projection, not a second core-state writer.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import json
11
+ import os
12
+ import shutil
13
+ import subprocess
14
+ import sys
15
+ from pathlib import Path
16
+
17
+ from .log import Colors, colored
18
+
19
+ TASK_RECORD_FIELDS = (
20
+ "id",
21
+ "name",
22
+ "title",
23
+ "description",
24
+ "status",
25
+ "dev_type",
26
+ "scope",
27
+ "package",
28
+ "priority",
29
+ "creator",
30
+ "assignee",
31
+ "createdAt",
32
+ "completedAt",
33
+ "branch",
34
+ "base_branch",
35
+ "worktree_path",
36
+ "commit",
37
+ "pr_url",
38
+ "subtasks",
39
+ "children",
40
+ "parent",
41
+ "relatedFiles",
42
+ "notes",
43
+ "meta",
44
+ )
45
+
46
+ _NULLABLE_STRING = {
47
+ "dev_type",
48
+ "scope",
49
+ "package",
50
+ "completedAt",
51
+ "branch",
52
+ "base_branch",
53
+ "worktree_path",
54
+ "commit",
55
+ "pr_url",
56
+ "parent",
57
+ }
58
+ _STRING_ARRAYS = {"subtasks", "children", "relatedFiles"}
59
+
60
+
61
+ class KernelCliNotFound(RuntimeError):
62
+ """`cstl kernel --json` (or TRELLIS_KERNEL_CLI) is not available."""
63
+
64
+
65
+ class KernelCommandError(RuntimeError):
66
+ """Kernel JSON CLI returned ok:false or could not be parsed."""
67
+
68
+ def __init__(
69
+ self,
70
+ message: str,
71
+ *,
72
+ result: dict | None = None,
73
+ returncode: int | None = None,
74
+ ) -> None:
75
+ super().__init__(message)
76
+ self.result = result
77
+ self.returncode = returncode
78
+
79
+
80
+ def to_kernel_record(task_data: dict) -> dict:
81
+ """Project a Python task dict onto the canonical 24-field Kernel record."""
82
+ record: dict = {}
83
+ for field in TASK_RECORD_FIELDS:
84
+ if field in task_data:
85
+ record[field] = task_data[field]
86
+ continue
87
+ if field in _NULLABLE_STRING:
88
+ record[field] = None
89
+ elif field in _STRING_ARRAYS:
90
+ record[field] = []
91
+ elif field == "meta":
92
+ record[field] = {}
93
+ elif field == "priority":
94
+ record[field] = "P2"
95
+ elif field == "status":
96
+ record[field] = "planning"
97
+ elif field in ("description", "notes", "id", "name", "title", "creator", "assignee", "createdAt"):
98
+ record[field] = ""
99
+ else:
100
+ record[field] = None
101
+ if not isinstance(record["meta"], dict):
102
+ record["meta"] = {}
103
+ else:
104
+ record["meta"] = {
105
+ key: value
106
+ for key, value in record["meta"].items()
107
+ if key not in {"classification", "task_kind", "task_type", "mode"}
108
+ }
109
+ for array_field in _STRING_ARRAYS:
110
+ value = record[array_field]
111
+ if not isinstance(value, list):
112
+ record[array_field] = []
113
+ return record
114
+
115
+
116
+ def kernel_cli_argv() -> list[str]:
117
+ override = os.environ.get("TRELLIS_KERNEL_CLI", "").strip()
118
+ if override:
119
+ if sys.platform == "win32":
120
+ import shlex
121
+
122
+ return shlex.split(override, posix=False)
123
+ import shlex
124
+
125
+ return shlex.split(override)
126
+ cstl = shutil.which("cstl")
127
+ if cstl:
128
+ return [cstl, "kernel", "--json"]
129
+ raise KernelCliNotFound(
130
+ "Kernel CLI not found. Set TRELLIS_KERNEL_CLI or put `cstl` on PATH. "
131
+ "Stage 2 create/start-execution/record-gate/archive/patch require `cstl kernel --json`."
132
+ )
133
+
134
+
135
+ def run_kernel_command(payload: dict, *, cwd: Path | None = None) -> dict:
136
+ """Send one Kernel Command on stdin and parse one JSON object from stdout."""
137
+ argv = kernel_cli_argv()
138
+ encoded = json.dumps(payload, ensure_ascii=False)
139
+ try:
140
+ proc = subprocess.run(
141
+ argv,
142
+ input=encoded,
143
+ capture_output=True,
144
+ text=True,
145
+ encoding="utf-8",
146
+ cwd=str(cwd) if cwd is not None else None,
147
+ check=False,
148
+ )
149
+ except OSError as err:
150
+ raise KernelCommandError(f"failed to invoke Kernel CLI {argv!r}: {err}") from err
151
+
152
+ raw = (proc.stdout or "").strip()
153
+ parsed: dict | None = None
154
+ if raw:
155
+ try:
156
+ parsed = json.loads(raw)
157
+ except json.JSONDecodeError:
158
+ last_line = raw.splitlines()[-1] if raw.splitlines() else raw
159
+ try:
160
+ parsed = json.loads(last_line)
161
+ except json.JSONDecodeError:
162
+ parsed = None
163
+
164
+ if not isinstance(parsed, dict):
165
+ err_tail = (proc.stderr or "").strip()[-800:]
166
+ raise KernelCommandError(
167
+ f"Kernel CLI did not return JSON (exit {proc.returncode}). {err_tail}",
168
+ returncode=proc.returncode,
169
+ )
170
+
171
+ if not parsed.get("ok"):
172
+ error = parsed.get("error") if isinstance(parsed.get("error"), dict) else {}
173
+ code = error.get("code", "UNKNOWN")
174
+ message = error.get("message", "kernel command failed")
175
+ raise KernelCommandError(
176
+ f"Kernel {payload.get('op', 'command')} failed ({code}): {message}",
177
+ result=parsed,
178
+ returncode=proc.returncode,
179
+ )
180
+ return parsed
181
+
182
+
183
+ def kernel_read(task_dir: Path) -> dict:
184
+ return run_kernel_command({"op": "read", "taskDir": str(task_dir)})
185
+
186
+
187
+ def kernel_expected_revision(task_dir: Path) -> int:
188
+ result = kernel_read(task_dir)
189
+ kernel = result.get("kernel")
190
+ if not isinstance(kernel, dict):
191
+ raise KernelCommandError("Kernel read did not include kernel.revision", result=result)
192
+ revision = kernel.get("revision")
193
+ if not isinstance(revision, int) or revision < 0:
194
+ raise KernelCommandError(
195
+ f"Kernel revision is invalid: {revision!r}",
196
+ result=result,
197
+ )
198
+ return revision
199
+
200
+
201
+ def kernel_create(
202
+ task_dir: Path,
203
+ record: dict,
204
+ *,
205
+ actor: str,
206
+ idempotency_key: str,
207
+ evidence: str | None = None,
208
+ extras: dict | None = None,
209
+ ) -> dict:
210
+ payload: dict = {
211
+ "op": "create",
212
+ "taskDir": str(task_dir),
213
+ "actor": actor,
214
+ "idempotencyKey": idempotency_key,
215
+ "record": to_kernel_record(record),
216
+ }
217
+ if extras:
218
+ payload["extras"] = extras
219
+ if evidence:
220
+ payload["evidence"] = evidence
221
+ return run_kernel_command(payload)
222
+
223
+
224
+ def kernel_start(
225
+ task_dir: Path,
226
+ record: dict,
227
+ extras: dict,
228
+ *,
229
+ expected_revision: int,
230
+ actor: str,
231
+ idempotency_key: str,
232
+ evidence: str | None = None,
233
+ ) -> dict:
234
+ payload: dict = {
235
+ "op": "start",
236
+ "taskDir": str(task_dir),
237
+ "expectedRevision": expected_revision,
238
+ "actor": actor,
239
+ "idempotencyKey": idempotency_key,
240
+ "record": to_kernel_record(record),
241
+ "extras": extras,
242
+ }
243
+ if evidence:
244
+ payload["evidence"] = evidence
245
+ return run_kernel_command(payload)
246
+
247
+
248
+ def kernel_record_gate(
249
+ task_dir: Path,
250
+ *,
251
+ expected_revision: int,
252
+ actor: str,
253
+ idempotency_key: str,
254
+ transition: str,
255
+ gate: str,
256
+ record: dict,
257
+ extras: dict,
258
+ evidence: str | None = None,
259
+ ) -> dict:
260
+ payload: dict = {
261
+ "op": "record-gate",
262
+ "taskDir": str(task_dir),
263
+ "expectedRevision": expected_revision,
264
+ "actor": actor,
265
+ "idempotencyKey": idempotency_key,
266
+ "transition": transition,
267
+ "gate": gate,
268
+ "record": record,
269
+ "extras": extras,
270
+ }
271
+ if evidence:
272
+ payload["evidence"] = evidence
273
+ return run_kernel_command(payload)
274
+
275
+
276
+ KERNEL_PROJECTION_EXTRA_KEYS = (
277
+ "quality_gate_results",
278
+ "execution_approval",
279
+ "required_controls",
280
+ "ac_evidence_ledger",
281
+ "independent_check",
282
+ "topology",
283
+ "dependency_graph",
284
+ "ondemand_modules",
285
+ "depends_on",
286
+ "dependency_satisfied",
287
+ "decompose_proposal",
288
+ "profile_health",
289
+ "event_bridge",
290
+ "middleware_providers",
291
+ "capability_router",
292
+ "required_capabilities",
293
+ )
294
+
295
+
296
+ def kernel_projection_extras(task_data: dict) -> dict:
297
+ """Non-canonical task.json fields owned by the same Kernel Command."""
298
+ extras: dict = {}
299
+ for key in KERNEL_PROJECTION_EXTRA_KEYS:
300
+ if key in task_data:
301
+ extras[key] = task_data[key]
302
+ return extras
303
+
304
+
305
+ def kernel_archive(
306
+ task_dir: Path,
307
+ record: dict,
308
+ extras: dict,
309
+ *,
310
+ expected_revision: int,
311
+ actor: str,
312
+ idempotency_key: str,
313
+ evidence: str | None = None,
314
+ ) -> dict:
315
+ payload: dict = {
316
+ "op": "archive",
317
+ "taskDir": str(task_dir),
318
+ "expectedRevision": expected_revision,
319
+ "actor": actor,
320
+ "idempotencyKey": idempotency_key,
321
+ "record": to_kernel_record(record),
322
+ "extras": extras,
323
+ }
324
+ if evidence:
325
+ payload["evidence"] = evidence
326
+ return run_kernel_command(payload)
327
+
328
+
329
+ def kernel_patch(
330
+ task_dir: Path,
331
+ record: dict | None = None,
332
+ extras: dict | None = None,
333
+ *,
334
+ expected_revision: int,
335
+ actor: str,
336
+ idempotency_key: str,
337
+ evidence: str | None = None,
338
+ ) -> dict:
339
+ payload: dict = {
340
+ "op": "patch",
341
+ "taskDir": str(task_dir),
342
+ "expectedRevision": expected_revision,
343
+ "actor": actor,
344
+ "idempotencyKey": idempotency_key,
345
+ }
346
+ if record is not None:
347
+ payload["record"] = to_kernel_record(record)
348
+ if extras is not None:
349
+ payload["extras"] = extras
350
+ if evidence:
351
+ payload["evidence"] = evidence
352
+ return run_kernel_command(payload)
353
+
354
+
355
+ def print_kernel_error(err: Exception) -> None:
356
+ print(colored(f"Error: {err}", Colors.RED), file=sys.stderr)
357
+ if isinstance(err, KernelCommandError) and isinstance(err.result, dict):
358
+ half = err.result.get("halfConversion")
359
+ if isinstance(half, dict) and half.get("kernelPersisted"):
360
+ print(
361
+ colored(
362
+ "Kernel.json was committed but the task.json projection did not finish "
363
+ "(half-conversion). Re-run the same command to recover; do not treat this as success.",
364
+ Colors.YELLOW,
365
+ ),
366
+ file=sys.stderr,
367
+ )
@@ -0,0 +1,280 @@
1
+ #!/usr/bin/env python3
2
+ """Personal Lite context pack (P29 context-progressive / P30 Stage 3).
3
+
4
+ Phase-assembled Definition / Evidence pack with a hard budget. Unactivated
5
+ On-demand modules (Parent / VCS / retrieval-extended) and a full workflow.md
6
+ dump are rejected instead of silently stuffed into the pack.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from typing import Any
12
+
13
+ LITE_PACK_VERSION = 1
14
+ LITE_PACK_SOURCE = "personal-lite-context-pack"
15
+
16
+ LITE_BASELINE_MODULES = (
17
+ "intake-basic",
18
+ "define-basic",
19
+ "approval-personal",
20
+ "execute-agent",
21
+ "verify-basic",
22
+ "close-basic",
23
+ "context-progressive",
24
+ "observability-local",
25
+ )
26
+
27
+ LITE_BLOCKED_ON_DEMAND_MODULES = (
28
+ "parent-child",
29
+ "vcs-integration",
30
+ "personal-memory",
31
+ "retention-storage",
32
+ "retrieval-extended",
33
+ )
34
+
35
+ LITE_RETRIEVAL_INTENTS = ("exact", "semantic", "structural", "external")
36
+
37
+ LITE_DEFAULT_MAX_ITEMS = 8
38
+ LITE_DEFAULT_MAX_ESTIMATED_TOKENS = 4000
39
+ LITE_CHARS_PER_TOKEN = 4
40
+ LITE_MIN_ITEM_TOKENS = 40
41
+
42
+ PHASE_ROLES: dict[str, tuple[str, ...]] = {
43
+ "open": ("triage", "retrieval-router"),
44
+ "define": ("triage", "definition", "retrieval-router"),
45
+ "approve": ("triage", "definition", "approval", "retrieval-router"),
46
+ "execute": ("definition", "approval", "retrieval-router"),
47
+ "verify": ("definition", "evidence", "retrieval-router"),
48
+ "integrate": ("definition", "evidence", "retrieval-router"),
49
+ "close": ("definition", "evidence", "outcome", "retrieval-router"),
50
+ }
51
+
52
+ ROLE_MODULE = {
53
+ "triage": "intake-basic",
54
+ "definition": "define-basic",
55
+ "approval": "approval-personal",
56
+ "evidence": "verify-basic",
57
+ "outcome": "close-basic",
58
+ "retrieval-router": "context-progressive",
59
+ }
60
+
61
+ STATUS_TO_PHASE = {
62
+ "planning": "define",
63
+ "in_progress": "execute",
64
+ "review": "verify",
65
+ "completed": "close",
66
+ "done": "close",
67
+ }
68
+
69
+
70
+ class LiteContextPackError(RuntimeError):
71
+ """Lite pack refused to assemble (budget, On-demand, or workflow dump)."""
72
+
73
+ def __init__(self, code: str, message: str) -> None:
74
+ super().__init__(message)
75
+ self.code = code
76
+
77
+
78
+ def kernel_phase_from_legacy_status(status: str | None) -> str:
79
+ """Project user-visible task.json.status onto a Kernel phase name."""
80
+ if not status:
81
+ return "open"
82
+ return STATUS_TO_PHASE.get(status, "open")
83
+
84
+
85
+ def estimate_lite_tokens(text: str) -> int:
86
+ if not text:
87
+ return LITE_MIN_ITEM_TOKENS
88
+ return max(LITE_MIN_ITEM_TOKENS, len(text) // LITE_CHARS_PER_TOKEN + 20)
89
+
90
+
91
+ def _normalize_path(value: object) -> str:
92
+ if not isinstance(value, str) or not value.strip():
93
+ return ""
94
+ return value.replace("\\", "/")
95
+
96
+
97
+ def _is_workflow_dump(role: str, path: str) -> bool:
98
+ if role == "workflow-full":
99
+ return True
100
+ base = path.rsplit("/", 1)[-1]
101
+ return base == "workflow.md"
102
+
103
+
104
+ def _is_blocked(name: str) -> bool:
105
+ return name in LITE_BLOCKED_ON_DEMAND_MODULES
106
+
107
+
108
+ def build_lite_context_pack(
109
+ *,
110
+ phase: str,
111
+ artifacts: list[dict[str, Any]] | None = None,
112
+ activated_modules: list[str] | None = None,
113
+ include_full_workflow: bool = False,
114
+ max_items: int | None = None,
115
+ max_estimated_tokens: int | None = None,
116
+ ) -> dict[str, Any]:
117
+ """Assemble a bounded Lite pack. Raises LiteContextPackError on refuse."""
118
+ if phase not in PHASE_ROLES:
119
+ raise LiteContextPackError("WORKFLOW_DUMP", f"invalid Lite pack phase: {phase}")
120
+ if include_full_workflow:
121
+ raise LiteContextPackError(
122
+ "WORKFLOW_DUMP",
123
+ "Lite context pack refuses a full workflow.md dump",
124
+ )
125
+
126
+ activated = [
127
+ name for name in (activated_modules or []) if isinstance(name, str) and name.strip()
128
+ ]
129
+ for name in activated:
130
+ if _is_blocked(name):
131
+ raise LiteContextPackError(
132
+ "ON_DEMAND_INACTIVE",
133
+ f"Lite pack excludes unactivated On-demand module: {name}",
134
+ )
135
+
136
+ item_cap = LITE_DEFAULT_MAX_ITEMS if max_items is None else max_items
137
+ token_cap = (
138
+ LITE_DEFAULT_MAX_ESTIMATED_TOKENS
139
+ if max_estimated_tokens is None
140
+ else max_estimated_tokens
141
+ )
142
+ allowed_roles = set(PHASE_ROLES[phase])
143
+
144
+ candidates: list[dict[str, Any]] = [
145
+ {
146
+ "role": "retrieval-router",
147
+ "module": "context-progressive",
148
+ "path": "capability://retrieval-router",
149
+ "estimatedTokens": LITE_MIN_ITEM_TOKENS,
150
+ }
151
+ ]
152
+
153
+ for artifact in artifacts or []:
154
+ if not isinstance(artifact, dict):
155
+ continue
156
+ role = str(artifact.get("role") or "")
157
+ path = _normalize_path(artifact.get("path"))
158
+ if _is_workflow_dump(role, path):
159
+ raise LiteContextPackError(
160
+ "WORKFLOW_DUMP",
161
+ "Lite context pack refuses a full workflow.md dump",
162
+ )
163
+ module_name = str(artifact.get("module") or ROLE_MODULE.get(role) or "")
164
+ if _is_blocked(module_name) or _is_blocked(role):
165
+ raise LiteContextPackError(
166
+ "ON_DEMAND_INACTIVE",
167
+ f"Lite pack excludes unactivated On-demand module: {module_name or role}",
168
+ )
169
+ if role not in allowed_roles:
170
+ continue
171
+ candidates.append(
172
+ {
173
+ "role": role,
174
+ "module": module_name or "define-basic",
175
+ "path": path,
176
+ "estimatedTokens": estimate_lite_tokens(str(artifact.get("content") or "")),
177
+ }
178
+ )
179
+
180
+ selected: list[dict[str, Any]] = []
181
+ omitted: list[dict[str, str]] = []
182
+ warnings: list[str] = []
183
+ estimated_tokens = 0
184
+ budget_exceeded = False
185
+
186
+ for item in candidates:
187
+ would_exceed_items = len(selected) >= item_cap
188
+ next_tokens = int(item["estimatedTokens"])
189
+ would_exceed_tokens = estimated_tokens + next_tokens > token_cap
190
+ if would_exceed_items or would_exceed_tokens:
191
+ budget_exceeded = True
192
+ reason = (
193
+ "outside token budget after higher-ranked Lite surfaces"
194
+ if would_exceed_tokens
195
+ else "outside item budget after higher-ranked Lite surfaces"
196
+ )
197
+ omitted.append({"role": item["role"], "path": item["path"], "reason": reason})
198
+ continue
199
+ selected.append(item)
200
+ estimated_tokens += next_tokens
201
+
202
+ if budget_exceeded:
203
+ if any(
204
+ row["path"].endswith("workflow.md") or row["role"] == "workflow-full"
205
+ for row in omitted
206
+ ):
207
+ raise LiteContextPackError(
208
+ "BUDGET_EXCEEDED",
209
+ "Lite context pack over budget; refusing silent full workflow dump",
210
+ )
211
+ warnings.append("budget limits caused Lite pack omission")
212
+
213
+ return {
214
+ "version": LITE_PACK_VERSION,
215
+ "source": LITE_PACK_SOURCE,
216
+ "phase": phase,
217
+ "budget": {
218
+ "maxItems": item_cap,
219
+ "maxEstimatedTokens": token_cap,
220
+ "estimatedTokens": estimated_tokens,
221
+ "itemsUsed": len(selected),
222
+ },
223
+ "selected": selected,
224
+ "omitted": omitted,
225
+ "modules": {
226
+ "baseline": list(LITE_BASELINE_MODULES),
227
+ "activatedOnDemand": activated,
228
+ },
229
+ "retrievalIntents": list(LITE_RETRIEVAL_INTENTS),
230
+ "warnings": warnings,
231
+ }
232
+
233
+
234
+ def collect_lite_artifacts(task_dir) -> list[dict[str, Any]]:
235
+ """Read Definition / Evidence surfaces when present (capped per file)."""
236
+ from pathlib import Path
237
+
238
+ root = Path(task_dir)
239
+ artifacts: list[dict[str, Any]] = []
240
+ for role, name in (("definition", "prd.md"), ("evidence", "verify.md")):
241
+ path = root / name
242
+ if not path.is_file():
243
+ continue
244
+ try:
245
+ content = path.read_text(encoding="utf-8")
246
+ except OSError:
247
+ continue
248
+ artifacts.append(
249
+ {
250
+ "role": role,
251
+ "path": name,
252
+ "content": content[:12_000],
253
+ }
254
+ )
255
+ return artifacts
256
+
257
+
258
+ def get_lite_context_json(repo_root=None) -> dict[str, Any]:
259
+ """Build a Lite pack for the selected task, or an Open-phase empty pack."""
260
+ from .paths import get_repo_root, get_selected_task
261
+ from .tasks import load_task
262
+ from .task_utils import resolve_task_dir
263
+
264
+ root = repo_root or get_repo_root()
265
+ selected = get_selected_task(root)
266
+ phase = "open"
267
+ artifacts: list[dict[str, Any]] = []
268
+ if selected:
269
+ task_dir = resolve_task_dir(selected, root)
270
+ info = load_task(task_dir) if task_dir.is_dir() else None
271
+ status = getattr(info, "status", None) if info is not None else None
272
+ if status is None and task_dir.is_dir():
273
+ from .io import read_json
274
+ from .paths import FILE_TASK_JSON
275
+
276
+ data = read_json(task_dir / FILE_TASK_JSON) or {}
277
+ status = data.get("status") if isinstance(data, dict) else None
278
+ phase = kernel_phase_from_legacy_status(status if isinstance(status, str) else None)
279
+ artifacts = collect_lite_artifacts(task_dir)
280
+ return build_lite_context_pack(phase=phase, artifacts=artifacts)