elanous 0.2.9 → 0.2.11

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 (694) hide show
  1. package/apps/pwa/out/404/index.html +1 -1
  2. package/apps/pwa/out/404.html +1 -1
  3. package/apps/pwa/out/_next/static/Y4zKzL62sakG5momz3SoQ/_buildManifest.js +1 -0
  4. package/apps/pwa/out/_next/static/chunks/1159-0c5ef8bc21cb6afb.js +1 -0
  5. package/apps/pwa/out/_next/static/chunks/1394-556de6d71bbdb0fb.js +1 -0
  6. package/apps/pwa/out/_next/static/chunks/1452.93700483b9d4fc61.js +9 -0
  7. package/apps/pwa/out/_next/static/chunks/{6062-73e68343a9814f6a.js → 1829-b453a1b22d3999bb.js} +1 -1
  8. package/apps/pwa/out/_next/static/chunks/2176-c379e3ebce874151.js +1 -0
  9. package/apps/pwa/out/_next/static/chunks/2310-7a59f043f6db207e.js +1 -0
  10. package/apps/pwa/out/_next/static/chunks/2426.8c6ee0c668d53def.js +1 -0
  11. package/apps/pwa/out/_next/static/chunks/2548-6e316138d347cefe.js +1 -0
  12. package/apps/pwa/out/_next/static/chunks/3170-7ec60a56fa0e4728.js +1 -0
  13. package/apps/pwa/out/_next/static/chunks/3278-ae6ef084769f6195.js +1 -0
  14. package/apps/pwa/out/_next/static/chunks/3454-988a53dac60a1763.js +1 -0
  15. package/apps/pwa/out/_next/static/chunks/3638-aa80b4641ea439db.js +1 -0
  16. package/apps/pwa/out/_next/static/chunks/3735-fe3402553ac1c9c6.js +1 -0
  17. package/apps/pwa/out/_next/static/chunks/3869-a39d1d9921e10d43.js +1 -0
  18. package/apps/pwa/out/_next/static/chunks/390.c373e31b3b9d0c41.js +5 -0
  19. package/apps/pwa/out/_next/static/chunks/3992-2e522a8ac5f2afde.js +25 -0
  20. package/apps/pwa/out/_next/static/chunks/4215-c9d91dd644ec1719.js +1 -0
  21. package/apps/pwa/out/_next/static/chunks/4508-5d0161d1e724d735.js +1 -0
  22. package/apps/pwa/out/_next/static/chunks/4685-c3c1c625b71accd1.js +1 -0
  23. package/apps/pwa/out/_next/static/chunks/4733-623a79345b171e1c.js +1 -0
  24. package/apps/pwa/out/_next/static/chunks/4883.3bd969ea1d3aaaff.js +2 -0
  25. package/apps/pwa/out/_next/static/chunks/507-5acd98bd6dab2d8a.js +1 -0
  26. package/apps/pwa/out/_next/static/chunks/5445-745eb795c5963d79.js +1 -0
  27. package/apps/pwa/out/_next/static/chunks/5532.6899a12958a89b72.js +1 -0
  28. package/apps/pwa/out/_next/static/chunks/5845-e6c003ce8d5fd36a.js +1 -0
  29. package/apps/pwa/out/_next/static/chunks/5978-b27004c677119115.js +1 -0
  30. package/apps/pwa/out/_next/static/chunks/{8046cadf-211ca3c0e00f2b72.js → 5cd7e3a9-404fd6b8d0ecd44c.js} +1 -1
  31. package/apps/pwa/out/_next/static/chunks/6048-4c5c33579bc4ef78.js +1 -0
  32. package/apps/pwa/out/_next/static/chunks/6303-422dbf0f67c72aac.js +1 -0
  33. package/apps/pwa/out/_next/static/chunks/6751-bb3595948b2e311b.js +1 -0
  34. package/apps/pwa/out/_next/static/chunks/7274-19b7279f18b47ab3.js +1 -0
  35. package/apps/pwa/out/_next/static/chunks/{4323.c5dfb6f503304775.js → 7334.a6679d9168f37aa8.js} +1 -1
  36. package/apps/pwa/out/_next/static/chunks/7551-a0e0707b79164ddd.js +1 -0
  37. package/apps/pwa/out/_next/static/chunks/7777-79f845045aa1ed86.js +1 -0
  38. package/apps/pwa/out/_next/static/chunks/79f8be9e-957d68913b590b08.js +1 -0
  39. package/apps/pwa/out/_next/static/chunks/8025-cc24ee5d8f2fb6f8.js +1 -0
  40. package/apps/pwa/out/_next/static/chunks/8039-df92e08fede5de8f.js +1 -0
  41. package/apps/pwa/out/_next/static/chunks/8206-c6684dcde12be721.js +1 -0
  42. package/apps/pwa/out/_next/static/chunks/8421-1c4acfb1395a0edf.js +1 -0
  43. package/apps/pwa/out/_next/static/chunks/8605-ea5bee9dfae512c7.js +1 -0
  44. package/apps/pwa/out/_next/static/chunks/8725.bf46bb1395dd0fa2.js +1 -0
  45. package/apps/pwa/out/_next/static/chunks/8734-252d511cd276c0dc.js +1 -0
  46. package/apps/pwa/out/_next/static/chunks/8834-ca1cb09c1cb1a6bf.js +1 -0
  47. package/apps/pwa/out/_next/static/chunks/{2415.ed6428ff334c7ce2.js → 9269.d2a4795bc6812717.js} +1 -1
  48. package/apps/pwa/out/_next/static/chunks/9538-2816b90923606459.js +1 -0
  49. package/apps/pwa/out/_next/static/chunks/9656-c1540088f5859c80.js +1 -0
  50. package/apps/pwa/out/_next/static/chunks/9906-482c6619b3fdda20.js +1 -0
  51. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-d16993889b125c42.js +1 -0
  52. package/apps/pwa/out/_next/static/chunks/app/approvals/page-1dcdc58fc2de2454.js +1 -0
  53. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-6a061bd97f0b9398.js +1 -0
  54. package/apps/pwa/out/_next/static/chunks/app/board/page-cb95a7432c9ee744.js +1 -0
  55. package/apps/pwa/out/_next/static/chunks/app/botlab/{page-f972fc1c00a62e88.js → page-b12b6d2f8d2d1aea.js} +1 -1
  56. package/apps/pwa/out/_next/static/chunks/app/bots/page-b9efe868bfd36255.js +1 -0
  57. package/apps/pwa/out/_next/static/chunks/app/chat/page-7c97dcdfe2e37a60.js +1 -0
  58. package/apps/pwa/out/_next/static/chunks/app/consult/page-8fd36984f549270a.js +1 -0
  59. package/apps/pwa/out/_next/static/chunks/app/control/page-5721537ca8a3598c.js +1 -0
  60. package/apps/pwa/out/_next/static/chunks/app/design-check/page-b654240a572e4c48.js +1 -0
  61. package/apps/pwa/out/_next/static/chunks/app/editor/page-b9750abc897508c3.js +1 -0
  62. package/apps/pwa/out/_next/static/chunks/app/exec/page-4cd1de371f8ad048.js +1 -0
  63. package/apps/pwa/out/_next/static/chunks/app/field/page-d99f89657828421c.js +1 -0
  64. package/apps/pwa/out/_next/static/chunks/app/healthz/route-d16993889b125c42.js +1 -0
  65. package/apps/pwa/out/_next/static/chunks/app/inside/page-50cb2a0eb15e6c83.js +1 -0
  66. package/apps/pwa/out/_next/static/chunks/app/intake/page-c8b1ccda6ba7ae98.js +1 -0
  67. package/apps/pwa/out/_next/static/chunks/app/layout-d6b1d156fb95e74b.js +1 -0
  68. package/apps/pwa/out/_next/static/chunks/app/live/page-2a9932ddb62d76bd.js +1 -0
  69. package/apps/pwa/out/_next/static/chunks/app/market/page-9688f5b4f1ff950f.js +1 -0
  70. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-6b15dd6456c5df09.js +1 -0
  71. package/apps/pwa/out/_next/static/chunks/app/morning/{page-adfbd2ba9598e59a.js → page-35414e0a007b4082.js} +1 -1
  72. package/apps/pwa/out/_next/static/chunks/app/not-found-e7a529e0507ac86c.js +1 -0
  73. package/apps/pwa/out/_next/static/chunks/app/observatory/page-e001823c35c9dd67.js +1 -0
  74. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-371423d1a573d64b.js +1 -0
  75. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-4b764b125339ac17.js +1 -0
  76. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-cdf8e4b01067bcf1.js +1 -0
  77. package/apps/pwa/out/_next/static/chunks/app/outputs/page-3a24072b35470157.js +1 -0
  78. package/apps/pwa/out/_next/static/chunks/app/page-94c82eb0ae5abb32.js +1 -0
  79. package/apps/pwa/out/_next/static/chunks/app/reflection/page-0be8d4b2ced75ba0.js +1 -0
  80. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-d37cb0d52f9b6bd5.js +1 -0
  81. package/apps/pwa/out/_next/static/chunks/app/sessions/page-686da0df9bf23e5c.js +1 -0
  82. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-4dc614f3ce043e63.js +1 -0
  83. package/apps/pwa/out/_next/static/chunks/app/settings/page-53f80edc6b8dc07c.js +1 -0
  84. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-d02c782fc792e2af.js +1 -0
  85. package/apps/pwa/out/_next/static/chunks/app/setup/layout-d16993889b125c42.js +1 -0
  86. package/apps/pwa/out/_next/static/chunks/app/setup/page-239c17b911b84e72.js +1 -0
  87. package/apps/pwa/out/_next/static/chunks/app/share/page-dc6a79fb003f746c.js +1 -0
  88. package/apps/pwa/out/_next/static/chunks/app/showroom/page-0fffb84cafdc9da6.js +1 -0
  89. package/apps/pwa/out/_next/static/chunks/app/tasks/page-1218753c82d585f6.js +1 -0
  90. package/apps/pwa/out/_next/static/chunks/app/term/page-4b5935e67531efb5.js +1 -0
  91. package/apps/pwa/out/_next/static/chunks/app/today/page-3285bc87caf1eaa5.js +1 -0
  92. package/apps/pwa/out/_next/static/chunks/app/trace/page-ad553b0e06505ecd.js +1 -0
  93. package/apps/pwa/out/_next/static/chunks/app/vault/page-fca096e3e860306f.js +1 -0
  94. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-9ebfa78f0d9885c3.js +1 -0
  95. package/apps/pwa/out/_next/static/chunks/app/workflows/page-3dc5f21f7e39622d.js +1 -0
  96. package/apps/pwa/out/_next/static/chunks/app/workspace/page-c6da2b468454532d.js +1 -0
  97. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-93bc70e36f595414.js +1 -0
  98. package/apps/pwa/out/_next/static/chunks/{2ea96958-e8c7e112de50b067.js → f48170cc-d05d28bf87a43bf4.js} +1 -1
  99. package/apps/pwa/out/_next/static/chunks/framework-172630e6aa5e2d7f.js +1 -0
  100. package/apps/pwa/out/_next/static/chunks/main-app-f0fe2d7477845181.js +1 -0
  101. package/apps/pwa/out/_next/static/chunks/main-fc98ac70b44eccb0.js +1 -0
  102. package/apps/pwa/out/_next/static/chunks/pages/_app-5bd7f10271f72413.js +1 -0
  103. package/apps/pwa/out/_next/static/chunks/pages/_error-97d0d85ea05fef9b.js +1 -0
  104. package/apps/pwa/out/_next/static/chunks/webpack-9e4d02d567b98240.js +1 -0
  105. package/apps/pwa/out/_next/static/css/543f56bcb1f79fca.css +1 -0
  106. package/apps/pwa/out/approvals/index.html +1 -1
  107. package/apps/pwa/out/approvals/index.txt +23 -23
  108. package/apps/pwa/out/autopilot/index.html +1 -1
  109. package/apps/pwa/out/autopilot/index.txt +23 -23
  110. package/apps/pwa/out/board/index.html +1 -1
  111. package/apps/pwa/out/board/index.txt +23 -23
  112. package/apps/pwa/out/botlab/index.html +1 -1
  113. package/apps/pwa/out/botlab/index.txt +22 -22
  114. package/apps/pwa/out/bots/index.html +1 -1
  115. package/apps/pwa/out/bots/index.txt +22 -22
  116. package/apps/pwa/out/chat/index.html +1 -1
  117. package/apps/pwa/out/chat/index.txt +22 -22
  118. package/apps/pwa/out/consult/index.html +1 -1
  119. package/apps/pwa/out/consult/index.txt +23 -23
  120. package/apps/pwa/out/control/index.html +1 -1
  121. package/apps/pwa/out/control/index.txt +23 -23
  122. package/apps/pwa/out/design-check/index.html +1 -1
  123. package/apps/pwa/out/design-check/index.txt +23 -23
  124. package/apps/pwa/out/editor/index.html +1 -1
  125. package/apps/pwa/out/editor/index.txt +23 -23
  126. package/apps/pwa/out/exec/index.html +1 -1
  127. package/apps/pwa/out/exec/index.txt +23 -23
  128. package/apps/pwa/out/field/index.html +1 -1
  129. package/apps/pwa/out/field/index.txt +23 -23
  130. package/apps/pwa/out/healthz +1 -1
  131. package/apps/pwa/out/index.html +1 -1
  132. package/apps/pwa/out/index.txt +22 -22
  133. package/apps/pwa/out/inside/index.html +1 -0
  134. package/apps/pwa/out/inside/index.txt +31 -0
  135. package/apps/pwa/out/intake/index.html +1 -1
  136. package/apps/pwa/out/intake/index.txt +23 -23
  137. package/apps/pwa/out/live/index.html +1 -1
  138. package/apps/pwa/out/live/index.txt +23 -23
  139. package/apps/pwa/out/market/index.html +1 -1
  140. package/apps/pwa/out/market/index.txt +22 -22
  141. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  142. package/apps/pwa/out/missions/placeholder/index.txt +22 -22
  143. package/apps/pwa/out/morning/index.html +1 -1
  144. package/apps/pwa/out/morning/index.txt +23 -23
  145. package/apps/pwa/out/observatory/index.html +1 -1
  146. package/apps/pwa/out/observatory/index.txt +23 -23
  147. package/apps/pwa/out/ops/checklist/index.html +1 -1
  148. package/apps/pwa/out/ops/checklist/index.txt +22 -22
  149. package/apps/pwa/out/ops/release/index.html +1 -1
  150. package/apps/pwa/out/ops/release/index.txt +22 -22
  151. package/apps/pwa/out/ops/seats/index.html +1 -0
  152. package/apps/pwa/out/ops/seats/index.txt +30 -0
  153. package/apps/pwa/out/outputs/index.html +1 -0
  154. package/apps/pwa/out/outputs/index.txt +30 -0
  155. package/apps/pwa/out/reflection/index.html +1 -1
  156. package/apps/pwa/out/reflection/index.txt +23 -23
  157. package/apps/pwa/out/scheduler/index.html +1 -1
  158. package/apps/pwa/out/scheduler/index.txt +23 -23
  159. package/apps/pwa/out/sessions/index.html +1 -1
  160. package/apps/pwa/out/sessions/index.txt +22 -22
  161. package/apps/pwa/out/settings/devices/index.html +1 -1
  162. package/apps/pwa/out/settings/devices/index.txt +23 -23
  163. package/apps/pwa/out/settings/index.html +1 -1
  164. package/apps/pwa/out/settings/index.txt +23 -23
  165. package/apps/pwa/out/setup/done/index.html +1 -1
  166. package/apps/pwa/out/setup/done/index.txt +23 -23
  167. package/apps/pwa/out/setup/index.html +1 -1
  168. package/apps/pwa/out/setup/index.txt +23 -23
  169. package/apps/pwa/out/share/index.html +1 -1
  170. package/apps/pwa/out/share/index.txt +23 -23
  171. package/apps/pwa/out/showroom/index.html +1 -1
  172. package/apps/pwa/out/showroom/index.txt +22 -22
  173. package/apps/pwa/out/tasks/index.html +1 -1
  174. package/apps/pwa/out/tasks/index.txt +23 -23
  175. package/apps/pwa/out/term/index.html +1 -1
  176. package/apps/pwa/out/term/index.txt +23 -24
  177. package/apps/pwa/out/today/index.html +1 -0
  178. package/apps/pwa/out/today/index.txt +33 -0
  179. package/apps/pwa/out/trace/index.html +1 -1
  180. package/apps/pwa/out/trace/index.txt +23 -23
  181. package/apps/pwa/out/vault/index.html +1 -1
  182. package/apps/pwa/out/vault/index.txt +23 -23
  183. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  184. package/apps/pwa/out/workflows/chat-ui/index.txt +23 -23
  185. package/apps/pwa/out/workflows/index.html +1 -1
  186. package/apps/pwa/out/workflows/index.txt +23 -23
  187. package/apps/pwa/out/workspace/index.html +1 -1
  188. package/apps/pwa/out/workspace/index.txt +23 -24
  189. package/apps/pwa/out/worktrees/index.html +1 -1
  190. package/apps/pwa/out/worktrees/index.txt +23 -23
  191. package/graphs/drafts/draft-cleanup.yaml +19 -0
  192. package/graphs/drafts/recipes.yaml +23 -0
  193. package/graphs/field/field-feed.yaml +2 -0
  194. package/graphs/implement-loop.yaml +1 -1
  195. package/graphs/intake/intake-daily.yaml +2 -1
  196. package/graphs/ops/recipes.yaml +3 -0
  197. package/graphs/ops/secret-exposure.yaml +24 -0
  198. package/graphs/release/recipes.yaml +3 -0
  199. package/graphs/release/release-loop.yaml +4 -1
  200. package/graphs/video/explainer-line.yaml +2 -2
  201. package/package.json +2 -1
  202. package/plugins/card-followup/graphs/run-step.ts +48 -13
  203. package/plugins/doc-draft/graphs/doc-draft.yaml +3 -3
  204. package/plugins/doc-draft/graphs/run-step.ts +13 -6
  205. package/plugins/elanous-hwp/README.md +25 -0
  206. package/plugins/elanous-hwp/cli.ts +22 -0
  207. package/plugins/elanous-hwp/converter.ts +237 -0
  208. package/plugins/elanous-hwp/fixtures/fixed-controls.hwpx +0 -0
  209. package/plugins/elanous-hwp/fixtures/fixed-table-controls.hwpx +0 -0
  210. package/plugins/elanous-hwp/fixtures/fixed.hwpx +0 -0
  211. package/plugins/elanous-hwp/plugin.json +16 -0
  212. package/plugins/elanous-hwp/plugin.ts +40 -0
  213. package/scripts/ci-public-export-leak-gate.ts +75 -14
  214. package/scripts/ci-public-leak-gate.ts +44 -9
  215. package/scripts/ci-pwa-build-gate.ts +160 -0
  216. package/scripts/context-hooks/claude-stop.sh +4 -0
  217. package/scripts/context-hooks/emit.ts +24 -0
  218. package/scripts/context-hooks/post-tool-use.sh +4 -0
  219. package/scripts/coord-channel-watch.sh +54 -8
  220. package/scripts/coord-digest.ts +199 -0
  221. package/scripts/coord-post.sh +60 -3
  222. package/scripts/coord-requests-bridge.ts +143 -0
  223. package/scripts/field-feed/lib.ts +4 -0
  224. package/scripts/fleet/node-setup.sh +20 -3
  225. package/scripts/homebrew-formula-bump.ts +69 -0
  226. package/scripts/install.ps1 +68 -9
  227. package/scripts/install.sh +39 -12
  228. package/scripts/lib/pwa-scenarios/scenarios.ts +20 -0
  229. package/scripts/lib/tui-regress-checks.ts +146 -0
  230. package/scripts/marketing/measure-site-numbers.ts +169 -0
  231. package/scripts/measure-gate-shards.ts +309 -0
  232. package/scripts/memory-lifecycle-cycle.ts +2 -2
  233. package/scripts/mission-request-judge.ts +31 -1
  234. package/scripts/morning-report.ts +2 -2
  235. package/scripts/opportunity-scan.ts +1 -1
  236. package/scripts/ops-now-probe.ts +327 -0
  237. package/scripts/publish-internal-dist.ts +11 -1
  238. package/scripts/release-loop/auto-approve-node.ts +21 -4
  239. package/scripts/release-loop/cut-branch.ts +84 -0
  240. package/scripts/release-loop/cutoff.ts +19 -8
  241. package/scripts/release-loop/gate-node.ts +14 -13
  242. package/scripts/release-loop/mac-smoke-node.ts +229 -0
  243. package/scripts/release-loop/node-verdict.ts +3 -3
  244. package/scripts/release-loop/ops-upgrade-node.ts +72 -16
  245. package/scripts/release-loop/release-readiness.ts +9 -0
  246. package/scripts/release-loop/tui-node-checks.ts +42 -0
  247. package/scripts/release-loop/tui-sim-node.ts +32 -8
  248. package/scripts/release-loop/unattended-release.ts +5 -3
  249. package/scripts/release-loop/version-node.ts +112 -13
  250. package/scripts/repo-watch-cycle.ts +1 -1
  251. package/scripts/retro-cycle.ts +2 -2
  252. package/scripts/schedule-health-report.ts +1 -1
  253. package/scripts/signal-digest-cycle.ts +2 -7
  254. package/scripts/signal-digest-photo.ts +11 -0
  255. package/scripts/storyboard/storyboard.ts +5 -161
  256. package/scripts/tech1-public-measure.ts +94 -0
  257. package/scripts/test-diet/lib.ts +30 -1
  258. package/scripts/test-diet/node.ts +83 -8
  259. package/scripts/test-timeout-audit.ts +114 -0
  260. package/scripts/tree-sync-alert.ts +1 -1
  261. package/scripts/tui-regress.ts +71 -0
  262. package/scripts/ux/demo-rehearsal.ts +243 -0
  263. package/scripts/ux/lib/rehearsal-verdict.ts +73 -0
  264. package/scripts/waitlist-daily-digest.ts +1 -1
  265. package/skills/diagram-master/SKILL.md +366 -0
  266. package/skills/diagram-master/references/engines/excalidraw/color-palette.md +67 -0
  267. package/skills/diagram-master/references/engines/excalidraw/element-templates.md +182 -0
  268. package/skills/diagram-master/references/engines/excalidraw/json-schema.md +71 -0
  269. package/skills/diagram-master/references/engines/excalidraw/render_excalidraw.py +189 -0
  270. package/skills/diagram-master/references/engines/excalidraw/render_template.html +57 -0
  271. package/skills/diagram-master/references/engines/matplotlib/matplotlib-patterns.md +235 -0
  272. package/skills/diagram-master/references/engines/matplotlib/render_matplotlib.py +184 -0
  273. package/skills/diagram-master/references/engines/mermaid/domain-templates.md +435 -0
  274. package/skills/diagram-master/references/engines/mermaid/render_mermaid.py +116 -0
  275. package/skills/diagram-master/references/engines/mermaid/syntax-reference.md +2247 -0
  276. package/skills/diagram-master/references/engines/nanobannana/render_nanobannana2.py +168 -0
  277. package/skills/diagram-master/references/engines/svg/color-palette.md +188 -0
  278. package/skills/diagram-master/references/engines/svg/html-base-template.html +332 -0
  279. package/skills/diagram-master/references/engines/svg/interactivity-patterns.md +435 -0
  280. package/skills/diagram-master/references/engines/svg/render_interactive_svg.py +212 -0
  281. package/skills/diagram-master/references/engines/svg/svg-element-templates.md +349 -0
  282. package/skills/diagram-master/references/examples/README.md +69 -0
  283. package/skills/diagram-master/references/examples/ref_grid_architecture.png +0 -0
  284. package/skills/diagram-master/references/examples/ref_layered_flow.png +0 -0
  285. package/skills/diagram-master/references/pyproject.toml +16 -0
  286. package/skills/diagram-master/references/quality-checklist.md +33 -0
  287. package/skills/diagram-master/references/routing-guide.md +164 -0
  288. package/skills/diagram-master/references/uv.lock +1356 -0
  289. package/skills/explainer-video/SKILL.md +4 -0
  290. package/skills/explainer-video/engine/reel.mjs +18 -13
  291. package/skills/explainer-video/engine/reel.sh +24 -5
  292. package/skills/explainer-video/engine/stitch.mjs +145 -0
  293. package/skills/google-workspace/SKILL.md +33 -0
  294. package/skills/google-workspace/scripts/gws-safe.sh +177 -0
  295. package/skills/grill-me/SKILL.md +1 -1
  296. package/skills/lecture-note-digitizer/SKILL.md +926 -0
  297. package/skills/lecture-note-digitizer/references/convert_to_pdf.py +30 -0
  298. package/skills/lecture-note-digitizer/references/extract_slide_diagram.py +73 -0
  299. package/skills/lecture-note-digitizer/references/html-template.md +167 -0
  300. package/skills/lecture-note-digitizer/references/katex-style-guide.md +165 -0
  301. package/skills/omni-crawl/SKILL.md +21 -11
  302. package/skills/omni-crawl/scripts/main.ts +6 -6
  303. package/skills/omni-crawl/src/router.ts +3 -3
  304. package/skills/omni-digest/SKILL.md +17 -9
  305. package/skills/omni-digest/digest.md +2 -2
  306. package/skills/omni-digest/src/fetch.ts +30 -3
  307. package/skills/pitch-deck/SKILL.md +36 -0
  308. package/skills/pitch-deck/template.html +80 -0
  309. package/skills/youtube-master/SKILL.md +163 -0
  310. package/skills/youtube-master/package-lock.json +558 -0
  311. package/skills/youtube-master/package.json +13 -0
  312. package/skills/youtube-master/references/study-note-template.md +124 -0
  313. package/skills/youtube-master/scripts/main.ts +435 -0
  314. package/skills/youtube-master/scripts/transcribe-local.ts +24 -0
  315. package/skills/youtube-master/src/audio.ts +63 -0
  316. package/skills/youtube-master/src/domain-staging.ts +50 -0
  317. package/skills/youtube-master/src/env.ts +36 -0
  318. package/skills/youtube-master/src/obsidian.ts +155 -0
  319. package/skills/youtube-master/src/router.ts +110 -0
  320. package/skills/youtube-master/src/stt.ts +199 -0
  321. package/skills/youtube-master/src/study-note.ts +175 -0
  322. package/skills/youtube-master/src/summarize.ts +358 -0
  323. package/skills/youtube-master/src/transcript.ts +76 -0
  324. package/skills/youtube-master/src/types.ts +91 -0
  325. package/skills/youtube-master/src/util.ts +93 -0
  326. package/skills/youtube-master/src/youtube.ts +70 -0
  327. package/skills/youtube-master/tsconfig.json +13 -0
  328. package/src/acp/codex-approval-adapter.ts +9 -3
  329. package/src/acp/core-turn-bridge.ts +117 -18
  330. package/src/acp/fact-question.ts +11 -0
  331. package/src/acp/server.ts +53 -9
  332. package/src/acp/tool-approval.ts +126 -0
  333. package/src/acp/turn-runner.ts +36 -0
  334. package/src/agent/monad-agent-turn.ts +8 -0
  335. package/src/agent-mission/driver.ts +80 -10
  336. package/src/agent-mission/pty-decision.ts +71 -0
  337. package/src/agent-mission/review-loop.ts +30 -0
  338. package/src/ask-user-question/pending-questions.ts +25 -10
  339. package/src/ask-user-question/types.ts +9 -0
  340. package/src/auth/setup-link-tokens.ts +16 -5
  341. package/src/autopilot/build/integrity-gate.ts +12 -0
  342. package/src/autopilot/build/llm-conflict-merge.ts +16 -3
  343. package/src/autopilot/tool-registry.ts +2 -0
  344. package/src/boot/daemon-prompt-turn.ts +8 -0
  345. package/src/boot/daemon-runtime.ts +9 -3
  346. package/src/boot/daemon-tools/index.ts +8 -0
  347. package/src/boot/daemon-tools/types.ts +2 -0
  348. package/src/boot/daemon-tools/web-search.ts +4 -0
  349. package/src/card-followup/core.ts +109 -0
  350. package/src/chat/ctrl-c-in-input.ts +14 -0
  351. package/src/chat/index.ts +53 -0
  352. package/src/chat/pickers/state.ts +6 -2
  353. package/src/cli/agent-cli.ts +7 -0
  354. package/src/cli/decisions-cli.ts +25 -3
  355. package/src/cli/doctor-bundle-upload.ts +68 -0
  356. package/src/cli/doctor-cli.ts +38 -4
  357. package/src/cli/drive-input-jail.ts +101 -0
  358. package/src/cli/hooks-cli.ts +17 -3
  359. package/src/cli/make-cli.ts +43 -0
  360. package/src/cli/ops-cli.ts +20 -0
  361. package/src/cli/persona-cli.ts +90 -4
  362. package/src/cli/plugin-cli.ts +31 -22
  363. package/src/cli/pod-cli.ts +61 -2
  364. package/src/cli/pr-cli.ts +82 -17
  365. package/src/cli/pty-drive-cli.ts +41 -7
  366. package/src/cli/pty-drive-verdict.ts +40 -0
  367. package/src/cli/pwa-stop.ts +163 -13
  368. package/src/cli/pwa-test.ts +3 -0
  369. package/src/cli/release-cli.ts +80 -24
  370. package/src/cli/self-update.ts +14 -0
  371. package/src/cli/setup-cli.ts +43 -1
  372. package/src/cli/skills-cli.ts +17 -1
  373. package/src/cli/storyboard-cli.ts +37 -0
  374. package/src/cli/test-state-dir-flag.ts +11 -4
  375. package/src/connectors/linear.ts +146 -0
  376. package/src/context-bus/claude-hooks-install.ts +10 -0
  377. package/src/context-bus/context-now-surfaces.ts +48 -0
  378. package/src/context-bus/context-now.ts +104 -0
  379. package/src/context-bus/coord-events.ts +115 -0
  380. package/src/context-bus/external-events.ts +52 -0
  381. package/src/context-bus/long-term-memory.ts +108 -0
  382. package/src/dashboard/dashboard-boot-fixture.ts +23 -0
  383. package/src/dashboard/index.ts +63 -21
  384. package/src/dashboard/slash-runtime/dashboard-handlers.ts +14 -1
  385. package/src/dashboard/slash-runtime/help-from-registry.ts +43 -28
  386. package/src/decisions/decision-cards.ts +58 -3
  387. package/src/decisions/decision-ledger.ts +85 -6
  388. package/src/decisions/decision-linear-projection.ts +157 -0
  389. package/src/decisions/web-push-decision-cards.ts +50 -0
  390. package/src/directive/interpretation-card.ts +36 -0
  391. package/src/discord/sprint21-runtime.ts +5 -1
  392. package/src/discord-self-message.ts +32 -0
  393. package/src/discord-slash-wire.ts +5 -1
  394. package/src/discord.ts +19 -2
  395. package/src/domains/coo-admin-tool.ts +62 -0
  396. package/src/domains/core-tools.ts +13 -4
  397. package/src/domains/elanous-card.ts +29 -0
  398. package/src/domains/ops-facts-tool.ts +76 -0
  399. package/src/domains/outbound-alert.ts +46 -13
  400. package/src/domains/release-tool.ts +168 -0
  401. package/src/domains/telegram-kind-route.ts +65 -9
  402. package/src/exec-requests/default-outputs.ts +25 -0
  403. package/src/exec-requests/planner.ts +106 -28
  404. package/src/exec-requests/runner.ts +123 -12
  405. package/src/exec-requests/store.ts +11 -2
  406. package/src/execution-loop/launch-gate.ts +100 -0
  407. package/src/field/field-media.ts +10 -7
  408. package/src/field/field-reel.ts +19 -5
  409. package/src/field-note/index.ts +201 -0
  410. package/src/field-note/transcribe.ts +248 -0
  411. package/src/git-fs/worktree.ts +45 -6
  412. package/src/graph-kinds/registry.ts +3 -3
  413. package/src/graph-runner/graph-approval-card.ts +113 -0
  414. package/src/graph-runner/graph-cli.ts +189 -4
  415. package/src/graph-runner/graph-grow-llm.ts +37 -0
  416. package/src/graph-runner/graph-grow.ts +47 -0
  417. package/src/graph-runner/graph-notify.ts +25 -3
  418. package/src/graph-runner/runner.ts +453 -35
  419. package/src/grok/agent-search.ts +8 -2
  420. package/src/guardian/secret-exposure-scan.ts +101 -0
  421. package/src/harness/browser-verify.ts +25 -2
  422. package/src/harness/harness-cli-command.ts +478 -42
  423. package/src/harness/harness-incidents-writer.fixture.ts +24 -0
  424. package/src/harness/harness-incidents.ts +129 -0
  425. package/src/harness/harness-pod-dispatch.ts +43 -23
  426. package/src/harness/harness-seams.ts +38 -4
  427. package/src/harness/harness-stop.ts +75 -6
  428. package/src/harness/launch-registry.ts +91 -0
  429. package/src/harness/process-garbage.ts +5 -3
  430. package/src/hitl/clarification-policy.ts +3 -1
  431. package/src/hooks/dispatch.ts +82 -0
  432. package/src/hooks/expose.ts +254 -0
  433. package/src/hooks/github-shadow.ts +108 -0
  434. package/src/hooks/providers.ts +71 -5
  435. package/src/hooks/queue.ts +1 -1
  436. package/src/hooks/receiver.ts +21 -20
  437. package/src/index.ts +206 -49
  438. package/src/intake-plane/author-goals.ts +37 -4
  439. package/src/intake-plane/categorize.ts +18 -46
  440. package/src/intake-plane/check.ts +3 -0
  441. package/src/intake-plane/discord-seat-work.ts +57 -2
  442. package/src/intake-plane/front-route-classifier.ts +33 -39
  443. package/src/intake-plane/persona-answer.ts +30 -0
  444. package/src/intake-plane/runtime-callables.ts +8 -1
  445. package/src/intake-plane/seat-doc-route.ts +170 -6
  446. package/src/intake-plane/telegram-seat-work.ts +80 -4
  447. package/src/intake-plane/telegram-work.ts +1 -0
  448. package/src/intake-plane/wish-folder.ts +67 -0
  449. package/src/intent-gate/gate.ts +30 -10
  450. package/src/live/detail-switch.ts +5 -0
  451. package/src/llm/difficulty.ts +57 -0
  452. package/src/llm/judge-layer.ts +101 -0
  453. package/src/llm/mission-router.ts +33 -19
  454. package/src/loops/contract/examples/cmo-seat.loop.yaml +51 -0
  455. package/src/loops/contract/examples/orchestrator.loop.yaml +57 -0
  456. package/src/loops/contract/examples/steward.loop.yaml +50 -0
  457. package/src/loops/contract/loop-agent.schema.json +138 -0
  458. package/src/loops/contract/validate.ts +64 -0
  459. package/src/loops/registry.ts +20 -6
  460. package/src/market/signed-index.ts +4 -0
  461. package/src/maturity/bot-command-maturity.ts +45 -0
  462. package/src/maturity/cli-maturity.ts +34 -0
  463. package/src/maturity/feature-maturity.ts +120 -1
  464. package/src/maturity/tui-slash-maturity.ts +31 -0
  465. package/src/memory.ts +32 -4
  466. package/src/model-tier/index.ts +2 -2
  467. package/src/model-tier/tier-resolver.ts +20 -20
  468. package/src/model-tier/types.ts +6 -6
  469. package/src/mss/logging/log-store.ts +15 -0
  470. package/src/nexus/api/config-model-tier.ts +5 -4
  471. package/src/nexus/api/exec-requests.ts +33 -3
  472. package/src/nexus/api/fabric-plans.ts +53 -0
  473. package/src/nexus/api/harness-api.ts +5 -1
  474. package/src/nexus/api/health.ts +2 -0
  475. package/src/nexus/api/http-server.ts +89 -1
  476. package/src/nexus/api/inside-events.ts +226 -0
  477. package/src/nexus/api/meta-api.ts +32 -8
  478. package/src/nexus/api/missions.ts +6 -1
  479. package/src/nexus/api/ops-api.ts +10 -1
  480. package/src/nexus/api/ops-seats-sources.ts +37 -7
  481. package/src/nexus/api/ops-seats.ts +49 -6
  482. package/src/nexus/api/outputs-route.ts +44 -0
  483. package/src/nexus/api/seat-requests.ts +132 -10
  484. package/src/nexus/api/sessions-store.ts +42 -0
  485. package/src/nexus/api/setup-codex-login.ts +160 -0
  486. package/src/nexus/api/sub-seat-charters.ts +34 -0
  487. package/src/nexus/api/tabs-mutations.ts +3 -1
  488. package/src/nexus/api/trace.ts +18 -6
  489. package/src/nexus/api/usage-api.ts +102 -0
  490. package/src/nexus/api/workflow-run-event-bridge.ts +1 -0
  491. package/src/nexus/api/workflows.ts +80 -6
  492. package/src/nexus/index.ts +47 -4
  493. package/src/nexus/kinds/webterm.ts +2 -1
  494. package/src/nexus/webterm/pty.ts +6 -2
  495. package/src/onboarding/cli-auth-status.ts +80 -0
  496. package/src/onboarding/first-run.ts +89 -0
  497. package/src/onboarding/lan-address.ts +45 -0
  498. package/src/onboarding/voice-ai.ts +3 -3
  499. package/src/onboarding/web-first.ts +119 -24
  500. package/src/onboarding.ts +4 -0
  501. package/src/ops-now/ops-now-refresh.ts +100 -0
  502. package/src/org/seat-budget.ts +48 -0
  503. package/src/org/stall-escalation.ts +63 -0
  504. package/src/outputs/ledger.ts +120 -0
  505. package/src/persona/persona-todo.ts +79 -0
  506. package/src/persona/presets.ts +116 -2
  507. package/src/persona/team-set.ts +155 -0
  508. package/src/plugins/install/plugin-install.ts +14 -0
  509. package/src/plugins/maker/market-bundle.ts +109 -0
  510. package/src/plugins/maker/plugin-maker.ts +254 -11
  511. package/src/plugins/maker/wizard-generate.ts +103 -0
  512. package/src/pod-lease/admission.ts +150 -0
  513. package/src/pod-lease/dependency-state.ts +85 -0
  514. package/src/pod-lease/host-lease.ts +127 -0
  515. package/src/project/project-store.ts +115 -0
  516. package/src/project/project-suggestion.ts +16 -0
  517. package/src/project/session-context.ts +24 -0
  518. package/src/release-loop/checklist.ts +79 -5
  519. package/src/release-loop/feature-store.ts +159 -24
  520. package/src/release-loop/next-md-merge.ts +58 -0
  521. package/src/release-loop/release-note.ts +59 -4
  522. package/src/release-loop/release-schedule.ts +53 -0
  523. package/src/repl/index.ts +7 -0
  524. package/src/seat-dispatch/ceo-commands.ts +41 -8
  525. package/src/seat-dispatch/ceo-intent.ts +38 -0
  526. package/src/seat-dispatch/seat-ask.ts +130 -0
  527. package/src/seat-dispatch/seat-questions.ts +100 -0
  528. package/src/seat-loop/seat-loop.ts +750 -0
  529. package/src/seat-loop/seat-report.ts +99 -0
  530. package/src/self-dev/decompose.ts +66 -11
  531. package/src/self-dev/dev-cli.ts +4 -1
  532. package/src/self-dev/dev-pipeline.ts +37 -1
  533. package/src/self-dev/draft-sweep.ts +38 -33
  534. package/src/self-dev/draft-triage-rules.ts +8 -5
  535. package/src/self-dev/fabric-plan-core.ts +297 -0
  536. package/src/self-dev/orchestrate.ts +36 -4
  537. package/src/self-dev/release-path-guard.ts +62 -0
  538. package/src/self-dev/run-store.ts +10 -1
  539. package/src/self-dev/shard-aggregate.ts +230 -0
  540. package/src/self-dev/shard-placement.ts +60 -0
  541. package/src/self-dev/shard-plan.ts +48 -0
  542. package/src/self-dev/shard-pod-run.ts +77 -0
  543. package/src/self-dev/shard-run.ts +105 -0
  544. package/src/self-implement/execution-clarification.ts +115 -0
  545. package/src/self-implement/gate-baseline.ts +1 -1
  546. package/src/self-implement/gate-cli.ts +46 -4
  547. package/src/self-implement/gate-scope.ts +37 -7
  548. package/src/self-implement/graph-variant.ts +171 -0
  549. package/src/self-implement/harness-policy-gates.ts +1 -1
  550. package/src/self-implement/headless-elanous-driver.ts +22 -3
  551. package/src/self-implement/host-regate.ts +22 -1
  552. package/src/self-implement/importer-test-index.ts +2 -1
  553. package/src/self-implement/l8-merge-queue.ts +130 -0
  554. package/src/self-implement/orchestrator.ts +279 -12
  555. package/src/self-implement/pipeline-shape.ts +1 -1
  556. package/src/self-implement/route-consumer-test-index.ts +122 -0
  557. package/src/self-implement/run-status-mapping.ts +5 -3
  558. package/src/self-implement/seams.ts +72 -11
  559. package/src/self-implement/test-seams.ts +1 -0
  560. package/src/self-implement/uncommitted-work.ts +59 -0
  561. package/src/session/chat.ts +2 -0
  562. package/src/session/index.ts +45 -13
  563. package/src/shell-env-bootstrap.ts +17 -0
  564. package/src/skills/gift-install.ts +215 -0
  565. package/src/skills/tools/web-search.ts +4 -1
  566. package/src/steward/directive.ts +51 -2
  567. package/src/steward/launch.ts +266 -0
  568. package/src/steward/outcome-notify.ts +115 -0
  569. package/src/steward/steward-cards.ts +25 -11
  570. package/src/steward/triage-plan.ts +32 -4
  571. package/src/steward/triage.ts +129 -13
  572. package/src/steward/working-backwards.ts +64 -0
  573. package/src/storyboard/storyboard.ts +141 -0
  574. package/src/task-cards/card-cli.ts +25 -1
  575. package/src/task-cards/card-store.ts +2 -2
  576. package/src/task-orchestrator/mission.ts +5 -0
  577. package/src/task-orchestrator/store.ts +12 -3
  578. package/src/task-orchestrator/surfaces/pod-command-job.ts +29 -7
  579. package/src/task-orchestrator/surfaces/pod-lease.ts +397 -0
  580. package/src/task-orchestrator/surfaces/pod-pool.ts +127 -26
  581. package/src/task-orchestrator/surfaces/self-implement-pod.ts +214 -40
  582. package/src/task-orchestrator/surfaces/self-implement.ts +15 -2
  583. package/src/task-orchestrator/types.ts +2 -0
  584. package/src/telegram-card-followup.ts +153 -0
  585. package/src/telegram-commands.ts +10 -1
  586. package/src/telegram-project-command.ts +71 -0
  587. package/src/telegram-report.ts +29 -19
  588. package/src/telegram-run.ts +7 -0
  589. package/src/telegram.ts +58 -11
  590. package/src/tool-runtime/self-cognition-runtimes.ts +13 -3
  591. package/src/tool-runtime/types.ts +4 -0
  592. package/src/tui-client/dashboard-session.ts +2 -0
  593. package/src/user-config.ts +216 -27
  594. package/src/version/packed-revision.json +1 -1
  595. package/src/web-push/notify-exec-request.ts +32 -0
  596. package/src/web-search/grok.ts +7 -1
  597. package/src/web-search/index.ts +28 -1
  598. package/src/web-search/provider.ts +4 -0
  599. package/src/web-search/x-intent.ts +5 -0
  600. package/src/workflow-runtime/executor.ts +277 -47
  601. package/src/workflow-runtime/index.ts +1 -0
  602. package/src/workflow-runtime/nodes/subworkflow.ts +60 -0
  603. package/src/workflow-runtime/plugin-kind-node.ts +1 -1
  604. package/src/workflow-runtime/schema.ts +79 -4
  605. package/src/workflow-runtime/types.ts +35 -4
  606. package/apps/pwa/out/_next/static/bFI5EEwg7T6aG88ibX4Zq/_buildManifest.js +0 -1
  607. package/apps/pwa/out/_next/static/chunks/1105-c473ed46272fba79.js +0 -1
  608. package/apps/pwa/out/_next/static/chunks/1201-09613bce624467c0.js +0 -1
  609. package/apps/pwa/out/_next/static/chunks/1377-1efeffbbac0963ff.js +0 -1
  610. package/apps/pwa/out/_next/static/chunks/1470.98a6ab61ccb4131c.js +0 -1
  611. package/apps/pwa/out/_next/static/chunks/216-5acd01cbde4acda7.js +0 -1
  612. package/apps/pwa/out/_next/static/chunks/2330-c2f07246c8788af1.js +0 -1
  613. package/apps/pwa/out/_next/static/chunks/2375-cb6debea56cce4fb.js +0 -1
  614. package/apps/pwa/out/_next/static/chunks/2929-617f1e66a55be184.js +0 -1
  615. package/apps/pwa/out/_next/static/chunks/2977-e38442e86289f1d3.js +0 -1
  616. package/apps/pwa/out/_next/static/chunks/2982-2a2df543be3d2e5a.js +0 -25
  617. package/apps/pwa/out/_next/static/chunks/3403.4bc97b4066945a94.js +0 -5
  618. package/apps/pwa/out/_next/static/chunks/3441-a1dd4acfeda14488.js +0 -1
  619. package/apps/pwa/out/_next/static/chunks/371-4ad6af55dad02ced.js +0 -1
  620. package/apps/pwa/out/_next/static/chunks/4114-3e63117452378807.js +0 -1
  621. package/apps/pwa/out/_next/static/chunks/5080-1613752891fcadb4.js +0 -10
  622. package/apps/pwa/out/_next/static/chunks/5609-012bb3557211f07e.js +0 -1
  623. package/apps/pwa/out/_next/static/chunks/5791-bb82743fa6299562.js +0 -1
  624. package/apps/pwa/out/_next/static/chunks/6090-85a3f05f954e5697.js +0 -1
  625. package/apps/pwa/out/_next/static/chunks/6284-69db415193d5f8bd.js +0 -1
  626. package/apps/pwa/out/_next/static/chunks/6320-baac91d68843a9a6.js +0 -1
  627. package/apps/pwa/out/_next/static/chunks/6416-505ba707cacafbba.js +0 -1
  628. package/apps/pwa/out/_next/static/chunks/6725-68ab7cc0b1faf00d.js +0 -1
  629. package/apps/pwa/out/_next/static/chunks/6998-a654fe5c58271600.js +0 -1
  630. package/apps/pwa/out/_next/static/chunks/7032-f4ef698518fba61c.js +0 -1
  631. package/apps/pwa/out/_next/static/chunks/7647.2fcd30d894d5ee30.js +0 -1
  632. package/apps/pwa/out/_next/static/chunks/7673-2eda1507d38dadb8.js +0 -1
  633. package/apps/pwa/out/_next/static/chunks/7990-e12efba616f46e03.js +0 -1
  634. package/apps/pwa/out/_next/static/chunks/8067-d976b441a104ab74.js +0 -1
  635. package/apps/pwa/out/_next/static/chunks/8142-744998d65d7bb17e.js +0 -1
  636. package/apps/pwa/out/_next/static/chunks/8519-5632377cce994f86.js +0 -1
  637. package/apps/pwa/out/_next/static/chunks/8619-4365e03a2862830f.js +0 -1
  638. package/apps/pwa/out/_next/static/chunks/8630-cdfc61e82c653ffc.js +0 -1
  639. package/apps/pwa/out/_next/static/chunks/8894-e263b332208e3957.js +0 -1
  640. package/apps/pwa/out/_next/static/chunks/920-f7dceb78f1c585ef.js +0 -1
  641. package/apps/pwa/out/_next/static/chunks/9454-139040d37c79dbc9.js +0 -1
  642. package/apps/pwa/out/_next/static/chunks/9651-3cd291a6fa1370d2.js +0 -1
  643. package/apps/pwa/out/_next/static/chunks/9740-8f5ff96c522b5f55.js +0 -1
  644. package/apps/pwa/out/_next/static/chunks/ae787e4c-f2998de7f1ba72a4.js +0 -1
  645. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-859cea1570b6227a.js +0 -1
  646. package/apps/pwa/out/_next/static/chunks/app/approvals/page-fb7c2d458f32fcad.js +0 -1
  647. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-35e2713597dd24d1.js +0 -1
  648. package/apps/pwa/out/_next/static/chunks/app/board/page-918b656f064577ba.js +0 -1
  649. package/apps/pwa/out/_next/static/chunks/app/bots/page-5f30e00693527ebd.js +0 -1
  650. package/apps/pwa/out/_next/static/chunks/app/chat/page-7fbae173b3c103a7.js +0 -1
  651. package/apps/pwa/out/_next/static/chunks/app/consult/page-3b4edcce364062ea.js +0 -1
  652. package/apps/pwa/out/_next/static/chunks/app/control/page-c5794f484c251f44.js +0 -1
  653. package/apps/pwa/out/_next/static/chunks/app/design-check/page-1130d8eefcd87e76.js +0 -1
  654. package/apps/pwa/out/_next/static/chunks/app/editor/page-973996d584f008c1.js +0 -1
  655. package/apps/pwa/out/_next/static/chunks/app/exec/page-016e2bda04eecb1f.js +0 -1
  656. package/apps/pwa/out/_next/static/chunks/app/field/page-b4d6d9a3b56f2940.js +0 -1
  657. package/apps/pwa/out/_next/static/chunks/app/healthz/route-859cea1570b6227a.js +0 -1
  658. package/apps/pwa/out/_next/static/chunks/app/intake/page-ec49a466121233ba.js +0 -1
  659. package/apps/pwa/out/_next/static/chunks/app/layout-f72db356744ceade.js +0 -1
  660. package/apps/pwa/out/_next/static/chunks/app/live/page-a6fd9b50d1b85378.js +0 -1
  661. package/apps/pwa/out/_next/static/chunks/app/market/page-8b0402a46a749a3d.js +0 -1
  662. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-037f83e2a1edc8e2.js +0 -1
  663. package/apps/pwa/out/_next/static/chunks/app/not-found-46fedff57d4bec67.js +0 -1
  664. package/apps/pwa/out/_next/static/chunks/app/observatory/page-0cf318e9dcee2bbf.js +0 -1
  665. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-899b327cb8b6845c.js +0 -1
  666. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-6c1e6ba743a29dfe.js +0 -1
  667. package/apps/pwa/out/_next/static/chunks/app/page-1a8e4807292f6b17.js +0 -1
  668. package/apps/pwa/out/_next/static/chunks/app/reflection/page-28deba6bcb834916.js +0 -1
  669. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-de68e2205794037d.js +0 -1
  670. package/apps/pwa/out/_next/static/chunks/app/sessions/page-c71c33a573230672.js +0 -1
  671. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-0df30e0c7e50148a.js +0 -1
  672. package/apps/pwa/out/_next/static/chunks/app/settings/page-4fc56790fe0d2682.js +0 -1
  673. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-59df2aebb38a7c1e.js +0 -1
  674. package/apps/pwa/out/_next/static/chunks/app/setup/layout-859cea1570b6227a.js +0 -1
  675. package/apps/pwa/out/_next/static/chunks/app/setup/page-4647be5dbcbd0795.js +0 -1
  676. package/apps/pwa/out/_next/static/chunks/app/share/page-e025892bda74b743.js +0 -1
  677. package/apps/pwa/out/_next/static/chunks/app/showroom/page-a0955de73b89f078.js +0 -1
  678. package/apps/pwa/out/_next/static/chunks/app/tasks/page-369846d2921af584.js +0 -1
  679. package/apps/pwa/out/_next/static/chunks/app/term/page-72cbc1c8cdd0f65d.js +0 -1
  680. package/apps/pwa/out/_next/static/chunks/app/trace/page-77b28bfb0996d5ea.js +0 -1
  681. package/apps/pwa/out/_next/static/chunks/app/vault/page-3220c4934f49c6c2.js +0 -1
  682. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-90670589b87abea8.js +0 -1
  683. package/apps/pwa/out/_next/static/chunks/app/workflows/page-7df9243ed021a4cc.js +0 -1
  684. package/apps/pwa/out/_next/static/chunks/app/workspace/page-4498c3853f4f688d.js +0 -1
  685. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-35bedde6d885f08f.js +0 -1
  686. package/apps/pwa/out/_next/static/chunks/framework-ea806bb57f2b8923.js +0 -1
  687. package/apps/pwa/out/_next/static/chunks/main-8d2302399c2c8f87.js +0 -1
  688. package/apps/pwa/out/_next/static/chunks/main-app-6870f30ea99f8346.js +0 -1
  689. package/apps/pwa/out/_next/static/chunks/pages/_app-b10928c60f0b8488.js +0 -1
  690. package/apps/pwa/out/_next/static/chunks/pages/_error-689039227d443fab.js +0 -1
  691. package/apps/pwa/out/_next/static/chunks/webpack-e5bea36f183b25e6.js +0 -1
  692. package/apps/pwa/out/_next/static/css/f92f63a5df0ba229.css +0 -1
  693. package/src/dashboard/turn-typeahead-echo.ts +0 -23
  694. /package/apps/pwa/out/_next/static/{bFI5EEwg7T6aG88ibX4Zq → Y4zKzL62sakG5momz3SoQ}/_ssgManifest.js +0 -0
