@blxzer/cursor-trellis 0.4.2 → 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 (315) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/README.md +23 -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 +137 -40
  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 +2 -4
  33. package/dist/configurators/workflow.js.map +1 -1
  34. package/dist/constants/paths.d.ts +9 -0
  35. package/dist/constants/paths.d.ts.map +1 -1
  36. package/dist/constants/paths.js +13 -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-meta/references/platform-files/skills-and-commands.md +2 -4
  40. package/dist/templates/common/bundled-skills/cstl-skill-creator/SKILL.md +10 -7
  41. package/dist/templates/common/bundled-skills/cstl-skill-creator/references/authoring-rules.md +93 -19
  42. package/dist/templates/common/bundled-skills/cstl-skill-creator/references/general-authoring.md +189 -0
  43. package/dist/templates/common/bundled-skills/cstl-skill-creator/references/review-checklist.md +10 -5
  44. package/dist/templates/common/commands/continue.md +5 -4
  45. package/dist/templates/common/index.d.ts +20 -0
  46. package/dist/templates/common/index.d.ts.map +1 -1
  47. package/dist/templates/common/index.js +21 -2
  48. package/dist/templates/common/index.js.map +1 -1
  49. package/dist/templates/common/optional-skills/chrome-cdp/SKILL.md +179 -0
  50. package/dist/templates/common/optional-skills/chrome-cdp/examples/fetch-hook-api-capture.md +149 -0
  51. package/dist/templates/common/optional-skills/chrome-cdp/scripts/cdp.mjs +903 -0
  52. package/dist/templates/cursor/agents/cstl-research.md +1 -1
  53. package/dist/templates/cursor/fixtures/expected-rules.d.ts +1 -1
  54. package/dist/templates/cursor/fixtures/expected-rules.d.ts.map +1 -1
  55. package/dist/templates/cursor/fixtures/expected-rules.js +7 -24
  56. package/dist/templates/cursor/fixtures/expected-rules.js.map +1 -1
  57. package/dist/templates/cursor/hooks.json +4 -0
  58. package/dist/templates/cursor/index.d.ts +1 -1
  59. package/dist/templates/cursor/index.js +1 -1
  60. package/dist/templates/cursor/rules/cstl-bootstrap.mdc +17 -0
  61. package/dist/templates/markdown/agents.md +15 -10
  62. package/dist/templates/markdown/framework/cursor-native-modes-guide.md.txt +12 -9
  63. package/dist/templates/markdown/framework/cursor-subagent-policy.md.txt +62 -126
  64. package/dist/templates/markdown/framework/dogfood-only-surfaces.md.txt +4 -6
  65. package/dist/templates/markdown/framework/execution-strategy.md.txt +2 -0
  66. package/dist/templates/markdown/framework/index.md.txt +4 -0
  67. package/dist/templates/markdown/framework/internal-skills-cursor-reachability.md.txt +4 -3
  68. package/dist/templates/markdown/framework/middleware-protocol.md.txt +83 -0
  69. package/dist/templates/markdown/framework/parallel-first-execution.md.txt +78 -0
  70. package/dist/templates/markdown/framework/release-boundary.md.txt +30 -0
  71. package/dist/templates/markdown/framework/retrieval-daily-guide.md.txt +1 -1
  72. package/dist/templates/markdown/framework/upgrade.md.txt +15 -0
  73. package/dist/templates/markdown/index.d.ts +4 -0
  74. package/dist/templates/markdown/index.d.ts.map +1 -1
  75. package/dist/templates/markdown/index.js +17 -0
  76. package/dist/templates/markdown/index.js.map +1 -1
  77. package/dist/templates/markdown/spec/guides/index.md.txt +0 -6
  78. package/dist/templates/shared-hooks/event-bridge.py +60 -0
  79. package/dist/templates/shared-hooks/index.d.ts +1 -1
  80. package/dist/templates/shared-hooks/index.d.ts.map +1 -1
  81. package/dist/templates/shared-hooks/index.js +1 -0
  82. package/dist/templates/shared-hooks/index.js.map +1 -1
  83. package/dist/templates/shared-hooks/session-start.py +18 -1
  84. package/dist/templates/trellis/CONTEXT.md +63 -3
  85. package/dist/templates/trellis/gitignore.txt +39 -39
  86. package/dist/templates/trellis/index.d.ts +12 -0
  87. package/dist/templates/trellis/index.d.ts.map +1 -1
  88. package/dist/templates/trellis/index.js +73 -15
  89. package/dist/templates/trellis/index.js.map +1 -1
  90. package/dist/templates/trellis/pool/README.md +12 -3
  91. package/dist/templates/trellis/pool/plan.md +24 -5
  92. package/dist/templates/trellis/scripts/common/adapter_middleware.py +158 -0
  93. package/dist/templates/trellis/scripts/common/artifact_locale.py +12 -3
  94. package/dist/templates/trellis/scripts/common/cursor_retrieval_env.py +5 -2
  95. package/dist/templates/trellis/scripts/common/full_quality.py +237 -0
  96. package/dist/templates/trellis/scripts/common/git.py +6 -0
  97. package/dist/templates/trellis/scripts/common/git_context.py +17 -5
  98. package/dist/templates/trellis/scripts/common/kernel_command.py +367 -0
  99. package/dist/templates/trellis/scripts/common/lite_context.py +280 -0
  100. package/dist/templates/trellis/scripts/common/ondemand_topology.py +225 -0
  101. package/dist/templates/trellis/scripts/common/parent_orchestration.py +1 -1
  102. package/dist/templates/trellis/scripts/common/pool_store.py +16 -4
  103. package/dist/templates/trellis/scripts/common/retrieval_agent_instructions.py +2 -2
  104. package/dist/templates/trellis/scripts/common/session_context.py +28 -2
  105. package/dist/templates/trellis/scripts/common/task_gates.py +73 -12
  106. package/dist/templates/trellis/scripts/common/task_map.py +42 -9
  107. package/dist/templates/trellis/scripts/common/task_store.py +286 -56
  108. package/dist/templates/trellis/scripts/common/test_adapter_middleware.py +56 -0
  109. package/dist/templates/trellis/scripts/common/test_depends_mode_block.py +9 -0
  110. package/dist/templates/trellis/scripts/common/test_full_quality.py +197 -0
  111. package/dist/templates/trellis/scripts/common/test_kernel_command.py +326 -0
  112. package/dist/templates/trellis/scripts/common/test_lite_path.py +190 -0
  113. package/dist/templates/trellis/scripts/common/test_ondemand_topology.py +122 -0
  114. package/dist/templates/trellis/scripts/common/test_pool_store.py +17 -0
  115. package/dist/templates/trellis/scripts/common/test_task_store_kernel_patch.py +163 -0
  116. package/dist/templates/trellis/scripts/common/workflow_phase.py +3 -0
  117. package/dist/templates/trellis/scripts/get_context.py +1 -0
  118. package/dist/templates/trellis/scripts/pool.py +5 -0
  119. package/dist/templates/trellis/scripts/task.py +83 -7
  120. package/dist/templates/trellis/workflow.md +100 -18
  121. package/dist/utils/cursor-retrieval-env.d.ts +2 -1
  122. package/dist/utils/cursor-retrieval-env.d.ts.map +1 -1
  123. package/dist/utils/cursor-retrieval-env.js +3 -2
  124. package/dist/utils/cursor-retrieval-env.js.map +1 -1
  125. package/dist/utils/cursor2plus-residue-cleanup.d.ts +38 -0
  126. package/dist/utils/cursor2plus-residue-cleanup.d.ts.map +1 -0
  127. package/dist/utils/cursor2plus-residue-cleanup.js +145 -0
  128. package/dist/utils/cursor2plus-residue-cleanup.js.map +1 -0
  129. package/dist/utils/manifest-prune.d.ts.map +1 -1
  130. package/dist/utils/manifest-prune.js +7 -2
  131. package/dist/utils/manifest-prune.js.map +1 -1
  132. package/dist/utils/p36-upgrade.d.ts +62 -0
  133. package/dist/utils/p36-upgrade.d.ts.map +1 -0
  134. package/dist/utils/p36-upgrade.js +160 -0
  135. package/dist/utils/p36-upgrade.js.map +1 -0
  136. package/dist/utils/project-capabilities.d.ts +1 -12
  137. package/dist/utils/project-capabilities.d.ts.map +1 -1
  138. package/dist/utils/project-capabilities.js +3 -100
  139. package/dist/utils/project-capabilities.js.map +1 -1
  140. package/dist/utils/readiness.d.ts +10 -0
  141. package/dist/utils/readiness.d.ts.map +1 -1
  142. package/dist/utils/readiness.js +58 -26
  143. package/dist/utils/readiness.js.map +1 -1
  144. package/dist/utils/retrieval-agent-instructions.js +1 -1
  145. package/dist/utils/retrieval-agent-instructions.js.map +1 -1
  146. package/dist/utils/template-hash.d.ts.map +1 -1
  147. package/dist/utils/template-hash.js +11 -1
  148. package/dist/utils/template-hash.js.map +1 -1
  149. package/dist/utils/uninstall-scrubbers.d.ts.map +1 -1
  150. package/dist/utils/uninstall-scrubbers.js +52 -14
  151. package/dist/utils/uninstall-scrubbers.js.map +1 -1
  152. package/dist/utils/update-rollout-report.d.ts +14 -0
  153. package/dist/utils/update-rollout-report.d.ts.map +1 -1
  154. package/dist/utils/update-rollout-report.js +1 -0
  155. package/dist/utils/update-rollout-report.js.map +1 -1
  156. package/dist/utils/workflow-ownership.d.ts +1 -1
  157. package/dist/utils/workflow-ownership.js +4 -4
  158. package/dist/utils/workflow-ownership.js.map +1 -1
  159. package/package.json +5 -4
  160. package/scripts/postinstall.js +6 -7
  161. package/dist/commands/campaign/canvas-render.d.ts +0 -68
  162. package/dist/commands/campaign/canvas-render.d.ts.map +0 -1
  163. package/dist/commands/campaign/canvas-render.js +0 -414
  164. package/dist/commands/campaign/canvas-render.js.map +0 -1
  165. package/dist/commands/campaign/compose.d.ts +0 -15
  166. package/dist/commands/campaign/compose.d.ts.map +0 -1
  167. package/dist/commands/campaign/compose.js +0 -110
  168. package/dist/commands/campaign/compose.js.map +0 -1
  169. package/dist/commands/campaign/index.d.ts +0 -13
  170. package/dist/commands/campaign/index.d.ts.map +0 -1
  171. package/dist/commands/campaign/index.js +0 -134
  172. package/dist/commands/campaign/index.js.map +0 -1
  173. package/dist/commands/campaign/kind-map.d.ts +0 -6
  174. package/dist/commands/campaign/kind-map.d.ts.map +0 -1
  175. package/dist/commands/campaign/kind-map.js +0 -21
  176. package/dist/commands/campaign/kind-map.js.map +0 -1
  177. package/dist/commands/campaign/mcp-server.d.ts +0 -6
  178. package/dist/commands/campaign/mcp-server.d.ts.map +0 -1
  179. package/dist/commands/campaign/mcp-server.js +0 -167
  180. package/dist/commands/campaign/mcp-server.js.map +0 -1
  181. package/dist/commands/campaign/render.d.ts +0 -5
  182. package/dist/commands/campaign/render.d.ts.map +0 -1
  183. package/dist/commands/campaign/render.js +0 -103
  184. package/dist/commands/campaign/render.js.map +0 -1
  185. package/dist/commands/campaign/trellis-load.d.ts +0 -13
  186. package/dist/commands/campaign/trellis-load.d.ts.map +0 -1
  187. package/dist/commands/campaign/trellis-load.js +0 -122
  188. package/dist/commands/campaign/trellis-load.js.map +0 -1
  189. package/dist/commands/campaign/types.d.ts +0 -61
  190. package/dist/commands/campaign/types.d.ts.map +0 -1
  191. package/dist/commands/campaign/types.js +0 -3
  192. package/dist/commands/campaign/types.js.map +0 -1
  193. package/dist/commands/goal/index.d.ts +0 -3
  194. package/dist/commands/goal/index.d.ts.map +0 -1
  195. package/dist/commands/goal/index.js +0 -159
  196. package/dist/commands/goal/index.js.map +0 -1
  197. package/dist/commands/goal/review.d.ts +0 -6
  198. package/dist/commands/goal/review.d.ts.map +0 -1
  199. package/dist/commands/goal/review.js +0 -28
  200. package/dist/commands/goal/review.js.map +0 -1
  201. package/dist/commands/rpc/client.d.ts +0 -51
  202. package/dist/commands/rpc/client.d.ts.map +0 -1
  203. package/dist/commands/rpc/client.js +0 -147
  204. package/dist/commands/rpc/client.js.map +0 -1
  205. package/dist/commands/rpc/index.d.ts +0 -10
  206. package/dist/commands/rpc/index.d.ts.map +0 -1
  207. package/dist/commands/rpc/index.js +0 -157
  208. package/dist/commands/rpc/index.js.map +0 -1
  209. package/dist/commands/rpc/serve.d.ts +0 -23
  210. package/dist/commands/rpc/serve.d.ts.map +0 -1
  211. package/dist/commands/rpc/serve.js +0 -60
  212. package/dist/commands/rpc/serve.js.map +0 -1
  213. package/dist/commands/rpc/smoke.d.ts +0 -16
  214. package/dist/commands/rpc/smoke.d.ts.map +0 -1
  215. package/dist/commands/rpc/smoke.js +0 -92
  216. package/dist/commands/rpc/smoke.js.map +0 -1
  217. package/dist/commands/sdk/index.d.ts +0 -9
  218. package/dist/commands/sdk/index.d.ts.map +0 -1
  219. package/dist/commands/sdk/index.js +0 -58
  220. package/dist/commands/sdk/index.js.map +0 -1
  221. package/dist/commands/sdk/run.d.ts +0 -42
  222. package/dist/commands/sdk/run.d.ts.map +0 -1
  223. package/dist/commands/sdk/run.js +0 -220
  224. package/dist/commands/sdk/run.js.map +0 -1
  225. package/dist/commands/sdk/status.d.ts +0 -14
  226. package/dist/commands/sdk/status.d.ts.map +0 -1
  227. package/dist/commands/sdk/status.js +0 -69
  228. package/dist/commands/sdk/status.js.map +0 -1
  229. package/dist/configurators/cursor2plus-local.d.ts +0 -6
  230. package/dist/configurators/cursor2plus-local.d.ts.map +0 -1
  231. package/dist/configurators/cursor2plus-local.js +0 -21
  232. package/dist/configurators/cursor2plus-local.js.map +0 -1
  233. package/dist/goal/action-packet.d.ts +0 -89
  234. package/dist/goal/action-packet.d.ts.map +0 -1
  235. package/dist/goal/action-packet.js +0 -113
  236. package/dist/goal/action-packet.js.map +0 -1
  237. package/dist/goal/audit.d.ts +0 -14
  238. package/dist/goal/audit.d.ts.map +0 -1
  239. package/dist/goal/audit.js +0 -12
  240. package/dist/goal/audit.js.map +0 -1
  241. package/dist/goal/constants.d.ts +0 -26
  242. package/dist/goal/constants.d.ts.map +0 -1
  243. package/dist/goal/constants.js +0 -24
  244. package/dist/goal/constants.js.map +0 -1
  245. package/dist/goal/paths.d.ts +0 -8
  246. package/dist/goal/paths.d.ts.map +0 -1
  247. package/dist/goal/paths.js +0 -36
  248. package/dist/goal/paths.js.map +0 -1
  249. package/dist/goal/preflight.d.ts +0 -21
  250. package/dist/goal/preflight.d.ts.map +0 -1
  251. package/dist/goal/preflight.js +0 -97
  252. package/dist/goal/preflight.js.map +0 -1
  253. package/dist/goal/review-seam.d.ts +0 -44
  254. package/dist/goal/review-seam.d.ts.map +0 -1
  255. package/dist/goal/review-seam.js +0 -153
  256. package/dist/goal/review-seam.js.map +0 -1
  257. package/dist/goal/reviewer-rules.d.ts +0 -13
  258. package/dist/goal/reviewer-rules.d.ts.map +0 -1
  259. package/dist/goal/reviewer-rules.js +0 -153
  260. package/dist/goal/reviewer-rules.js.map +0 -1
  261. package/dist/goal/reviewer.d.ts +0 -9
  262. package/dist/goal/reviewer.d.ts.map +0 -1
  263. package/dist/goal/reviewer.js +0 -54
  264. package/dist/goal/reviewer.js.map +0 -1
  265. package/dist/goal/runtime.d.ts +0 -32
  266. package/dist/goal/runtime.d.ts.map +0 -1
  267. package/dist/goal/runtime.js +0 -242
  268. package/dist/goal/runtime.js.map +0 -1
  269. package/dist/goal/sdk-client.d.ts +0 -10
  270. package/dist/goal/sdk-client.d.ts.map +0 -1
  271. package/dist/goal/sdk-client.js +0 -34
  272. package/dist/goal/sdk-client.js.map +0 -1
  273. package/dist/goal/state.d.ts +0 -40
  274. package/dist/goal/state.d.ts.map +0 -1
  275. package/dist/goal/state.js +0 -37
  276. package/dist/goal/state.js.map +0 -1
  277. package/dist/goal/task-triggers.d.ts +0 -15
  278. package/dist/goal/task-triggers.d.ts.map +0 -1
  279. package/dist/goal/task-triggers.js +0 -23
  280. package/dist/goal/task-triggers.js.map +0 -1
  281. package/dist/goal/walls.d.ts +0 -12
  282. package/dist/goal/walls.d.ts.map +0 -1
  283. package/dist/goal/walls.js +0 -65
  284. package/dist/goal/walls.js.map +0 -1
  285. package/dist/goal/worker-prompt.d.ts +0 -13
  286. package/dist/goal/worker-prompt.d.ts.map +0 -1
  287. package/dist/goal/worker-prompt.js +0 -41
  288. package/dist/goal/worker-prompt.js.map +0 -1
  289. package/dist/goal/worker.d.ts +0 -42
  290. package/dist/goal/worker.d.ts.map +0 -1
  291. package/dist/goal/worker.js +0 -180
  292. package/dist/goal/worker.js.map +0 -1
  293. package/dist/templates/common/bundled-skills/cstl-cursor2plus-setup/SKILL.md +0 -63
  294. package/dist/templates/cursor/commands/cursor2plus-setup.md +0 -82
  295. package/dist/templates/cursor/rules/cstl-cursor-modes.mdc +0 -32
  296. package/dist/templates/cursor/rules/cstl-session-rename.mdc +0 -31
  297. package/dist/templates/cursor/rules/cstl-subagent-dispatch.mdc +0 -34
  298. package/dist/templates/cursor/rules/cstl-triage.mdc +0 -36
  299. package/dist/templates/cursor/rules/retrieval-routing.mdc +0 -78
  300. package/dist/templates/trellis/local/README.md +0 -85
  301. package/dist/templates/trellis/local/config.local.json.example +0 -6
  302. package/dist/templates/trellis/local/index.d.ts +0 -8
  303. package/dist/templates/trellis/local/index.d.ts.map +0 -1
  304. package/dist/templates/trellis/local/index.js +0 -15
  305. package/dist/templates/trellis/local/index.js.map +0 -1
  306. package/dist/templates/trellis/local/patch_wpelc8.py +0 -421
  307. package/dist/templates/trellis/local/smoke.py +0 -94
  308. package/dist/templates/trellis/local/subagent-models.json.example +0 -4
  309. package/dist/templates/trellis/local/trellis-task-models.json.example +0 -10
  310. package/dist/templates/trellis/local/trellis-task-models.json5.example +0 -45
  311. package/dist/templates/trellis/local/trellis_task_models_config.py +0 -200
  312. package/dist/utils/cursor-sdk-gate.d.ts +0 -10
  313. package/dist/utils/cursor-sdk-gate.d.ts.map +0 -1
  314. package/dist/utils/cursor-sdk-gate.js +0 -32
  315. package/dist/utils/cursor-sdk-gate.js.map +0 -1
@@ -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)
@@ -0,0 +1,225 @@
1
+ """Stage 5 Topology + typed graph + On-demand (Python projection).
2
+
3
+ Kernel extras remain authoritative. This module renders and validates the
4
+ same contract; it does not schedule workers or write a second graph.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from typing import Any
10
+
11
+ STAGE5_SOURCE = "stage5-ondemand-topology"
12
+ STAGE5_SCHEMA_VERSION = 1
13
+
14
+ STAGE5_ONDEMAND_MODULES = (
15
+ "candidate-pool",
16
+ "define-extended",
17
+ "worker-orchestration",
18
+ "parent-child",
19
+ "debug-recovery",
20
+ "integration-handoff",
21
+ "session-transfer",
22
+ "spec-learning",
23
+ "vcs-integration",
24
+ "personal-memory",
25
+ "retention-storage",
26
+ )
27
+
28
+ ONDEMAND_OWNERS = {
29
+ "integration-handoff": "parent-child",
30
+ "session-transfer": "session-transfer",
31
+ }
32
+
33
+ LITE_DORMANT_ONDEMAND = (
34
+ "parent-child",
35
+ "vcs-integration",
36
+ "personal-memory",
37
+ "retention-storage",
38
+ )
39
+
40
+
41
+ class TopologyError(RuntimeError):
42
+ """Topology / On-demand contract rejected a request."""
43
+
44
+
45
+ def capture_candidate(summary: str) -> dict[str, Any]:
46
+ return {
47
+ "kind": "candidate",
48
+ "summary": (summary or "").strip(),
49
+ "task_created": False,
50
+ }
51
+
52
+
53
+ def propose_decompose(parent_id: str, slices: list[str]) -> dict[str, Any]:
54
+ return {
55
+ "parent_id": parent_id,
56
+ "slices": _unique(slices),
57
+ "confirmed": False,
58
+ "children_created": [],
59
+ }
60
+
61
+
62
+ def confirm_decompose(proposal: dict[str, Any]) -> dict[str, Any]:
63
+ slices = _unique(proposal.get("slices") or [])
64
+ return {
65
+ **proposal,
66
+ "confirmed": True,
67
+ "children_created": slices,
68
+ }
69
+
70
+
71
+ def apply_retention(outcome: str) -> str:
72
+ return outcome
73
+
74
+
75
+ def default_topology(
76
+ parent: str | None = None,
77
+ children: list[str] | None = None,
78
+ ) -> dict[str, Any]:
79
+ """Seed Topology. ``parent_id`` + empty children stays ``single``."""
80
+ kids = _unique(children or [])
81
+ parent_id = (parent or "").strip() or None
82
+ return {
83
+ "schema_version": STAGE5_SCHEMA_VERSION,
84
+ "kind": "parent-child" if kids else "single",
85
+ "parent_id": parent_id,
86
+ "children": kids,
87
+ }
88
+
89
+
90
+ def assign_parent(topology: dict[str, Any], parent_id: str) -> dict[str, Any]:
91
+ current = topology.get("parent_id")
92
+ next_parent = (parent_id or "").strip()
93
+ if not next_parent:
94
+ raise TopologyError("parent_id must be a non-empty string")
95
+ if current and current != next_parent:
96
+ raise TopologyError(
97
+ f"single-parent tree: second parent rejected ({current} vs {next_parent})"
98
+ )
99
+ children = list(topology.get("children") or [])
100
+ return {
101
+ "schema_version": STAGE5_SCHEMA_VERSION,
102
+ "kind": "parent-child" if children else "single",
103
+ "parent_id": next_parent,
104
+ "children": children,
105
+ }
106
+
107
+
108
+ def assert_integration_authority(actor_role: str) -> None:
109
+ if actor_role == "parent":
110
+ return
111
+ raise TopologyError(
112
+ "Integration authority stays with Parent; Child cannot integrate-child"
113
+ )
114
+
115
+
116
+ def expand_depends_on_to_requires(
117
+ graph: dict[str, Any],
118
+ from_id: str,
119
+ depends_on: list[str],
120
+ ) -> dict[str, Any]:
121
+ edges = list(graph.get("edges") or [])
122
+ for dep in _unique(depends_on):
123
+ if any(
124
+ edge.get("from") == from_id
125
+ and edge.get("to") == dep
126
+ and edge.get("type") == "requires"
127
+ for edge in edges
128
+ if isinstance(edge, dict)
129
+ ):
130
+ continue
131
+ edges.append({"from": from_id, "to": dep, "type": "requires"})
132
+ return {"schema_version": STAGE5_SCHEMA_VERSION, "edges": edges}
133
+
134
+
135
+ def unmet_requires(
136
+ graph: dict[str, Any],
137
+ satisfied: list[str],
138
+ from_id: str | None = None,
139
+ ) -> list[str]:
140
+ done = set(satisfied)
141
+ missing: list[str] = []
142
+ for edge in graph.get("edges") or []:
143
+ if not isinstance(edge, dict) or edge.get("type") != "requires":
144
+ continue
145
+ if from_id and edge.get("from") != from_id:
146
+ continue
147
+ target = edge.get("to")
148
+ if isinstance(target, str) and target not in done and target not in missing:
149
+ missing.append(target)
150
+ return missing
151
+
152
+
153
+ def project_task_map_graph(
154
+ topology: dict[str, Any],
155
+ graph: dict[str, Any],
156
+ ) -> dict[str, Any]:
157
+ children = []
158
+ for child_id in topology.get("children") or []:
159
+ if not isinstance(child_id, str):
160
+ continue
161
+ depends = [
162
+ edge.get("to")
163
+ for edge in graph.get("edges") or []
164
+ if isinstance(edge, dict)
165
+ and edge.get("type") == "requires"
166
+ and edge.get("from") == child_id
167
+ ]
168
+ children.append({"id": child_id, "depends_on": depends})
169
+ return {
170
+ "graph_authority": "kernel-extras",
171
+ "topology_kind": topology.get("kind") or "single",
172
+ "parent_id": topology.get("parent_id"),
173
+ "children": children,
174
+ }
175
+
176
+
177
+ def apply_kernel_graph_projection(data: dict[str, Any], extras: dict[str, Any]) -> None:
178
+ """Stamp Parent task-map frontmatter from Kernel extras. Projection only."""
179
+ topology = extras.get("topology")
180
+ graph = extras.get("dependency_graph")
181
+ if not isinstance(topology, dict) and not isinstance(graph, dict):
182
+ return
183
+ projection = project_task_map_graph(
184
+ topology if isinstance(topology, dict) else {},
185
+ graph if isinstance(graph, dict) else {"edges": []},
186
+ )
187
+ data["graph_authority"] = "kernel-extras"
188
+ data["topology_kind"] = projection["topology_kind"]
189
+ if projection.get("parent_id"):
190
+ data["parent_id"] = projection["parent_id"]
191
+ existing = {
192
+ child.get("id"): child
193
+ for child in data.get("children") or []
194
+ if isinstance(child, dict)
195
+ }
196
+ children = data.setdefault("children", [])
197
+ if not isinstance(children, list):
198
+ data["children"] = []
199
+ children = data["children"]
200
+ for child in projection["children"]:
201
+ current = existing.get(child["id"])
202
+ if current is None:
203
+ current = {"id": child["id"], "state": "open", "depends_on": []}
204
+ children.append(current)
205
+ existing[child["id"]] = current
206
+ current["depends_on"] = child["depends_on"]
207
+
208
+
209
+ def resident_on_demand_modules(extras: dict[str, Any]) -> list[str]:
210
+ modules = extras.get("ondemand_modules")
211
+ if not isinstance(modules, dict):
212
+ return []
213
+ active = modules.get("active") or []
214
+ return [name for name in active if isinstance(name, str)]
215
+
216
+
217
+ def _unique(values: list[Any]) -> list[str]:
218
+ out: list[str] = []
219
+ for value in values:
220
+ if not isinstance(value, str):
221
+ continue
222
+ item = value.strip()
223
+ if item and item not in out:
224
+ out.append(item)
225
+ return out
@@ -271,7 +271,7 @@ def build_child_prompt(
271
271
  f"Selected task: {child_rel}",
272
272
  "",
273
273
  "Delivery mode: subagent (Cursor)",
274
- "- **Default:** Parent session dispatches **Task** with `subagent_type=cstl-implement` and this prompt as the task description (writable sub-agent). Model under Cursor++ BYOK comes from `.cstl/local/cursor2plus/` routing (see `.cstl/framework/cursor-subagent-policy.md` Method 2.5/2.6).",
274
+ "- **Default:** Parent session dispatches **Task** with `subagent_type=cstl-implement` and this prompt as the task description (writable sub-agent). Native Cursor `model:` frontmatter still applies. Cursor++ / cstl-byok are not Native install surfaces.",
275
275
  "- **Exception:** If parent `child-prompts.md` or the user names this child for a **separate writable Agent chat**, open a new Agent session, pick the model manually, paste this prompt — do not use Task from Parent.",
276
276
  "- Parent retains `review-child` / `integrate-child`. Child must not nest further `cstl-research` / `cstl-implement` / `cstl-check` Task dispatches.",
277
277
  f"- Parent orchestration: `execution_topology={topo}`, `merge_limit={merge_lim}`.",
@@ -12,7 +12,7 @@ Frontmatter writes splice a re-serialized header between the raw `---`
12
12
  fences so the body is preserved byte-for-byte (including the blank line
13
13
  after the closing fence and the final newline), and key order stays stable:
14
14
  known keys first (id, title, status, type, locale, created, approved,
15
- linked_tasks), unknown keys keep their original relative order.
15
+ priority, linked_tasks), unknown keys keep their original relative order.
16
16
  """