@@ -5,7 +5,8 @@
5
5
  // elanous release tag --version <x.y.z> --source <commit> [--yes]
6
6
  // elanous release verify [--version <x.y.z>]
7
7
  // elanous release notes --from <ref> [--to <ref>]
8
- // elanous release run --version <x.y.z> [--dry-run] [--json] [--if-ready]
8
+ // elanous release run --version <x.y.z> [--cut-commit <sha>] [--dry-run] [--json] [--if-ready]
9
+ // elanous release cut-branch --version <x.y.z> --base <sha> --pick <sha>... [--dry-run]
9
10
  //
10
11
  // 🩸 계기(2026-09-25 v0.1.0 첫 공개): 손으로 밟은 절차에서 둘을 빠뜨릴 뻔했다 — 공개본 git 커밋(판 커밋이 공개본 커밋이 된다) ·
11
12
  // PWA 빌드(빌드 산출은 공개본에 안 실린다 → 빠뜨리면 «웹 화면 없는 판»). 절차를 명령으로 굳힌다.
@@ -23,10 +24,13 @@ import { effectiveInstanceRoot, releaseLedgerRoot } from '../instance/resolve.js
23
24
  import { getElanousConfigDirOverride } from '../elanous-config-dir.js';
24
25
  import { envLiteral } from '../platform/env-literal.js';
25
26
  import { userConfigPath } from '../user-config.js';
26
- import { addItem, devVersion, listChecklist, removeItem, seedFromRoadmap, setItem, summarizeChecklist, type ChecklistStatus, type ChecklistDisposition, type ChecklistKind } from '../release-loop/checklist.js';
27
+ import { addItem, claimItem, decodeClaimHistoryEntry, devVersion, listChecklist, ownerMatches, parseOwner, parityGap, removeItem, seedFromRoadmap, setItem, summarizeChecklist, type ChecklistStatus, type ChecklistDisposition, type ChecklistKind } from '../release-loop/checklist.js';
27
28
  import * as features from '../release-loop/feature-store.js';
29
+ import { formatSchedule, getSchedule, listSchedules, setSchedule } from '../release-loop/release-schedule.js';
28
30
  import { writeStdoutJson } from './stdout-json.js';
31
+ import { CliUserError } from './cli-user-error.js';
29
32
  import { runUnattendedRelease, type UnattendedReleaseDeps } from '../../scripts/release-loop/unattended-release.js';
33
+ import { cutReleaseBranch } from '../../scripts/release-loop/cut-branch.js';
30
34
  import { releaseReadiness } from '../../scripts/release-loop/release-readiness.js';
31
35
  import { runReleaseIfReady } from './release-run-if-ready.js';