17
17
 
18
18
  from __future__ import annotations
@@ -22,7 +22,8 @@ from dataclasses import dataclass, field
22
22
  from pathlib import Path
23
23
 
24
24
  from .artifact_search import parse_frontmatter, split_frontmatter
25
- from .io import read_json, write_json
25
+ from .io import read_json
26
+ from .kernel_command import kernel_expected_revision, kernel_patch, kernel_projection_extras
26
27
  from .paths import get_repo_root, get_tasks_dir
27
28
 
28
29
  POOL_STATUSES = frozenset({"inbox", "review", "accepted", "rejected", "rework"})
@@ -39,6 +40,7 @@ KNOWN_FRONTMATTER_KEY_ORDER = (
39
40
  "locale",
40
41
  "created",
41
42
  "approved",
43
+ "priority",
42
44
  LINKED_TASKS_KEY,
43
45
  )
44
46
 
@@ -328,7 +330,7 @@ def _iter_task_dirs(tasks_dir: Path):
328
330
 
329
331
 
330
332
  def _task_meta_with_pool_items(task_dir: Path, item_id: str, *, remove: bool) -> bool:
331
- """Add/remove item_id in task.json meta.pool_items. True if file changed."""
333
+ """Add/remove item_id in task.json meta.pool_items via Kernel patch."""
332
334
  data = read_json(task_dir / "task.json")
333
335
  if not isinstance(data, dict):
334
336
  return False
@@ -346,7 +348,17 @@ def _task_meta_with_pool_items(task_dir: Path, item_id: str, *, remove: bool) ->
346
348
  pool_items.append(item_id)
347
349
  meta[POOL_ITEMS_KEY] = pool_items
348
350
  data[META_KEY] = meta
349
- return bool(write_json(task_dir / "task.json", data))
351
+ action = "unlink" if remove else "link"
352
+ kernel_patch(
353
+ task_dir,
354
+ data,
355
+ kernel_projection_extras(data),
356
+ expected_revision=kernel_expected_revision(task_dir),
357
+ actor="pool.py link" if not remove else "pool.py unlink",
358
+ idempotency_key=f"patch:pool-{action}:{task_dir.name}:{item_id}",
359
+ evidence="pool_store._task_meta_with_pool_items",
360
+ )
361
+ return True
350
362
 
351
363
 
352
364
  def link_item_task(
@@ -312,11 +312,11 @@ def render_agent_instructions(
312
312
  label = (
313
313
  "**语义合规(cursorEnv 未知,保守 BYOK):**"
314
314
  if cursor_env == ENV_UNKNOWN
315
- else "**语义合规(Cursor++ BYOK):**"
315
+ else "**语义合规(BYOK env):**"
316
316
  )
317
317
  if locale != "zh":
318
318
  lines.append(
319
- "**Semantic compliance (Cursor++ BYOK):**"
319
+ "**Semantic compliance (BYOK env):**"
320
320
  if cursor_env != ENV_UNKNOWN
321
321
  else "**Semantic compliance (cursorEnv unknown, conservative BYOK):**"
322
322
  )
@@ -22,7 +22,6 @@ from pathlib import Path
22
22
  from .active_task import resolve_context_key
23
23
  from .artifact_locale import artifact_locale_summary, resolve_artifact_locale
24
24
  from .config import get_git_packages, get_journal_snippet_enabled
25
- from .session_memory import get_latest_journal_summary
26
25
  from .git import run_git
27
26
  from .packages_context import get_packages_section
28
27
  from .tasks import iter_active_tasks, load_task, get_all_statuses, children_progress
@@ -48,7 +47,30 @@ from .paths import (
48
47
  # =============================================================================
49
48
 
50
49
  _PACKAGE_NAME = "@blxzer/cursor-trellis"
51
- _PYTHON_CMD = "python"
50
+
51
+
52
+ def _resolve_python_command() -> str:
53
+ """Pick the platform-appropriate python command.
54
+
55
+ Mirrors test resolvePython() (win32 prefers python, POSIX prefers
56
+ python3) so rendered guidance matches what integration tests assert.
57
+ """
58
+ candidates = ["python", "python3"] if os.name == "nt" else ["python3", "python"]
59
+ for exe in candidates:
60
+ try:
61
+ subprocess.run(
62
+ [exe, "--version"],
63
+ capture_output=True,
64
+ timeout=2,
65
+ check=True,
66
+ )
67
+ return exe
68
+ except (OSError, subprocess.SubprocessError):
69
+ continue
70
+ return "python"
71
+
72
+
73
+ _PYTHON_CMD = _resolve_python_command()
52
74
  _UPDATE_CHECK_TIMEOUT_SECONDS = 1.0
53
75
  _VERSION_RE = re.compile(
54
76
  r"^\s*(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:-([0-9A-Za-z.-]+))?\s*$"
@@ -842,6 +864,10 @@ def _resolve_journal_snippet(
842
864
  ) -> str:
843
865
  if not journal_file or not developer:
844
866
  return ""
867
+ try:
868
+ from .session_memory import get_latest_journal_summary
869
+ except ImportError:
870
+ return ""
845
871
  return get_latest_journal_summary(journal_file, repo_root, developer)
846
872
 
847
873