32
36
 
@@ -468,15 +472,23 @@ function checklistCodenames(): Record<string, string> {
468
472
  function checklistVersion(input: string | undefined, codenames: Record<string, string>): string {
469
473
  const v = input ?? devVersion().replace(/-dev\.\d+$/, '');
470
474
  const resolved = isReleaseVersion(v) ? v : Object.entries(codenames).find(([, name]) => name === v)?.[0] ?? v;
471
- if (!isReleaseVersion(resolved)) throw new Error(`체크리스트 판 또는 별칭이 아니다: ${v}`);
475
+ if (!isReleaseVersion(resolved)) throw new CliUserError(`체크리스트 판 또는 별칭이 아니다: ${v}`);
472
476
  return resolved;
473
477
  }
474
478
 
475
- async function checklistOutput(v: string, codenames: Record<string, string>, mode: 'list' | 'status', json?: boolean): Promise<void> {
476
- const data = listChecklist(v);
479
+ async function checklistOutput(v: string, codenames: Record<string, string>, mode: 'list' | 'status', json?: boolean, owner?: string): Promise<void> {
480
+ if (owner !== undefined) parseOwner(owner);
481
+ const snapshot = listChecklist(v);
482
+ const data = owner === undefined ? snapshot : { ...snapshot, items: snapshot.items.filter((item) => ownerMatches(item.owner, owner)) };
477
483
  const summary = summarizeChecklist(data);
478
- const result = { ...data, codename: codenames[v] ?? '', ...summary };
484
+ const parity = data.items.flatMap((item) => {
485
+ const why = item.kind === 'screen' ? parityGap(item.evidence) : null;
486
+ return why ? [{ id: item.id, why }] : [];
487
+ });
488
+ const result = { ...data, codename: codenames[v] ?? '', ...summary, parity };
489
+ for (const { id, why } of parity) debug.log('release.checklist', 'parity-warning', { version: v, id, why });
479
490
  if (json) { await writeStdoutJson(`${JSON.stringify(result)}\n`); return; }
491
+ console.log(`⚠ 짝 경고 ${parity.length}: ${parity.map(({ id }) => id).join(', ') || '없음'}`);
480
492
  console.log(`${v}${result.codename ? ` (${result.codename})` : ''} · 공개 ${data.released || '없음'} · 개발 ${data.dev}`);
481
493
  if (mode === 'list') {
482
494
  for (const item of data.items) console.log(`${({ green: '🟢', yellow: '🟡', red: '🔴', done: '✅' } as const)[item.status]} ${item.id} ${item.title}${item.owner ? ` · ${item.owner}` : ''}`);
@@ -489,6 +501,26 @@ async function checklistOutput(v: string, codenames: Record<string, string>, mod
489
501
 
490
502
  export function registerReleaseCommands(program: Command, releaseRunDeps: UnattendedReleaseDeps = {}): void {
491
503
  const release = program.command('release').description('공개 배포 한 판 — prepare(로컬) → publish(--yes) → verify (docs/manual/MANUAL-versioning-and-release-2026-09-25.md)');
504
+ const schedule = release.command('schedule').description('판별 컷·착지 마감 조회·갱신');
505
+ const scheduleLedgerRoot = () => releaseRunDeps.ledgerRoot ?? releaseLedgerRoot();
506
+ schedule.command('list').option('--json', '결과 JSON').action((o: { json?: boolean }) => {
507
+ const rows = listSchedules(scheduleLedgerRoot());
508
+ if (o.json) console.log(JSON.stringify(rows));
509
+ else for (const row of rows) console.log(formatSchedule(row));
510
+ });
511
+ schedule.command('show').requiredOption('--version <v>', '조회할 판').option('--json', '결과 JSON')
512
+ .action((o: { version: string; json?: boolean }) => {
513
+ const row = getSchedule(o.version, scheduleLedgerRoot());
514
+ if (!row) throw new CliUserError(`없는 판 일정: ${o.version}`, '판 일정 등록: elanous release schedule set --version <v> --cut-at <iso> --land-by <iso>');
515
+ if (o.json) console.log(JSON.stringify(row)); else console.log(formatSchedule(row));
516
+ });
517
+ schedule.command('set').requiredOption('--version <v>', '갱신할 판')
518
+ .option('--cut-at <iso>', '오프셋 포함 컷 시각').option('--land-by <iso>', '오프셋 포함 착지 마감').option('--json', '결과 JSON')
519
+ .action((o: { version: string; cutAt?: string; landBy?: string; json?: boolean }) => {
520
+ if (o.cutAt === undefined && o.landBy === undefined) throw new CliUserError('갱신할 시각을 지정하라', '--cut-at <iso> 또는 --land-by <iso>');
521
+ const row = setSchedule(o.version, { cutAt: o.cutAt, landBy: o.landBy }, process.env.ELANOUS_TRACK || 'cli', scheduleLedgerRoot());
522
+ if (o.json) console.log(JSON.stringify(row)); else console.log(formatSchedule(row));
523
+ });
492
524
  const checklist = release.command('checklist').description('판별 확인표 조회·갱신')
493
525
  .option('--version <v>', '판 또는 별칭(기본: package.json 의 개발판에서 -dev.N 제거)')
494
526
  .option('--json', '결과 JSON');
@@ -498,15 +530,15 @@ export function registerReleaseCommands(program: Command, releaseRunDeps: Unatte
498
530
  return { version: checklistVersion(opts.version, codenames), codenames, json: opts.json };
499
531
  };
500
532
  const withContext = (cmd: Command) => cmd.option('--version <v>', '판 또는 별칭').option('--json', '결과 JSON');
501
- withContext(checklist.command('list').description('모든 칸')).action(async (_opts: unknown, cmd: Command) => {
533
+ withContext(checklist.command('list').description('모든 칸')).option('--owner <owner>', '자리 또는 하위 자리로 거르기').action(async (opts: { owner?: string }, cmd: Command) => {
502
534
  const { version, codenames, json } = context(cmd);
503
- await checklistOutput(version, codenames, 'list', json);
535
+ await checklistOutput(version, codenames, 'list', json, opts.owner);
504
536
  });
505
- withContext(checklist.command('status').description('상태 요약')).action(async (_opts: unknown, cmd: Command) => {
537
+ withContext(checklist.command('status').description('상태 요약')).option('--owner <owner>', '자리 또는 하위 자리로 거르기').action(async (opts: { owner?: string }, cmd: Command) => {
506
538
  const { version, codenames, json } = context(cmd);
507
- await checklistOutput(version, codenames, 'status', json);
539
+ await checklistOutput(version, codenames, 'status', json, opts.owner);
508
540
  });
509
- withContext(checklist.command('add <id> <title>').description('칸 추가')).option('--owner <owner>', '담당').option('--kind <kind>', 'screen = 다섯 화면 짝 칸')
541
+ withContext(checklist.command('add <id> <title>').description('칸 추가')).option('--owner <owner>', '담당').option('--kind <kind>', 'screen = 다섯 화면 짝 칸 · 근거에 짝: PWA … · 데스크톱 … · 폴드 … · 아이폰 … · 아이패드 … 한 줄')
510
542
  .action((id: string, title: string, opts: { owner?: string; kind?: string }, cmd: Command) => {
511
543
  const { version, json } = context(cmd);
512
544
  const data = addItem(version, { id, title, ...(opts.owner !== undefined ? { owner: opts.owner } : {}), ...(opts.kind !== undefined ? { kind: opts.kind as ChecklistKind } : {}) });
@@ -514,15 +546,28 @@ export function registerReleaseCommands(program: Command, releaseRunDeps: Unatte
514
546
  });
515
547
  withContext(checklist.command('set <id>').description('칸 상태·근거·담당·처분 갱신'))
516
548
  .option('--status <status>', 'green|yellow|red|done').option('--evidence <evidence>', '근거').option('--owner <owner>', '담당')
517
- .option('--disposition <disposition>', 'move|known-issue|block').option('--kind <kind>', 'screen = 다섯 화면 짝 칸')
549
+ .option('--disposition <disposition>', 'move|known-issue|block').option('--kind <kind>', 'screen = 다섯 화면 짝 칸 · 근거에 짝: PWA … · 데스크톱 … · 폴드 … · 아이폰 … · 아이패드 … 한 줄')
518
550
  .action((id: string, opts: { status?: string; evidence?: string; owner?: string; disposition?: string; kind?: string }, cmd: Command) => {
519
551
  const { version, json } = context(cmd);
520
- if (opts.status !== undefined && !['green', 'yellow', 'red', 'done'].includes(opts.status)) throw new Error(`잘못된 상태: ${opts.status}`);
521
- if (opts.disposition !== undefined && !['move', 'known-issue', 'block'].includes(opts.disposition)) throw new Error(`잘못된 처분: ${opts.disposition}`);
522
- if (opts.status === undefined && opts.evidence === undefined && opts.owner === undefined && opts.disposition === undefined && opts.kind === undefined) throw new Error('갱신할 칸을 지정하라');
552
+ if (opts.status !== undefined && !['green', 'yellow', 'red', 'done'].includes(opts.status)) throw new CliUserError(`잘못된 상태: ${opts.status}`, 'green|yellow|red|done');
553
+ if (opts.disposition !== undefined && !['move', 'known-issue', 'block'].includes(opts.disposition)) throw new CliUserError(`잘못된 처분: ${opts.disposition}`, 'move|known-issue|block');
554
+ if (opts.status === undefined && opts.evidence === undefined && opts.owner === undefined && opts.disposition === undefined && opts.kind === undefined) throw new CliUserError('갱신할 칸을 지정하라', '--status · --evidence · --owner · --disposition · --kind 중 하나');
523
555
  const data = setItem(version, id, { ...(opts.status !== undefined ? { status: opts.status as ChecklistStatus } : {}), ...(opts.evidence !== undefined ? { evidence: opts.evidence } : {}), ...(opts.owner !== undefined ? { owner: opts.owner } : {}), ...(opts.disposition !== undefined ? { disposition: opts.disposition as ChecklistDisposition } : {}), ...(opts.kind !== undefined ? { kind: opts.kind as ChecklistKind } : {}) }, process.env.ELANOUS_TRACK || 'cli');
556
+ const item = data.items.find((entry) => entry.id === id);
557
+ const why = item?.kind === 'screen' && item.status === 'green' ? parityGap(item.evidence) : null;
558
+ if (why) {
559
+ console.error(`⚠ 짝: ${why} — 근거에 «짝: PWA … · 데스크톱 … · 폴드 … · 아이폰 … · 아이패드 …» 한 줄`);
560
+ debug.log('release.checklist', 'parity-warning', { version, id, why });
561
+ }
524
562
  if (json) console.log(JSON.stringify(data)); else console.log(`✅ ${id} 갱신`);
525
563
  });
564
+ withContext(checklist.command('claim <id>').description('칸의 단일 주인을 잡는다'))
565
+ .requiredOption('--by <owner>', '잡을 자리 또는 하위 자리').option('--force', '기존 주인에서 강제로 바꾼다')
566
+ .action((id: string, opts: { by: string; force?: boolean }, cmd: Command) => {
567
+ const { version, json } = context(cmd);
568
+ const data = claimItem(version, id, opts.by, { force: opts.force });
569
+ if (json) console.log(JSON.stringify(data)); else console.log(`✅ ${id} 주인 ${opts.by}`);
570
+ });
526
571
  withContext(checklist.command('rm <id>').description('칸 삭제')).action((id: string, _opts: unknown, cmd: Command) => {
527
572
  const { version, json } = context(cmd);
528
573
  const data = removeItem(version, id, process.env.ELANOUS_TRACK || 'cli');
@@ -540,8 +585,8 @@ export function registerReleaseCommands(program: Command, releaseRunDeps: Unatte
540
585
  .action((id: string, opts: { from: string; to: string }, cmd: Command) => {
541
586
  const { codenames, json } = context(cmd);
542
587
  const from = checklistVersion(opts.from, codenames), to = checklistVersion(opts.to, codenames);
543
- const data = features.move(id, from, to, actor());
544
- if (json) console.log(JSON.stringify(data)); else console.log(`✅ ${id} ${from} → ${to}`);
588
+ features.move(id, from, to, actor());
589
+ if (json) console.log(JSON.stringify(listChecklist(to))); else console.log(`✅ ${id} ${from} → ${to}`);
545
590
  });
546
591
  withContext(checklist.command('retitle <id> <title>').description('칸 제목 수정')).action((id: string, title: string, _opts: unknown, cmd: Command) => {
547
592
  const { version, json } = context(cmd);
@@ -554,11 +599,11 @@ export function registerReleaseCommands(program: Command, releaseRunDeps: Unatte
554
599
  const { version, json } = context(cmd);
555
600
  const snapshot = listChecklist(version);
556
601
  features.evidenceAdd(id, version, ref, actor(), snapshot.released, snapshot.dev);
557
- if (json) console.log(JSON.stringify(features.history(id))); else console.log(`✅ ${id} 근거 ${ref}`);
602
+ if (json) console.log(JSON.stringify(features.history(id).map(decodeClaimHistoryEntry))); else console.log(`✅ ${id} 근거 ${ref}`);
558
603
  });
559
604
  withContext(checklist.command('history <id>').description('칸의 판 이동·변경 이력')).action((id: string, _opts: unknown, cmd: Command) => {
560
605
  const { json } = context(cmd);
561
- const rows = features.history(id);
606
+ const rows = features.history(id).map(decodeClaimHistoryEntry);
562
607
  if (json) { console.log(JSON.stringify(rows)); return; }
563
608
  const info = features.details(id);
564
609
  if (info) {
@@ -570,8 +615,8 @@ export function registerReleaseCommands(program: Command, releaseRunDeps: Unatte
570
615
  withContext(checklist.command('export').description('SQLite 에서 checklist.json 스냅샷 생성')).action((_opts: unknown, cmd: Command) => {
571
616
  const { version, json } = context(cmd);
572
617
  const snapshot = listChecklist(version);
573
- const data = features.exportJson(version, snapshot.released, snapshot.dev);
574
- if (json) console.log(JSON.stringify(data)); else console.log(`✅ ${version} checklist.json 내보냄`);
618
+ features.exportJson(version, snapshot.released, snapshot.dev);
619
+ if (json) console.log(JSON.stringify(listChecklist(version))); else console.log(`✅ ${version} checklist.json 내보냄`);
575
620
  });
576
621
  release.command('prepare')
577
622
  .description('깨끗한 원본 → 공개본 → 공개 저장소 이력 위 커밋 → PWA → 묶음·체크섬 → 로컬 끝까지 (네트워크 쓰기 없음)')
@@ -632,19 +677,30 @@ export function registerReleaseCommands(program: Command, releaseRunDeps: Unatte
632
677
  const { draftReleaseNotes, readLandedCommits, renderReleaseNotes } = await import('../../scripts/release-notes.js');
633
678
  console.log(renderReleaseNotes(draftReleaseNotes(readLandedCommits(o.from, o.to), o.from, o.to)));
634
679
  });
680
+ release.command('cut-branch')
681
+ .description('원래 컷에 main 의 수리 커밋만 얹은 release/<v> 가지를 만들고 push')
682
+ .requiredOption('--version <v>', '릴리스 판(x.y.z)')
683
+ .requiredOption('--base <sha>', '원래 컷 SHA')
684
+ .requiredOption('--pick <sha...>', '차례로 얹을 main 커밋 SHA')
685
+ .option('--dry-run', '가지·worktree·push 없이 계획만 출력')
686
+ .option('--json', '결과 한 줄 JSON(stdout)')
687
+ .action(async (o: { version: string; base: string; pick: string[]; dryRun?: boolean; json?: boolean }) => {
688
+ await jsonAction(o.json, async (log) => cutReleaseBranch({ ...o, log }), () => true);
689
+ });
635
690
  release.command('run')
636
691
  .description('릴리스 루프 실행 — 원장 직전 판과 release.loop 설정으로 그래프 입력 구성 · --dry-run 은 입력만 출력')
637
692
  .requiredOption('--version <v>', '공개할 판(x.y.z)')
693
+ .option('--cut-commit <sha>', 'origin/main HEAD 대신 컷으로 쓸 커밋')
638
694
  .option('--dry-run', '체크리스트·그래프 실행 없이 전체 입력 보기')
639
695
  .option('--if-ready', '준비되지 않았으면 사유를 출력하고 성공으로 건너뛴다')
640
696
  .option('--json', '결과 한 줄 JSON(stdout) · 사람 줄은 stderr')
641
- .action(async (o: { version: string; dryRun?: boolean; ifReady?: boolean; json?: boolean }) => {
697
+ .action(async (o: { version: string; cutCommit?: string; dryRun?: boolean; ifReady?: boolean; json?: boolean }) => {
642
698
  if (o.ifReady && !o.dryRun) {
643
699
  try {
644
700
  const outcome = await runReleaseIfReady(o.version, {
645
701
  ledgerRoot: releaseRunDeps.ledgerRoot,
646
702
  readiness: (version, options) => releaseReadiness(version, { ledgerRoot: releaseRunDeps.ledgerRoot, checklist: releaseRunDeps.checklist, ...options }),
647
- run: () => runUnattendedRelease({ version: o.version }, releaseRunDeps),
703
+ run: () => runUnattendedRelease({ version: o.version, cutCommit: o.cutCommit }, releaseRunDeps),
648
704
  });
649
705
  if (outcome.skipped) {
650
706
  if (o.json) await writeStdoutJson(`${JSON.stringify(outcome)}\n`);
@@ -664,7 +720,7 @@ export function registerReleaseCommands(program: Command, releaseRunDeps: Unatte
664
720
  return;
665
721
  }
666
722
  await jsonAction(o.json, async (log) => {
667
- const result = await runUnattendedRelease({ version: o.version, dryRun: o.dryRun }, releaseRunDeps);
723
+ const result = await runUnattendedRelease({ version: o.version, dryRun: o.dryRun, cutCommit: o.cutCommit }, releaseRunDeps);
668
724
  log(`${result.dryRun ? '· 드라이런' : '▶ 릴리스 루프'} ${result.input.version} · 입력 ${JSON.stringify(result.input)}`);
669
725
  return result;
670
726
  }, (r) => r.dryRun || r.state?.status === 'done' || r.state?.status === 'awaiting-approval');
@@ -343,6 +343,14 @@ function pruneReleaseVersions(plan: { current: string; previous: string; keep: n
343
343
  return { removed, kept };
344
344
  }
345
345
 
346
+ /** The build identity of an install.json — version, version folder and commit; empty when it cannot be read. */
347
+ function installedBuild(metadata: unknown): string {
348
+ if (typeof metadata !== 'object' || metadata === null) return '';
349
+ const { version, versionDir, commit } = metadata as { version?: unknown; versionDir?: unknown; commit?: unknown };
350
+ if (typeof version !== 'string' || !version || typeof versionDir !== 'string' || !versionDir) return '';
351
+ return JSON.stringify([version, versionDir, typeof commit === 'string' ? commit : '']);
352
+ }
353
+
346
354
  /** A release update never packs the checkout: the downloaded installer verifies its own release tarball. */
347
355
  export async function runReleaseUpdate(options: ReleaseUpdateOptions = {}, deps: ReleaseUpdateDeps = {}): Promise<SelfUpdateResult> {
348
356
  const out = deps.out ?? console;
@@ -390,8 +398,10 @@ export async function runReleaseUpdate(options: ReleaseUpdateOptions = {}, deps:
390
398
  return fail(1, `설치기 다운로드 실패 (${url}): ${String(error)}`);
391
399
  }
392
400
  let previous = '';
401
+ let previousBuild = '';
393
402
  try {
394
403
  const metadata: unknown = JSON.parse(readFileSync(join(prefix, 'install.json'), 'utf8'));
404
+ previousBuild = installedBuild(metadata);
395
405
  if (typeof metadata === 'object' && metadata !== null && 'versionDir' in metadata && typeof metadata.versionDir === 'string') {
396
406
  const dir = metadata.versionDir;
397
407
  if (/^versions\/[a-zA-Z0-9._-]+$/.test(dir) && !dir.includes('..')) previous = basename(dir);
@@ -421,6 +431,10 @@ export async function runReleaseUpdate(options: ReleaseUpdateOptions = {}, deps:
421
431
  prune = { removed: [], kept: [], skipped: `정리 실패: ${String(error)}` };
422
432
  }
423
433
  if (!options.restart) return finish({ exitCode: 0, installedVersion, decision: null, restarted: false, prune, reason: '--restart 없음: 재시작하지 않음' });
434
+ // REL6: the same build again (version · folder · commit unchanged) leaves the running daemon as it is.
435
+ if (previousBuild && previousBuild === installedBuild(metadata)) {
436
+ return finish({ exitCode: 0, installedVersion, decision: null, restarted: false, prune, reason: `already at ${installedVersion} · restart skipped` });
437
+ }
424
438
  const os = deps.os ?? platform();
425
439
  const command = os === 'darwin' ? 'launchctl' : os === 'linux' ? 'systemctl' : null;
426
440
  if (!command) return finish({ exitCode: 1, installedVersion, decision: null, restarted: false, prune, reason: `지원하지 않는 플랫폼: ${os}` });
@@ -5,6 +5,7 @@ import { Command } from 'commander';
5
5
  import { formatDoctorReport, registerDoctorCommand, runDoctor, type DoctorOptions, type DoctorReport } from './doctor-cli.js';
6
6
  import { applyClaudePluginSetup, planClaudePluginSetup } from './claude-plugin-setup.js';
7
7
  import { recommendedSetup } from './setup-recommend.js';
8
+ import { detectSkillCliAuth, type CliAuthStatus } from '../onboarding/cli-auth-status.js';
8
9
  import { debug } from '../debug/log.js';
9
10
  import { getUserConfig, saveUserConfig, type UserConfig } from '../user-config.js';
10
11
 
@@ -22,6 +23,7 @@ type SetupStep = {
22
23
  export interface SetupCliDeps extends DoctorOptions {
23
24
  runDoctor?: (options: DoctorOptions) => DoctorReport;
24
25
  runDoctorFix?: (args: readonly string[]) => Promise<number>;
26
+ detectSkillCliAuth?: typeof detectSkillCliAuth;
25
27
  readFile?: (path: string) => string;
26
28
  exists?: (path: string) => boolean;
27
29
  homeDir?: () => string;
@@ -142,10 +144,46 @@ export function formatSetupReport(doctor: DoctorReport, steps: SetupStep[], nonI
142
144
  ].join('\n');
143
145
  }
144
146
 
147
+ const CLI_SKILLS = {
148
+ gh: 'github', gws: 'google-workspace', op: '1password', himalaya: 'himalaya',
149
+ } as const;
150
+
151
+ function formatCliSkillStatus(status: CliAuthStatus, enabled: boolean, reason?: string): string {
152
+ return `${status.cli}: found=${status.found ? 'yes' : 'no'} / enabled=${enabled ? 'yes' : 'no'} / ${enabled ? 'enabled' : `skipped (${reason ?? status.skippedReason ?? 'not authenticated'})`}`;
153
+ }
154
+
155
+ function reportCliSkills(deps: SetupCliDeps, apply: boolean): string[] {
156
+ const statuses = (deps.detectSkillCliAuth ?? detectSkillCliAuth)();
157
+ const config = (deps.getUserConfig ?? getUserConfig)();
158
+ const deny = new Set(config.skills.deny ?? []);
159
+ const allow = config.skills.allow ?? [];
160
+ let changed = false;
161
+ const lines = statuses.map((status) => {
162
+ const skill = CLI_SKILLS[status.cli];
163
+ const allowed = allow.length === 0 || allow.some((name) => name.toLowerCase() === skill);
164
+ if (apply) {
165
+ if (status.authenticated && allowed && deny.has(skill)) { deny.delete(skill); changed = true; }
166
+ if (!status.authenticated && !deny.has(skill)) { deny.add(skill); changed = true; }
167
+ }
168
+ const enabled = status.authenticated && allowed && !deny.has(skill);
169
+ return formatCliSkillStatus(status, enabled,
170
+ status.authenticated && !allowed ? 'excluded by skills.allow' :
171
+ status.authenticated && !apply && deny.has(skill) ? 'not enabled; run setup to apply' : undefined);
172
+ });
173
+ if (apply && changed) (deps.saveUserConfig ?? saveUserConfig)({
174
+ ...config, skills: { ...config.skills, deny: [...deny] },
175
+ });
176
+ return lines;
177
+ }
178
+
145
179
  export function registerSetupCommand(program: Command, deps: SetupCliDeps = {}): void {
146
180
  const out = deps.out ?? { log: (value: string) => console.log(value), error: (value: string) => console.error(value) };
147
181
  const setExitCode = deps.setExitCode ?? ((code: number) => { process.exitCode = code; });
148
182
  const doctorRunner = deps.runDoctor ?? runDoctor;
183
+ const showCliSkills = (apply: boolean) => {
184
+ out.log('Setup: CLI skills (public GitHub repository reads remain available without login)');
185
+ for (const line of reportCliSkills(deps, apply)) out.log(line);
186
+ };
149
187
  const isStdinTty = deps.isStdinTty ?? (() => process.stdin.isTTY === true);
150
188
  const prompt = deps.prompt ?? (async (message: string) => {
151
189
  const { createInterface } = await import('node:readline/promises');
@@ -161,6 +199,7 @@ export function registerSetupCommand(program: Command, deps: SetupCliDeps = {}):
161
199
  const doctor = doctorRunner(deps);
162
200
  if (opts.nonInteractive) {
163
201
  out.log(formatSetupReport(doctor, setupSteps(deps), true));
202
+ showCliSkills(false);
164
203
  for (const line of recommendedSetup((deps.getUserConfig ?? getUserConfig)())) out.log(line);
165
204
  setExitCode(0);
166
205
  return;
@@ -183,6 +222,7 @@ export function registerSetupCommand(program: Command, deps: SetupCliDeps = {}):
183
222
  }
184
223
  }
185
224
  out.log(formatSetupReport(doctor, steps, false));
225
+ showCliSkills(false);
186
226
  for (const line of recommendedSetup((deps.getUserConfig ?? getUserConfig)())) out.log(line);
187
227
  const accepted = opts.yes || (isStdinTty() && /^(?:y|yes)?$/i.test((await prompt('추천대로 켤까요? [Y/n] '))?.trim() ?? 'n'));
188
228
  if (accepted) {
@@ -197,7 +237,9 @@ export function registerSetupCommand(program: Command, deps: SetupCliDeps = {}):
197
237
  await doctorProgram.parseAsync([...args], { from: 'user' });
198
238
  return exitCode;
199
239
  });
200
- setExitCode(await runDoctorFix(['doctor', '--fix', '--yes']));
240
+ const fixCode = await runDoctorFix(['doctor', '--fix', '--yes']);
241
+ showCliSkills(true);
242
+ setExitCode(fixCode);
201
243
  return;
202
244
  }
203
245
  setExitCode(0);
@@ -73,7 +73,23 @@ export function registerConnectCommand(program: Command): void {
73
73
  }
74
74
 
75
75
  export function registerSkillsCommands(program: Command): void {
76
- const skills = program.command('skills').description('Check and repair installed skills');
76
+ const skills = program.command('skills').description('Check, install and repair skills');
77
+ skills.command('install <pack>')
78
+ .description('Install a gift skill pack from a code')
79
+ .option('--code <code>', 'Gift code')
80
+ .option('--email <address>', 'Email for gift delivery')
81
+ .option('--agree', 'Agree to email collection and retention')
82
+ .option('--endpoint <url>', 'Gift API origin (overrides skills.giftEndpoint)')
83
+ .option('--json', 'Print the install result as JSON')
84
+ .action(async (pack: string, opts: { code?: string; email?: string; agree?: boolean; endpoint?: string; json?: boolean }) => {
85
+ const { getUserConfig } = await import('../user-config.js');
86
+ const { installGiftPack } = await import('../skills/gift-install.js');
87
+ const result = await installGiftPack({ pack, code: opts.code, email: opts.email, agree: opts.agree,
88
+ endpoint: opts.endpoint ?? getUserConfig().skills.giftEndpoint });
89
+ if (opts.json) console.log(JSON.stringify(result));
90
+ else console.log(result.message);
91
+ if (!result.ok) process.exitCode = 1;
92
+ });
77
93
  skills.command('sources')
78
94
  .description('List every skill folder elanous reads, in order (preset · shared · connected · package)')
79
95
  .option('--json', 'JSON output')
@@ -0,0 +1,37 @@
1
+ import { readFileSync, writeFileSync } from 'node:fs';
2
+ import { join, relative } from 'node:path';
3
+ import { parse } from 'yaml';
4
+ import { debug } from '../debug/log.js';
5
+ import { files, lint, render, STORYBOARD_ROOT, type Storyboard } from '../storyboard/storyboard.js';
6
+
7
+ export function runStoryboard(cmd: 'lint' | 'render', args: string[], json = false): number {
8
+ const reports: Array<{ file: string; errors: string[]; warnings: string[] }> = [];
9
+ let errorCount = 0, warningCount = 0;
10
+ for (const f of files(args)) {
11
+ const file = relative(STORYBOARD_ROOT, f);
12
+ let sb: Storyboard | undefined;
13
+ let errors: string[], warnings: string[];
14
+ try {
15
+ sb = parse(readFileSync(f, 'utf8')) as Storyboard;
16
+ ({ errors, warnings } = lint(sb, file));
17
+ } catch (cause) {
18
+ errors = [cause instanceof Error ? cause.message : String(cause)];
19
+ warnings = [];
20
+ }
21
+ reports.push({ file, errors, warnings });
22
+ errorCount += errors.length;
23
+ warningCount += warnings.length;
24
+ if (json) continue;
25
+ for (const e of errors) console.log(`✗ ${file}: ${e}`);
26
+ for (const w of warnings) console.log(`⚠ ${file}: ${w}`);
27
+ if (errors.length || !sb) continue;
28
+ if (cmd === 'render') {
29
+ const out = join(STORYBOARD_ROOT, 'docs', 'marketing', `STORYBOARD-${sb.id}-v${sb.version}.md`);
30
+ writeFileSync(out, render(sb, file) + '\n');
31
+ console.log(`✓ ${file} → ${relative(STORYBOARD_ROOT, out)}`);
32
+ } else console.log(`✓ ${file} · v${sb.version} · ${sb.status} · ${sb.kind === 'site' ? `사이트 ${sb.sites?.length ?? 0}` : `샷 ${sb.shots.length}`}`);
33
+ }
34
+ if (cmd === 'lint') debug.log('storyboard.lint', 'checked', { files: reports.length, errorCount, warningCount });
35
+ if (json) console.log(JSON.stringify({ files: reports, errorCount, warningCount }));
36
+ return errorCount ? 1 : 0;
37
+ }
@@ -71,7 +71,8 @@ export function applyTestStateDirFlagFromArgv(): string | undefined {
71
71
  * 뒤에만 있어 사람이 부를 입구가 없었고, 그래서 문서 174곳에 수동 주문이 화석으로 남았다.
72
72
  *
73
73
  * 하는 일: nexus state root ⊕ ELANOUS_STATE_DIR ⊕ config-dir 을 **한 뿌리로** 세우고,
74
- * config 사본이 없으면 물질화하며, config 경로가 test 루트 밖이면 **기동을 거부**한다. */
74
+ * 운영 config 가 있으면 test-safe 사본을 물질화하고, 없으면 격리 기본값을 사용하며,
75
+ * config 경로가 test 루트 밖이면 **기동을 거부**한다. */
75
76
  export function applyIsolatedRoot(dir: string): void {
76
77
  const { setTestStateRoot } = require('../nexus/paths.js') as typeof import('../nexus/paths.js');
77
78
  setTestStateRoot(dir);
@@ -98,7 +99,7 @@ export function applyIsolatedRoot(dir: string): void {
98
99
  }
99
100
  cfgDir.setElanousConfigDir(dir);
100
101
  }
101
- // config 사본 보장 — 없으면 운영에서 물질화(최초 무마찰), 있으면 drift 경고만
102
+ // config 사본이 없고 운영 원본이 있으면 물질화하고, 있으면 drift 경고만
102
103
  // (자동 덮어쓰기 금지 — 명시적 sync 원칙). 유닛 테스트(NODE_ENV=test)는
103
104
  // 실 운영 config/secrets 를 temp 로 복사하지 않도록 sync 스킵.
104
105
  if (process.env.NODE_ENV !== 'test') {
@@ -106,8 +107,14 @@ export function applyIsolatedRoot(dir: string): void {
106
107
  const { existsSync } = require('node:fs') as typeof import('node:fs');
107
108
  const { join } = require('node:path') as typeof import('node:path');
108
109
  if (!existsSync(join(dir, 'config.json'))) {
109
- const r = sync.syncTestConfig(dir);
110
- console.error(`[test-isolation] 운영 config 물질화 → ${r.testConfigPath} (telegram=${r.telegramMode})`);
110
+ // A fresh build/test environment may have no operating config to copy.
111
+ // Keep the isolated config root and use the ordinary safe defaults there.
112
+ if (existsSync(join(sync.prodConfigDir(), 'config.json'))) {
113
+ const r = sync.syncTestConfig(dir);
114
+ console.error(`[test-isolation] 운영 config 물질화 → ${r.testConfigPath} (telegram=${r.telegramMode})`);
115
+ } else {
116
+ console.error(`[test-isolation] 운영 config 없음 — ${dir} 의 기본 설정 사용`);
117
+ }
111
118
  } else if (sync.isTestConfigStale(dir)) {
112
119
  console.error(`[test-isolation] ⚠️ 운영 config 가 테스트 사본보다 최신 — 'elanous config sync-test' 로 갱신 권장`);
113
120
  }
@@ -122,6 +122,152 @@ export async function fetchLinearIssues({ apiKey, teamKey, labelOrPrefix, since,
122
122
  return events.sort((a, b) => PRIORITY_RANK[a.priority ?? 'none'] - PRIORITY_RANK[b.priority ?? 'none']);
123
123
  }
124
124
 
125
+ export interface LinearProjectIssue {
126
+ identifier: string;
127
+ title: string;
128
+ url: string;
129
+ state: { name: string; type: string };
130
+ dueDate: string | null;
131
+ priority: number;
132
+ assignee: { name: string } | null;
133
+ updatedAt: string;
134
+ }
135
+
136
+ type LinearRequestOptions = { apiKey: string; fetch?: typeof fetch };
137
+
138
+ async function linearRequest<T>({ apiKey, fetch: fetchFn = fetch }: LinearRequestOptions, query: string, variables: Record<string, unknown>): Promise<T> {
139
+ let response: Response;
140
+ try {
141
+ response = await fetchFn('https://api.linear.app/graphql', {
142
+ method: 'POST', headers: { Authorization: apiKey, 'Content-Type': 'application/json' },
143
+ body: JSON.stringify({ query, variables }),
144
+ });
145
+ } catch (error) {
146
+ throw new Error(`Linear GraphQL request failed: ${error instanceof Error ? error.message : String(error)}`);
147
+ }
148
+ if (!response.ok) throw new Error(`Linear GraphQL HTTP ${response.status}`);
149
+ let body: { data?: T; errors?: Array<{ message?: string }> };
150
+ try { body = await response.json() as typeof body; }
151
+ catch { throw new Error('Linear GraphQL invalid JSON response'); }
152
+ if (body.errors?.length) throw new Error(`Linear GraphQL: ${body.errors.map(e => e.message ?? 'unknown error').join('; ')}`);
153
+ if (!body.data) throw new Error('Linear GraphQL missing data');
154
+ return body.data;
155
+ }
156
+
157
+ /** Shared unique name → project id lookup for COO reads and decision writes. */
158
+ export async function resolveLinearProjectId({ apiKey, project, fetch: fetchFn = fetch }: LinearRequestOptions & { project: string }): Promise<string> {
159
+ const cursors = new Set<string>();
160
+ let projectId = project;
161
+ if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(project)) {
162
+ const matches: string[] = [];
163
+ let after: string | null = null;
164
+ do {
165
+ const data: { projects?: { nodes?: Array<{ id: string; name: string }>; pageInfo?: { hasNextPage: boolean; endCursor: string | null } } } = await linearRequest({ apiKey, fetch: fetchFn },
166
+ `query CooProjects($after: String) { projects(first: 100, after: $after) { nodes { id name } pageInfo { hasNextPage endCursor } } }`, { after });
167
+ if (!Array.isArray(data.projects?.nodes)) throw new Error('Linear GraphQL missing projects');
168
+ for (const candidate of data.projects.nodes) if (candidate.name === project) matches.push(candidate.id);
169
+ const page = data.projects.pageInfo;
170
+ if (page?.hasNextPage && (!page.endCursor || cursors.has(page.endCursor))) throw new Error('Linear GraphQL pagination cursor missing or repeated');
171
+ after = page?.hasNextPage ? page.endCursor : null;
172
+ if (after) cursors.add(after);
173
+ } while (after);
174
+ if (matches.length > 1) throw new Error('프로젝트 이름이 여럿과 맞습니다');
175
+ if (!matches.length) throw new Error('프로젝트 이름과 맞는 항목이 없습니다');
176
+ projectId = matches[0]!;
177
+ }
178
+ return projectId;
179
+ }
180
+
181
+ /** Read-only project lookup. A name is resolved uniquely before querying its issues. */
182
+ export async function fetchLinearProjectIssues({ apiKey, project, includeDone = false, fetch: fetchFn = fetch }: {
183
+ apiKey: string; project: string; includeDone?: boolean; fetch?: typeof fetch;
184
+ }): Promise<LinearProjectIssue[] & { truncated: boolean }> {
185
+ const projectId = await resolveLinearProjectId({ apiKey, project, fetch: fetchFn });
186
+ const issues: LinearProjectIssue[] = [];
187
+ const cursors = new Set<string>();
188
+ let after: string | null = null;
189
+ do {
190
+ const stateFilter = includeDone ? '' : ', state: { type: { nin: ["completed", "canceled"] } }';
191
+ const data: { issues?: { nodes?: LinearProjectIssue[]; pageInfo?: { hasNextPage: boolean; endCursor: string | null } } } = await linearRequest({ apiKey, fetch: fetchFn },
192
+ `query CooProjectIssues($projectId: ID!, $after: String, $first: Int!) {
193
+ issues(filter: { project: { id: { eq: $projectId } }${stateFilter} }, first: $first, after: $after) {
194
+ nodes { identifier title url state { name type } dueDate priority assignee { name } updatedAt }
195
+ pageInfo { hasNextPage endCursor }
196
+ }
197
+ }`, { projectId, after, first: Math.min(100, 250 - issues.length) });
198
+ if (!Array.isArray(data.issues?.nodes)) throw new Error('Linear GraphQL missing issues');
199
+ for (const node of data.issues.nodes) {
200
+ if (!includeDone && (node.state?.type === 'completed' || node.state?.type === 'canceled')) continue;
201
+ if (issues.length < 250) issues.push(node);
202
+ }
203
+ const page = data.issues.pageInfo;
204
+ if (page?.hasNextPage && (!page.endCursor || cursors.has(page.endCursor))) throw new Error('Linear GraphQL pagination cursor missing or repeated');
205
+ after = page?.hasNextPage ? page.endCursor : null;
206
+ if (after) cursors.add(after);
207
+ } while (after && issues.length < 250);
208
+ Object.defineProperty(issues, 'truncated', { value: after !== null, enumerable: false });
209
+ return issues as LinearProjectIssue[] & { truncated: boolean };
210
+ }
211
+
212
+ /** Find a previously created decision issue after a crash between Linear creation and the local rename. */
213
+ export async function findLinearDecisionIssue({ apiKey, projectId, decisionId, fetch: fetchFn = fetch }: LinearRequestOptions & { projectId: string; decisionId: string }): Promise<string | null> {
214
+ const cursors = new Set<string>();
215
+ let after: string | null = null;
216
+ let match: string | null = null;
217
+ do {
218
+ const data: { issues?: { nodes?: Array<{ id: string; description: string | null }>; pageInfo?: { hasNextPage: boolean; endCursor: string | null } } } = await linearRequest({ apiKey, fetch: fetchFn },
219
+ 'query DecisionIssue($projectId: ID!, $decisionId: String!, $after: String) { issues(filter: { project: { id: { eq: $projectId } }, description: { contains: $decisionId } }, first: 100, after: $after) { nodes { id description } pageInfo { hasNextPage endCursor } } }',
220
+ { projectId, decisionId, after });
221
+ if (!Array.isArray(data.issues?.nodes)) throw new Error('Linear GraphQL missing issues');
222
+ for (const node of data.issues.nodes) {
223
+ if (!node.description?.split('\n').includes(`결정 id: ${decisionId}`)) continue;
224
+ if (match) throw new Error('Linear decision issue duplicated');
225
+ match = node.id;
226
+ }
227
+ const page = data.issues.pageInfo;
228
+ if (page?.hasNextPage && (!page.endCursor || cursors.has(page.endCursor))) throw new Error('Linear GraphQL pagination cursor missing or repeated');
229
+ after = page?.hasNextPage ? page.endCursor : null;
230
+ if (after) cursors.add(after);
231
+ } while (after);
232
+ return match;
233
+ }
234
+
235
+ /** Create in the COO project; Linear requires a team from that project for issueCreate. */
236
+ export async function createLinearIssue({ apiKey, projectId, title, description, dueDate, fetch: fetchFn = fetch }: LinearRequestOptions & {
237
+ projectId: string; title: string; description: string; dueDate?: string;
238
+ }): Promise<string> {
239
+ const data = await linearRequest<{ project?: { teams?: { nodes?: Array<{ id: string }> } } }>({ apiKey, fetch: fetchFn },
240
+ 'query DecisionProjectTeam($id: String!) { project(id: $id) { teams { nodes { id } } } }', { id: projectId });
241
+ const teams = data.project?.teams?.nodes;
242
+ if (!teams?.[0]?.id) throw new Error('Linear project has no team');
243
+ const created = await linearRequest<{ issueCreate?: { success?: boolean; issue?: { id?: string } } }>({ apiKey, fetch: fetchFn },
244
+ 'mutation DecisionIssueCreate($input: IssueCreateInput!) { issueCreate(input: $input) { success issue { id } } }',
245
+ { input: { teamId: teams[0].id, projectId, title, description, ...(dueDate ? { dueDate } : {}) } });
246
+ const id = created.issueCreate?.issue?.id;
247
+ if (!created.issueCreate?.success || !id) throw new Error('Linear issueCreate did not return an issue id');
248
+ return id;
249
+ }
250
+
251
+ export async function commentLinearIssue({ apiKey, issueId, body, fetch: fetchFn = fetch }: LinearRequestOptions & { issueId: string; body: string }): Promise<void> {
252
+ const data = await linearRequest<{ commentCreate?: { success?: boolean; comment?: { id?: string } } }>({ apiKey, fetch: fetchFn },
253
+ 'mutation DecisionComment($input: CommentCreateInput!) { commentCreate(input: $input) { success comment { id } } }',
254
+ { input: { issueId, body } });
255
+ if (!data.commentCreate?.success || !data.commentCreate.comment?.id) throw new Error('Linear commentCreate failed');
256
+ }
257
+
258
+ export async function moveLinearIssueToStateType({ apiKey, issueId, type, fetch: fetchFn = fetch }: LinearRequestOptions & {
259
+ issueId: string; type: 'completed' | 'canceled';
260
+ }): Promise<void> {
261
+ const data = await linearRequest<{ issue?: { team?: { states?: { nodes?: Array<{ id: string; type: string }> } } } }>({ apiKey, fetch: fetchFn },
262
+ 'query DecisionIssueStates($id: String!) { issue(id: $id) { team { states { nodes { id type } } } } }', { id: issueId });
263
+ const state = data.issue?.team?.states?.nodes?.find(node => node.type === type);
264
+ if (!state?.id) throw new Error(`Linear team workflow state not found: ${type}`);
265
+ const updated = await linearRequest<{ issueUpdate?: { success?: boolean } }>({ apiKey, fetch: fetchFn },
266
+ 'mutation DecisionIssueClose($id: String!, $input: IssueUpdateInput!) { issueUpdate(id: $id, input: $input) { success } }',
267
+ { id: issueId, input: { stateId: state.id } });
268
+ if (!updated.issueUpdate?.success) throw new Error('Linear issueUpdate failed');
269
+ }
270
+
125
271
  export function toTaskRequest(event: ExternalTaskEvent): {
126
272
  title: string; description: string; priority: 'high' | 'medium' | 'low';
127
273
  external: { provider: 'linear'; ref: string; url?: string; team?: string };
@@ -0,0 +1,10 @@
1
+ import { resolve } from 'node:path';
2
+
3
+ /** Render a Claude Code settings fragment; never touch the user's settings file. */
4
+ export function claudeHooksSettings(repoRoot: string): string {
5
+ const command = (file: string) => `bash '${resolve(repoRoot, 'scripts/context-hooks', file).replaceAll("'", "'\\''")}'`;
6
+ return JSON.stringify({ hooks: {
7
+ Stop: [{ hooks: [{ type: 'command', command: command('claude-stop.sh') }] }],
8
+ PostToolUse: [{ hooks: [{ type: 'command', command: command('post-tool-use.sh') }] }],
9
+ } }, null, 2);
10
+ }