elanous 0.2.10 → 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 (584) 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/{8067-87ce253ec2f23697.js → 5845-e6c003ce8d5fd36a.js} +1 -1
  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-54b32bd27726588d.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-35414e0a007b4082.js +1 -0
  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 -1
  152. package/apps/pwa/out/ops/seats/index.txt +22 -22
  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 -1
  178. package/apps/pwa/out/today/index.txt +23 -23
  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/intake/intake-daily.yaml +2 -1
  194. package/graphs/ops/recipes.yaml +3 -0
  195. package/graphs/ops/secret-exposure.yaml +24 -0
  196. package/graphs/release/recipes.yaml +3 -0
  197. package/graphs/release/release-loop.yaml +4 -1
  198. package/graphs/video/explainer-line.yaml +2 -2
  199. package/package.json +2 -1
  200. package/plugins/elanous-hwp/README.md +25 -0
  201. package/plugins/elanous-hwp/cli.ts +22 -0
  202. package/plugins/elanous-hwp/converter.ts +237 -0
  203. package/plugins/elanous-hwp/fixtures/fixed-controls.hwpx +0 -0
  204. package/plugins/elanous-hwp/fixtures/fixed-table-controls.hwpx +0 -0
  205. package/plugins/elanous-hwp/fixtures/fixed.hwpx +0 -0
  206. package/plugins/elanous-hwp/plugin.json +16 -0
  207. package/plugins/elanous-hwp/plugin.ts +40 -0
  208. package/scripts/ci-public-export-leak-gate.ts +75 -14
  209. package/scripts/ci-public-leak-gate.ts +44 -9
  210. package/scripts/context-hooks/claude-stop.sh +4 -0
  211. package/scripts/context-hooks/emit.ts +24 -0
  212. package/scripts/context-hooks/post-tool-use.sh +4 -0
  213. package/scripts/coord-channel-watch.sh +12 -0
  214. package/scripts/coord-requests-bridge.ts +143 -0
  215. package/scripts/field-feed/lib.ts +4 -0
  216. package/scripts/fleet/node-setup.sh +20 -3
  217. package/scripts/homebrew-formula-bump.ts +69 -0
  218. package/scripts/marketing/measure-site-numbers.ts +169 -0
  219. package/scripts/measure-gate-shards.ts +309 -0
  220. package/scripts/mission-request-judge.ts +31 -1
  221. package/scripts/morning-report.ts +2 -2
  222. package/scripts/opportunity-scan.ts +1 -1
  223. package/scripts/release-loop/auto-approve-node.ts +21 -4
  224. package/scripts/release-loop/cut-branch.ts +84 -0
  225. package/scripts/release-loop/gate-node.ts +14 -13
  226. package/scripts/release-loop/mac-smoke-node.ts +229 -0
  227. package/scripts/release-loop/node-verdict.ts +3 -3
  228. package/scripts/release-loop/tui-node-checks.ts +42 -0
  229. package/scripts/release-loop/tui-sim-node.ts +32 -8
  230. package/scripts/release-loop/unattended-release.ts +5 -3
  231. package/scripts/release-loop/version-node.ts +61 -10
  232. package/scripts/signal-digest-cycle.ts +2 -7
  233. package/scripts/signal-digest-photo.ts +11 -0
  234. package/scripts/tech1-public-measure.ts +94 -0
  235. package/scripts/test-diet/node.ts +1 -1
  236. package/scripts/tui-regress.ts +2 -2
  237. package/scripts/ux/demo-rehearsal.ts +243 -0
  238. package/scripts/ux/lib/rehearsal-verdict.ts +73 -0
  239. package/skills/diagram-master/SKILL.md +366 -0
  240. package/skills/diagram-master/references/engines/excalidraw/color-palette.md +67 -0
  241. package/skills/diagram-master/references/engines/excalidraw/element-templates.md +182 -0
  242. package/skills/diagram-master/references/engines/excalidraw/json-schema.md +71 -0
  243. package/skills/diagram-master/references/engines/excalidraw/render_excalidraw.py +189 -0
  244. package/skills/diagram-master/references/engines/excalidraw/render_template.html +57 -0
  245. package/skills/diagram-master/references/engines/matplotlib/matplotlib-patterns.md +235 -0
  246. package/skills/diagram-master/references/engines/matplotlib/render_matplotlib.py +184 -0
  247. package/skills/diagram-master/references/engines/mermaid/domain-templates.md +435 -0
  248. package/skills/diagram-master/references/engines/mermaid/render_mermaid.py +116 -0
  249. package/skills/diagram-master/references/engines/mermaid/syntax-reference.md +2247 -0
  250. package/skills/diagram-master/references/engines/nanobannana/render_nanobannana2.py +168 -0
  251. package/skills/diagram-master/references/engines/svg/color-palette.md +188 -0
  252. package/skills/diagram-master/references/engines/svg/html-base-template.html +332 -0
  253. package/skills/diagram-master/references/engines/svg/interactivity-patterns.md +435 -0
  254. package/skills/diagram-master/references/engines/svg/render_interactive_svg.py +212 -0
  255. package/skills/diagram-master/references/engines/svg/svg-element-templates.md +349 -0
  256. package/skills/diagram-master/references/examples/README.md +69 -0
  257. package/skills/diagram-master/references/examples/ref_grid_architecture.png +0 -0
  258. package/skills/diagram-master/references/examples/ref_layered_flow.png +0 -0
  259. package/skills/diagram-master/references/pyproject.toml +16 -0
  260. package/skills/diagram-master/references/quality-checklist.md +33 -0
  261. package/skills/diagram-master/references/routing-guide.md +164 -0
  262. package/skills/diagram-master/references/uv.lock +1356 -0
  263. package/skills/google-workspace/SKILL.md +33 -0
  264. package/skills/google-workspace/scripts/gws-safe.sh +177 -0
  265. package/skills/grill-me/SKILL.md +1 -1
  266. package/skills/lecture-note-digitizer/SKILL.md +926 -0
  267. package/skills/lecture-note-digitizer/references/convert_to_pdf.py +30 -0
  268. package/skills/lecture-note-digitizer/references/extract_slide_diagram.py +73 -0
  269. package/skills/lecture-note-digitizer/references/html-template.md +167 -0
  270. package/skills/lecture-note-digitizer/references/katex-style-guide.md +165 -0
  271. package/skills/omni-crawl/SKILL.md +21 -11
  272. package/skills/omni-crawl/scripts/main.ts +6 -6
  273. package/skills/omni-crawl/src/router.ts +3 -3
  274. package/skills/omni-digest/SKILL.md +17 -9
  275. package/skills/omni-digest/digest.md +2 -2
  276. package/skills/omni-digest/src/fetch.ts +30 -3
  277. package/skills/pitch-deck/SKILL.md +2 -2
  278. package/skills/pitch-deck/template.html +2 -2
  279. package/skills/youtube-master/SKILL.md +163 -0
  280. package/skills/youtube-master/package-lock.json +558 -0
  281. package/skills/youtube-master/package.json +13 -0
  282. package/skills/youtube-master/references/study-note-template.md +124 -0
  283. package/skills/youtube-master/scripts/main.ts +435 -0
  284. package/skills/youtube-master/scripts/transcribe-local.ts +24 -0
  285. package/skills/youtube-master/src/audio.ts +63 -0
  286. package/skills/youtube-master/src/domain-staging.ts +50 -0
  287. package/skills/youtube-master/src/env.ts +36 -0
  288. package/skills/youtube-master/src/obsidian.ts +155 -0
  289. package/skills/youtube-master/src/router.ts +110 -0
  290. package/skills/youtube-master/src/stt.ts +199 -0
  291. package/skills/youtube-master/src/study-note.ts +175 -0
  292. package/skills/youtube-master/src/summarize.ts +358 -0
  293. package/skills/youtube-master/src/transcript.ts +76 -0
  294. package/skills/youtube-master/src/types.ts +91 -0
  295. package/skills/youtube-master/src/util.ts +93 -0
  296. package/skills/youtube-master/src/youtube.ts +70 -0
  297. package/skills/youtube-master/tsconfig.json +13 -0
  298. package/src/acp/server.ts +5 -2
  299. package/src/agent-mission/driver.ts +80 -10
  300. package/src/agent-mission/pty-decision.ts +71 -0
  301. package/src/agent-mission/review-loop.ts +30 -0
  302. package/src/autopilot/build/llm-conflict-merge.ts +16 -3
  303. package/src/autopilot/tool-registry.ts +2 -0
  304. package/src/boot/daemon-prompt-turn.ts +5 -0
  305. package/src/boot/daemon-runtime.ts +9 -3
  306. package/src/boot/daemon-tools/web-search.ts +3 -0
  307. package/src/card-followup/core.ts +109 -0
  308. package/src/chat/index.ts +1 -0
  309. package/src/cli/drive-input-jail.ts +101 -0
  310. package/src/cli/hooks-cli.ts +17 -3
  311. package/src/cli/make-cli.ts +43 -0
  312. package/src/cli/persona-cli.ts +90 -4
  313. package/src/cli/plugin-cli.ts +31 -22
  314. package/src/cli/pod-cli.ts +19 -9
  315. package/src/cli/pr-cli.ts +51 -15
  316. package/src/cli/pty-drive-cli.ts +41 -7
  317. package/src/cli/pty-drive-verdict.ts +40 -0
  318. package/src/cli/release-cli.ts +17 -4
  319. package/src/cli/setup-cli.ts +43 -1
  320. package/src/cli/skills-cli.ts +17 -1
  321. package/src/context-bus/claude-hooks-install.ts +10 -0
  322. package/src/context-bus/context-now-surfaces.ts +48 -0
  323. package/src/context-bus/context-now.ts +104 -0
  324. package/src/context-bus/coord-events.ts +2 -2
  325. package/src/context-bus/external-events.ts +52 -0
  326. package/src/context-bus/long-term-memory.ts +108 -0
  327. package/src/dashboard/index.ts +9 -4
  328. package/src/dashboard/slash-runtime/dashboard-handlers.ts +7 -1
  329. package/src/decisions/decision-cards.ts +10 -3
  330. package/src/decisions/decision-ledger.ts +13 -5
  331. package/src/discord/sprint21-runtime.ts +5 -1
  332. package/src/discord-self-message.ts +32 -0
  333. package/src/discord-slash-wire.ts +5 -1
  334. package/src/discord.ts +17 -1
  335. package/src/domains/core-tools.ts +3 -2
  336. package/src/domains/outbound-alert.ts +6 -1
  337. package/src/domains/telegram-kind-route.ts +51 -13
  338. package/src/exec-requests/default-outputs.ts +25 -0
  339. package/src/exec-requests/planner.ts +10 -2
  340. package/src/exec-requests/runner.ts +20 -6
  341. package/src/exec-requests/store.ts +3 -0
  342. package/src/execution-loop/launch-gate.ts +100 -0
  343. package/src/field/field-reel.ts +5 -1
  344. package/src/field-note/index.ts +201 -0
  345. package/src/field-note/transcribe.ts +248 -0
  346. package/src/graph-kinds/registry.ts +3 -3
  347. package/src/graph-runner/graph-cli.ts +61 -1
  348. package/src/graph-runner/graph-grow-llm.ts +37 -0
  349. package/src/graph-runner/graph-grow.ts +47 -0
  350. package/src/graph-runner/runner.ts +317 -25
  351. package/src/grok/agent-search.ts +8 -2
  352. package/src/guardian/secret-exposure-scan.ts +101 -0
  353. package/src/harness/harness-cli-command.ts +247 -7
  354. package/src/harness/harness-pod-dispatch.ts +43 -23
  355. package/src/harness/harness-seams.ts +38 -4
  356. package/src/harness/harness-stop.ts +23 -4
  357. package/src/harness/launch-registry.ts +91 -0
  358. package/src/harness/process-garbage.ts +5 -3
  359. package/src/hooks/dispatch.ts +82 -0
  360. package/src/hooks/expose.ts +254 -0
  361. package/src/hooks/github-shadow.ts +108 -0
  362. package/src/hooks/providers.ts +71 -5
  363. package/src/hooks/queue.ts +1 -1
  364. package/src/hooks/receiver.ts +21 -20
  365. package/src/index.ts +52 -14
  366. package/src/intake-plane/categorize.ts +18 -46
  367. package/src/intake-plane/discord-seat-work.ts +10 -0
  368. package/src/intake-plane/front-route-classifier.ts +33 -39
  369. package/src/intake-plane/persona-answer.ts +30 -0
  370. package/src/intake-plane/runtime-callables.ts +8 -1
  371. package/src/intake-plane/telegram-seat-work.ts +36 -2
  372. package/src/intake-plane/wish-folder.ts +67 -0
  373. package/src/intent-gate/gate.ts +30 -10
  374. package/src/live/detail-switch.ts +5 -0
  375. package/src/llm/difficulty.ts +57 -0
  376. package/src/llm/judge-layer.ts +101 -0
  377. package/src/llm/mission-router.ts +33 -19
  378. package/src/loops/contract/examples/cmo-seat.loop.yaml +51 -0
  379. package/src/loops/contract/examples/orchestrator.loop.yaml +57 -0
  380. package/src/loops/contract/examples/steward.loop.yaml +50 -0
  381. package/src/loops/contract/loop-agent.schema.json +138 -0
  382. package/src/loops/contract/validate.ts +64 -0
  383. package/src/loops/registry.ts +20 -6
  384. package/src/market/signed-index.ts +4 -0
  385. package/src/maturity/bot-command-maturity.ts +45 -0
  386. package/src/maturity/feature-maturity.ts +26 -4
  387. package/src/model-tier/index.ts +2 -2
  388. package/src/model-tier/tier-resolver.ts +20 -20
  389. package/src/model-tier/types.ts +6 -6
  390. package/src/mss/logging/log-store.ts +15 -0
  391. package/src/nexus/api/config-model-tier.ts +5 -4
  392. package/src/nexus/api/fabric-plans.ts +53 -0
  393. package/src/nexus/api/harness-api.ts +5 -1
  394. package/src/nexus/api/health.ts +2 -0
  395. package/src/nexus/api/http-server.ts +36 -0
  396. package/src/nexus/api/inside-events.ts +226 -0
  397. package/src/nexus/api/meta-api.ts +21 -8
  398. package/src/nexus/api/missions.ts +6 -1
  399. package/src/nexus/api/ops-seats.ts +33 -0
  400. package/src/nexus/api/outputs-route.ts +44 -0
  401. package/src/nexus/api/seat-requests.ts +44 -2
  402. package/src/nexus/api/sessions-store.ts +17 -38
  403. package/src/nexus/api/sub-seat-charters.ts +34 -0
  404. package/src/nexus/api/tabs-mutations.ts +3 -1
  405. package/src/nexus/api/trace.ts +18 -6
  406. package/src/nexus/api/workflow-run-event-bridge.ts +1 -0
  407. package/src/nexus/api/workflows.ts +80 -6
  408. package/src/nexus/index.ts +2 -0
  409. package/src/nexus/kinds/webterm.ts +2 -1
  410. package/src/nexus/webterm/pty.ts +6 -2
  411. package/src/onboarding/cli-auth-status.ts +80 -0
  412. package/src/onboarding/voice-ai.ts +3 -3
  413. package/src/org/seat-budget.ts +48 -0
  414. package/src/org/stall-escalation.ts +63 -0
  415. package/src/outputs/ledger.ts +120 -0
  416. package/src/persona/persona-todo.ts +79 -0
  417. package/src/persona/presets.ts +116 -2
  418. package/src/persona/team-set.ts +155 -0
  419. package/src/plugins/install/plugin-install.ts +14 -0
  420. package/src/plugins/maker/market-bundle.ts +109 -0
  421. package/src/plugins/maker/plugin-maker.ts +254 -11
  422. package/src/plugins/maker/wizard-generate.ts +103 -0
  423. package/src/pod-lease/admission.ts +150 -0
  424. package/src/pod-lease/dependency-state.ts +85 -0
  425. package/src/pod-lease/host-lease.ts +127 -0
  426. package/src/project/project-suggestion.ts +16 -0
  427. package/src/project/session-context.ts +24 -0
  428. package/src/release-loop/checklist.ts +2 -2
  429. package/src/release-loop/feature-store.ts +5 -3
  430. package/src/release-loop/next-md-merge.ts +58 -0
  431. package/src/release-loop/release-note.ts +59 -4
  432. package/src/seat-dispatch/seat-ask.ts +130 -0
  433. package/src/seat-dispatch/seat-questions.ts +100 -0
  434. package/src/seat-loop/seat-loop.ts +548 -29
  435. package/src/seat-loop/seat-report.ts +37 -2
  436. package/src/self-dev/dev-cli.ts +4 -1
  437. package/src/self-dev/dev-pipeline.ts +37 -1
  438. package/src/self-dev/draft-sweep.ts +38 -33
  439. package/src/self-dev/draft-triage-rules.ts +8 -5
  440. package/src/self-dev/fabric-plan-core.ts +297 -0
  441. package/src/self-dev/orchestrate.ts +36 -4
  442. package/src/self-dev/release-path-guard.ts +62 -0
  443. package/src/self-dev/shard-aggregate.ts +230 -0
  444. package/src/self-dev/shard-placement.ts +60 -0
  445. package/src/self-dev/shard-plan.ts +48 -0
  446. package/src/self-dev/shard-pod-run.ts +77 -0
  447. package/src/self-dev/shard-run.ts +105 -0
  448. package/src/self-implement/gate-baseline.ts +1 -1
  449. package/src/self-implement/gate-cli.ts +40 -1
  450. package/src/self-implement/gate-scope.ts +37 -7
  451. package/src/self-implement/graph-variant.ts +171 -0
  452. package/src/self-implement/harness-policy-gates.ts +1 -1
  453. package/src/self-implement/headless-elanous-driver.ts +22 -3
  454. package/src/self-implement/host-regate.ts +22 -1
  455. package/src/self-implement/importer-test-index.ts +2 -1
  456. package/src/self-implement/l8-merge-queue.ts +130 -0
  457. package/src/self-implement/orchestrator.ts +242 -9
  458. package/src/self-implement/route-consumer-test-index.ts +122 -0
  459. package/src/self-implement/seams.ts +72 -11
  460. package/src/self-implement/test-seams.ts +1 -0
  461. package/src/self-implement/uncommitted-work.ts +59 -0
  462. package/src/session/index.ts +36 -13
  463. package/src/skills/gift-install.ts +215 -0
  464. package/src/skills/tools/web-search.ts +4 -1
  465. package/src/steward/launch.ts +42 -26
  466. package/src/steward/steward-cards.ts +17 -9
  467. package/src/steward/triage.ts +46 -14
  468. package/src/task-cards/card-cli.ts +25 -1
  469. package/src/task-orchestrator/mission.ts +5 -0
  470. package/src/task-orchestrator/store.ts +12 -3
  471. package/src/task-orchestrator/surfaces/pod-command-job.ts +29 -7
  472. package/src/task-orchestrator/surfaces/pod-lease.ts +156 -19
  473. package/src/task-orchestrator/surfaces/pod-pool.ts +127 -26
  474. package/src/task-orchestrator/surfaces/self-implement-pod.ts +214 -40
  475. package/src/task-orchestrator/surfaces/self-implement.ts +15 -2
  476. package/src/task-orchestrator/types.ts +2 -0
  477. package/src/telegram-card-followup.ts +16 -96
  478. package/src/telegram-commands.ts +10 -1
  479. package/src/telegram-project-command.ts +71 -0
  480. package/src/telegram-report.ts +21 -17
  481. package/src/telegram.ts +29 -3
  482. package/src/tool-runtime/self-cognition-runtimes.ts +13 -3
  483. package/src/user-config.ts +102 -20
  484. package/src/version/packed-revision.json +1 -1
  485. package/src/web-search/grok.ts +7 -1
  486. package/src/web-search/index.ts +28 -1
  487. package/src/web-search/provider.ts +4 -0
  488. package/src/web-search/x-intent.ts +5 -0
  489. package/src/workflow-runtime/executor.ts +277 -47
  490. package/src/workflow-runtime/index.ts +1 -0
  491. package/src/workflow-runtime/nodes/subworkflow.ts +60 -0
  492. package/src/workflow-runtime/plugin-kind-node.ts +1 -1
  493. package/src/workflow-runtime/schema.ts +79 -4
  494. package/src/workflow-runtime/types.ts +35 -4
  495. package/apps/pwa/out/_next/static/2DsiDfDCks44g1cPUMhiW/_buildManifest.js +0 -1
  496. package/apps/pwa/out/_next/static/chunks/1105-c473ed46272fba79.js +0 -1
  497. package/apps/pwa/out/_next/static/chunks/1377-1efeffbbac0963ff.js +0 -1
  498. package/apps/pwa/out/_next/static/chunks/1470.98a6ab61ccb4131c.js +0 -1
  499. package/apps/pwa/out/_next/static/chunks/216-5acd01cbde4acda7.js +0 -1
  500. package/apps/pwa/out/_next/static/chunks/2330-c2f07246c8788af1.js +0 -1
  501. package/apps/pwa/out/_next/static/chunks/2375-cb6debea56cce4fb.js +0 -1
  502. package/apps/pwa/out/_next/static/chunks/24-e6d28380efab7faf.js +0 -1
  503. package/apps/pwa/out/_next/static/chunks/2977-e38442e86289f1d3.js +0 -1
  504. package/apps/pwa/out/_next/static/chunks/2982-2a2df543be3d2e5a.js +0 -25
  505. package/apps/pwa/out/_next/static/chunks/3403.4bc97b4066945a94.js +0 -5
  506. package/apps/pwa/out/_next/static/chunks/3615-740dd270764ffa24.js +0 -1
  507. package/apps/pwa/out/_next/static/chunks/371-4ad6af55dad02ced.js +0 -1
  508. package/apps/pwa/out/_next/static/chunks/3734-1015fe991eb9b7e7.js +0 -1
  509. package/apps/pwa/out/_next/static/chunks/4114-3e63117452378807.js +0 -1
  510. package/apps/pwa/out/_next/static/chunks/5012-a7fe6f3c8b6fe45a.js +0 -1
  511. package/apps/pwa/out/_next/static/chunks/5609-012bb3557211f07e.js +0 -1
  512. package/apps/pwa/out/_next/static/chunks/5783-ef40ebfc91d55230.js +0 -1
  513. package/apps/pwa/out/_next/static/chunks/5791-bb82743fa6299562.js +0 -1
  514. package/apps/pwa/out/_next/static/chunks/62-ff227c3edb8b248b.js +0 -1
  515. package/apps/pwa/out/_next/static/chunks/6320-baac91d68843a9a6.js +0 -1
  516. package/apps/pwa/out/_next/static/chunks/6725-68ab7cc0b1faf00d.js +0 -1
  517. package/apps/pwa/out/_next/static/chunks/6998-a654fe5c58271600.js +0 -1
  518. package/apps/pwa/out/_next/static/chunks/7633-1b3709b0854c9412.js +0 -1
  519. package/apps/pwa/out/_next/static/chunks/7647.2fcd30d894d5ee30.js +0 -1
  520. package/apps/pwa/out/_next/static/chunks/7673-2eda1507d38dadb8.js +0 -1
  521. package/apps/pwa/out/_next/static/chunks/7951-8b03dbb10b24cd06.js +0 -1
  522. package/apps/pwa/out/_next/static/chunks/7990-e12efba616f46e03.js +0 -1
  523. package/apps/pwa/out/_next/static/chunks/8142-744998d65d7bb17e.js +0 -1
  524. package/apps/pwa/out/_next/static/chunks/8195-6fde3d7a39859206.js +0 -10
  525. package/apps/pwa/out/_next/static/chunks/8619-4365e03a2862830f.js +0 -1
  526. package/apps/pwa/out/_next/static/chunks/8894-b5fb36bab837cd29.js +0 -1
  527. package/apps/pwa/out/_next/static/chunks/920-f7dceb78f1c585ef.js +0 -1
  528. package/apps/pwa/out/_next/static/chunks/9454-84d0d5d77446bd92.js +0 -1
  529. package/apps/pwa/out/_next/static/chunks/9651-3cd291a6fa1370d2.js +0 -1
  530. package/apps/pwa/out/_next/static/chunks/988-5f92e7ab7ccea3dd.js +0 -1
  531. package/apps/pwa/out/_next/static/chunks/9989-5582582f06429840.js +0 -1
  532. package/apps/pwa/out/_next/static/chunks/ae787e4c-f2998de7f1ba72a4.js +0 -1
  533. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-859cea1570b6227a.js +0 -1
  534. package/apps/pwa/out/_next/static/chunks/app/approvals/page-d2253d7e58ef1890.js +0 -1
  535. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-5441d4a9ab35e518.js +0 -1
  536. package/apps/pwa/out/_next/static/chunks/app/board/page-d9b12d680922d980.js +0 -1
  537. package/apps/pwa/out/_next/static/chunks/app/bots/page-e268d6d145827a22.js +0 -1
  538. package/apps/pwa/out/_next/static/chunks/app/chat/page-f8116559c7424da7.js +0 -1
  539. package/apps/pwa/out/_next/static/chunks/app/consult/page-1085399fd0f32112.js +0 -1
  540. package/apps/pwa/out/_next/static/chunks/app/control/page-1df686a14f65895a.js +0 -1
  541. package/apps/pwa/out/_next/static/chunks/app/design-check/page-54e1f966ab60a4dc.js +0 -1
  542. package/apps/pwa/out/_next/static/chunks/app/editor/page-7ed95f296f5ee8e9.js +0 -1
  543. package/apps/pwa/out/_next/static/chunks/app/exec/page-11edac668d77e5c3.js +0 -1
  544. package/apps/pwa/out/_next/static/chunks/app/field/page-59e2b9a22cd5a6d5.js +0 -1
  545. package/apps/pwa/out/_next/static/chunks/app/healthz/route-859cea1570b6227a.js +0 -1
  546. package/apps/pwa/out/_next/static/chunks/app/intake/page-2976df21ad74ab48.js +0 -1
  547. package/apps/pwa/out/_next/static/chunks/app/layout-34d211a67f16d510.js +0 -1
  548. package/apps/pwa/out/_next/static/chunks/app/live/page-8ff652d5d950639b.js +0 -1
  549. package/apps/pwa/out/_next/static/chunks/app/market/page-b31610deda76337b.js +0 -1
  550. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-6cb0e1bbefe5fb3d.js +0 -1
  551. package/apps/pwa/out/_next/static/chunks/app/morning/page-a286a152cf584576.js +0 -1
  552. package/apps/pwa/out/_next/static/chunks/app/not-found-d9237581e7003bcb.js +0 -1
  553. package/apps/pwa/out/_next/static/chunks/app/observatory/page-83274e761643fdd2.js +0 -1
  554. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-ceee6cfb1d4e6d68.js +0 -1
  555. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-9c9f8bb3e4a64091.js +0 -1
  556. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-c17217603ea3426a.js +0 -1
  557. package/apps/pwa/out/_next/static/chunks/app/page-efb6177cbf1e8a51.js +0 -1
  558. package/apps/pwa/out/_next/static/chunks/app/reflection/page-d307523703bc4df3.js +0 -1
  559. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-462f4e4bab45ce7d.js +0 -1
  560. package/apps/pwa/out/_next/static/chunks/app/sessions/page-8990e96f33af3837.js +0 -1
  561. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-e168fa1030af0548.js +0 -1
  562. package/apps/pwa/out/_next/static/chunks/app/settings/page-1233e05a4068bc9c.js +0 -1
  563. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-13a1840b1fac337a.js +0 -1
  564. package/apps/pwa/out/_next/static/chunks/app/setup/layout-859cea1570b6227a.js +0 -1
  565. package/apps/pwa/out/_next/static/chunks/app/setup/page-64a29fc80b11ab55.js +0 -1
  566. package/apps/pwa/out/_next/static/chunks/app/share/page-68e787339a16b3dc.js +0 -1
  567. package/apps/pwa/out/_next/static/chunks/app/showroom/page-4fddc90b23feb215.js +0 -1
  568. package/apps/pwa/out/_next/static/chunks/app/tasks/page-886ba8260dc79c79.js +0 -1
  569. package/apps/pwa/out/_next/static/chunks/app/term/page-0f26507a1722e40b.js +0 -1
  570. package/apps/pwa/out/_next/static/chunks/app/today/page-109d4ccd42259c9d.js +0 -1
  571. package/apps/pwa/out/_next/static/chunks/app/trace/page-f5c2a4e510726774.js +0 -1
  572. package/apps/pwa/out/_next/static/chunks/app/vault/page-b2e0039416f42522.js +0 -1
  573. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-26102fd7350f171e.js +0 -1
  574. package/apps/pwa/out/_next/static/chunks/app/workflows/page-708948bc8a802794.js +0 -1
  575. package/apps/pwa/out/_next/static/chunks/app/workspace/page-5e5611620f78fb0b.js +0 -1
  576. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-139ba5c367bbf60a.js +0 -1
  577. package/apps/pwa/out/_next/static/chunks/framework-ea806bb57f2b8923.js +0 -1
  578. package/apps/pwa/out/_next/static/chunks/main-8d2302399c2c8f87.js +0 -1
  579. package/apps/pwa/out/_next/static/chunks/main-app-202b4e81f33895ea.js +0 -1
  580. package/apps/pwa/out/_next/static/chunks/pages/_app-b10928c60f0b8488.js +0 -1
  581. package/apps/pwa/out/_next/static/chunks/pages/_error-689039227d443fab.js +0 -1
  582. package/apps/pwa/out/_next/static/chunks/webpack-e5bea36f183b25e6.js +0 -1
  583. package/apps/pwa/out/_next/static/css/43feca5fd387dcbd.css +0 -1
  584. /package/apps/pwa/out/_next/static/{2DsiDfDCks44g1cPUMhiW → Y4zKzL62sakG5momz3SoQ}/_ssgManifest.js +0 -0
@@ -11,6 +11,9 @@ gate:
11
11
  command: 'bun scripts/release-loop/gate-node.ts --json'
12
12
  # 4h — the Pod gate took 2h02m on 09-30 (G1f ②) and the 0.2.5 loop was cut at exactly 2h with no verdict; K9 (≤15 min) is the real fix.
13
13
  timeout_ms: 14400000
14
+ mac-smoke:
15
+ command: 'bun scripts/release-loop/mac-smoke-node.ts'
16
+ timeout_ms: 1800000
14
17
  export-check:
15
18
  command: 'bun scripts/release-loop/export-check-node.ts'
16
19
  timeout_ms: 1800000
@@ -15,6 +15,7 @@ nodes:
15
15
  - { node_id: cutoff, kind: judge, recipe: 'cmd:cutoff', max_visits: 1 }
16
16
  - { node_id: checklist-gate, kind: gate, recipe: 'cmd:checklist-gate', max_visits: 1 }
17
17
  - { node_id: gate, kind: gate, recipe: 'cmd:gate', max_visits: 1, notify: start }
18
+ - { node_id: mac-smoke, kind: gate, recipe: 'cmd:mac-smoke', max_visits: 1 }
18
19
  - { node_id: export-check, kind: gate, recipe: 'cmd:export-check', max_visits: 1 }
19
20
  - { node_id: pwa, kind: gate, recipe: 'cmd:pwa', max_visits: 1, notify: start }
20
21
  - { node_id: prepare, kind: agent, recipe: 'cmd:prepare', max_visits: 1, notify: start }
@@ -37,7 +38,9 @@ edges:
37
38
  - { from: version-release, on: outcome, map: { ok: cutoff, fail: failed, error: failed } }
38
39
  - { from: cutoff, on: outcome, map: { ok: checklist-gate, fail: failed, error: failed } }
39
40
  - { from: checklist-gate, on: outcome, map: { ok: gate, fail: failed, error: failed } }
40
- - { from: gate, on: outcome, map: { ok: export-check, fail: failed, error: failed } }
41
+ - { from: gate, on: outcome, map: { ok: mac-smoke, fail: failed, error: failed } }
42
+ # Warning-only: a macOS failure or unmeasured run never blocks publishing — auto-approve shows it as a warning row.
43
+ - { from: mac-smoke, on: outcome, map: { ok: export-check, fail: export-check, error: export-check } }
41
44
  - { from: export-check, on: outcome, map: { ok: pwa, fail: failed, error: failed } }
42
45
  - { from: pwa, on: outcome, map: { ok: prepare, fail: failed, error: failed } }
43
46
  - { from: prepare, on: outcome, map: { ok: upgrade, fail: failed, error: failed } }
@@ -1,7 +1,7 @@
1
1
  # «N가지 한 번에 정리» explainer: facts → script → timed VO → composition → checked render → measured QC.
2
2
  # Engine = skills/explainer-video (vo.ts · build.mjs). Render/snapshot/check = the existing hyperframes-render recipe.
3
- # ⚠️ Recipes explainer-script / explainer-vo / explainer-build / explainer-qc are declared here and not yet coded
4
- # (src/video-pipeline/recipes) — a walk stops at the first of them as «unknown recipe». Until then the skill runs these steps.
3
+ # Recipes explainer-script / explainer-vo / explainer-build / explainer-qc are implemented in
4
+ # src/video-pipeline/recipes/explainer.ts; the graph routes their outcomes through render and QC.
5
5
  graph_id: explainer-line
6
6
  version: 1
7
7
  entry_node: script
package/package.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "elanous",
3
- "version": "0.2.10",
3
+ "mcpName": "io.github.ElanvitalAI/elanous",
4
+ "version": "0.2.11",
4
5
  "description": "Self-healing coding harness where eyes, hands, and memory all turn on one sentence — it authors its own goals, implements them in isolated worktrees, and reviews its own work.",
5
6
  "license": "Apache-2.0",
6
7
  "author": "Elanvital AI",
@@ -0,0 +1,25 @@
1
+ # elanous-hwp
2
+
3
+ **S** — HWPX files carry Korean office documents in ZIP/XML form. **C** — Markdown-only tooling cannot read their paragraphs, headings or tables. **Q** — How can these documents be exchanged without external binaries? **A** — This official plugin provides a built-in ZIP/XML reader and a minimal writer.
4
+
5
+ ```sh
6
+ bun plugins/elanous-hwp/cli.ts to-md input.hwpx output.md
7
+ bun plugins/elanous-hwp/cli.ts from-md input.md output.hwpx
8
+ ```
9
+
10
+ The plugin also registers `HwpxToMarkdown` (HWPX path → Markdown text) and `MarkdownToHwpx` (Markdown path and new HWPX output path) as host LLM tools. `to-md` reads spine-ordered HWPX sections and emits paragraphs, headings (referenced paragraph properties with OUTLINE heading levels 0–5), and GFM tables. `from-md` writes paragraphs and headings into a minimal HWPX ZIP; it does not write tables. Existing output files are not overwritten. `.hwp` legacy binary files are **not supported**: save them as `.hwpx` first. No external executable, Python package, or third-party ZIP/XML library is required. This first piece does not validate page layout or promise round-trip preservation of unsupported styling, images, or merged cells.
11
+
12
+ ## Verification record
13
+
14
+ - Official manifest and both host conversion tools: `plugin.json` (`trust: official`, `main: ./plugin.ts`), `plugin.ts`, and `converter.test.ts` (host tool test and manifest loader assertions).
15
+ - Fixed compressed sample → OUTLINE level 0 heading with reference ID 4, ordinary paragraph with nonzero reference ID 2, joined paragraph, GFM table and escaped pipe, then mixed text/table/text within one run in original order: `fixtures/fixed.hwpx` and `converter.test.ts` (fixed sample test). `fixtures/fixed-controls.hwpx` checks text/lineBreak/tab interleaving inside one `<hp:t>`. `fixtures/fixed-table-controls.hwpx` checks that a cell's lineBreak and separate paragraphs become `<br>` within one GFM row (test at `converter.test.ts:64-70`; implementation at `converter.ts:153-162`).
16
+ - Minimal Markdown → HWPX → identical Markdown; unsupported Markdown tables fail explicitly: `converter.ts` and `converter.test.ts` (round-trip test). ATX headings end on their own line even without a blank separator: `converter.ts:198-220` and `converter.test.ts:55-62` assert three distinct generated XML content paragraphs (four including the section-properties paragraph), properties 1/0/2. Multiline ordinary paragraphs retain lineBreak controls (`converter.test.ts:47-53`). Legacy HWP guidance and no-overwrite behavior: `cli.ts`, `plugin.ts`, and `converter.test.ts` (CLI test).
17
+ - Zero external runtime binary dependencies: `converter.ts` uses `node:zlib`; `cli.ts` and `plugin.ts` use `node:fs`. The independent consumer below is a *verification-only* script from the existing pack, not invoked by the plugin.
18
+ - `bun test plugins/elanous-hwp/converter.test.ts 2>&1 | tail -6`: `8 pass`, `0 fail`, `Ran 8 tests across 1 file. [58.00ms]` (40 assertions). This includes a spawned `bun plugins/elanous-hwp/cli.ts to-md …` subprocess in the CLI test.
19
+ - `bun bin/elanous.mjs --test self typecheck 2>&1 | tail -4`: `✅ 변경 파일 타입 검사 통과 (4개 파일 검사…)`.
20
+ - `bun bin/elanous.mjs --test self gate --changed 2>&1 | tail -20`: `tests: pass (1 files)` and `[test-count] changed-test-files=1, test-cases=0→8, decrease=0`; the gate explicitly lists six files outside the bun test runner, including fixture archives (these archives are read by the focused tests).
21
+ - Earlier `bun .elanous-test/scratch/verify-generated-hwpx.ts`: `independent HWPX consumer: PASS — title, paragraph, OPF spine` (previous run, not a fresh verification; uses the repository's existing `packs/elanous-hwp/test/consumer.py` only for verification).
22
+ - Mutation (this review, table): changed `converter.ts:157` replacement of internal newlines from `<br>` to empty; `bun test plugins/elanous-hwp/converter.test.ts -t 'fixed HWPX table sample retains cell line breaks and multiple cell paragraphs within GFM rows' 2>&1 | tail -12` returned `0 pass`, `1 fail`, `Ran 1 test across 1 file. [29.00ms]`. Failing check verbatim: `(fail) fixed HWPX table sample retains cell line breaks and multiple cell paragraphs within GFM rows [2.78ms]`. Restored `<br>`.
23
+ - Mutation (this review, ATX): changed `converter.ts:209` heading regex from `{1,6}` to `{7,8}`; `bun test plugins/elanous-hwp/converter.test.ts -t 'adjacent ATX headings terminate at the end of their line even without blank separators' 2>&1 | tail -10` returned `0 pass`, `1 fail`, `Ran 1 test across 1 file. [37.00ms]`. Failing check verbatim: `(fail) adjacent ATX headings terminate at the end of their line even without blank separators [2.73ms]`. Restored `{1,6}`; full focused file passed `8 pass`, `0 fail` afterward.
24
+ - Preservation: `git status --short -- packs/elanous-hwp` returns no lines; the separate existing pack is unchanged. English release note: `release/next.md:30`. Review disposition: both must-fixes apply and are compatible with the requested Markdown and HWPX conversion; no refutation.
25
+ - Commit status: intentionally uncommitted. This self-implement child is explicitly prohibited from git commit/push/PR/branch changes; the parent harness owns the approval-gated commit.
@@ -0,0 +1,22 @@
1
+ import { readFileSync, writeFileSync } from 'node:fs';
2
+ import { hwpxToMarkdown, markdownToHwpx } from './converter.js';
3
+
4
+ export function main(args: string[]): void {
5
+ const [command, input, output] = args;
6
+ if (!input || !output || !['to-md', 'from-md'].includes(command ?? '')) {
7
+ throw new Error('Usage: bun plugins/elanous-hwp/cli.ts to-md input.hwpx output.md | from-md input.md output.hwpx');
8
+ }
9
+ if (/\.hwp$/i.test(input) || /\.hwp$/i.test(output)) throw new Error('HWP(옛 바이너리)는 지원 안 함 · hwpx 로 저장해 달라');
10
+ if (command === 'to-md') {
11
+ if (!/\.hwpx$/i.test(input) || !/\.md$/i.test(output)) throw new Error('Expected .hwpx input and .md output');
12
+ writeFileSync(output, hwpxToMarkdown(readFileSync(input), input), { flag: 'wx' });
13
+ } else {
14
+ if (!/\.md$/i.test(input) || !/\.hwpx$/i.test(output)) throw new Error('Expected .md input and .hwpx output');
15
+ writeFileSync(output, markdownToHwpx(readFileSync(input, 'utf8')), { flag: 'wx' });
16
+ }
17
+ }
18
+
19
+ if (import.meta.main) {
20
+ try { main(process.argv.slice(2)); }
21
+ catch (error) { console.error(error instanceof Error ? error.message : error); process.exitCode = 1; }
22
+ }
@@ -0,0 +1,237 @@
1
+ import { deflateRawSync, inflateRawSync } from 'node:zlib';
2
+
3
+ const encoder = new TextEncoder();
4
+ const decoder = new TextDecoder('utf-8', { fatal: true });
5
+ const HP = 'http://www.hancom.co.kr/hwpml/2011/paragraph';
6
+ const HH = 'http://www.hancom.co.kr/hwpml/2011/head';
7
+ /** HWPX section root lives in the section namespace (OWPML `hs:sec`), not the paragraph one. */
8
+ const HS = 'http://www.hancom.co.kr/hwpml/2011/section';
9
+ const OPF = 'http://www.idpf.org/2007/opf/';
10
+ const HWP_NOTICE = 'HWP(옛 바이너리)는 지원 안 함 · hwpx 로 저장해 달라';
11
+
12
+ type XmlNode = { name: string; attrs: Record<string, string>; children: XmlNode[]; content: (XmlNode | string)[] };
13
+ const local = (name: string) => name.split(':').pop()!;
14
+ const xmlEscape = (value: string) => value.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
15
+ function unescapeXml(value: string): string {
16
+ return value.replace(/&(#x[\da-f]+|#\d+|amp|lt|gt|quot|apos);/gi, (_, entity: string) => {
17
+ const named: Record<string, string> = { amp: '&', lt: '<', gt: '>', quot: '"', apos: "'" };
18
+ if (entity[0] !== '#') return named[entity] ?? `&${entity};`;
19
+ const cp = entity[1]?.toLowerCase() === 'x' ? parseInt(entity.slice(2), 16) : parseInt(entity.slice(1), 10);
20
+ if (cp > 0x10ffff || cp === 0 || (cp >= 0xd800 && cp <= 0xdfff)) throw new Error('Invalid XML character');
21
+ return String.fromCodePoint(cp);
22
+ });
23
+ }
24
+ function parseXml(xml: string): XmlNode {
25
+ if (/<!DOCTYPE|<!ENTITY/i.test(xml)) throw new Error('External XML entities are not supported');
26
+ const root: XmlNode = { name: '', attrs: {}, children: [], content: [] };
27
+ const stack = [root];
28
+ const tokens = xml.match(/<[^>]*>|[^<]+/g) ?? [];
29
+ for (const token of tokens) {
30
+ if (token.startsWith('<?') || token.startsWith('<!--')) continue;
31
+ if (token.startsWith('</')) {
32
+ if (local(stack.at(-1)!.name) !== local(token.slice(2, -1).trim()) || stack.length === 1) throw new Error('Invalid HWPX XML');
33
+ stack.pop();
34
+ } else if (token.startsWith('<')) {
35
+ if (token.startsWith('<!')) throw new Error('Unsupported HWPX XML declaration');
36
+ const match = /^<([\w:.-]+)(?:\s|\/?>)/.exec(token);
37
+ if (!match) throw new Error('Invalid HWPX XML');
38
+ const attrs: Record<string, string> = {};
39
+ for (const attr of token.slice(match[0].length).matchAll(/([\w:.-]+)\s*=\s*(["'])(.*?)\2/g)) attrs[local(attr[1]!)] = unescapeXml(attr[3]!);
40
+ const node: XmlNode = { name: local(match[1]!), attrs, children: [], content: [] };
41
+ stack.at(-1)!.children.push(node);
42
+ stack.at(-1)!.content.push(node);
43
+ if (!token.endsWith('/>')) stack.push(node);
44
+ } else {
45
+ stack.at(-1)!.content.push(unescapeXml(token));
46
+ }
47
+ }
48
+ if (stack.length !== 1 || root.children.length !== 1) throw new Error('Invalid HWPX XML');
49
+ return root.children[0]!;
50
+ }
51
+ const children = (node: XmlNode, name: string) => node.children.filter(child => child.name === name);
52
+ const descendants = (node: XmlNode, name: string): XmlNode[] => node.children.flatMap(child => [ ...(child.name === name ? [child] : []), ...descendants(child, name) ]);
53
+ const nodeText = (node: XmlNode): string => node.name === 'lineBreak' ? '\n' : node.name === 'tab' ? '\t' : node.content.map(part => typeof part === 'string' ? (node.name === 't' ? part : '') : nodeText(part)).join('');
54
+
55
+ function crc32(bytes: Uint8Array): number {
56
+ let crc = -1;
57
+ for (const byte of bytes) {
58
+ crc ^= byte;
59
+ for (let i = 0; i < 8; i++) crc = (crc >>> 1) ^ (crc & 1 ? 0xedb88320 : 0);
60
+ }
61
+ return (crc ^ -1) >>> 0;
62
+ }
63
+ function readZip(bytes: Uint8Array): Map<string, Uint8Array> {
64
+ const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
65
+ let eocd = -1;
66
+ for (let i = bytes.length - 22; i >= Math.max(0, bytes.length - 65557); i--) {
67
+ if (view.getUint32(i, true) === 0x06054b50 && i + 22 + view.getUint16(i + 20, true) === bytes.length) { eocd = i; break; }
68
+ }
69
+ if (eocd < 0 || view.getUint16(eocd + 4, true) !== 0 || view.getUint16(eocd + 6, true) !== 0) throw new Error('Invalid HWPX ZIP');
70
+ if (view.getUint16(eocd + 8, true) !== view.getUint16(eocd + 10, true) || view.getUint32(eocd + 16, true) + view.getUint32(eocd + 12, true) > eocd) throw new Error('Invalid HWPX ZIP directory');
71
+ const count = view.getUint16(eocd + 10, true);
72
+ if (count > 1024) throw new Error('HWPX ZIP has too many entries');
73
+ let cursor = view.getUint32(eocd + 16, true);
74
+ const entries = new Map<string, Uint8Array>();
75
+ const directoryEnd = view.getUint32(eocd + 16, true) + view.getUint32(eocd + 12, true);
76
+ for (let i = 0; i < count; i++) {
77
+ if (cursor + 46 > directoryEnd || view.getUint32(cursor, true) !== 0x02014b50) throw new Error('Invalid HWPX ZIP directory');
78
+ const flags = view.getUint16(cursor + 8, true), method = view.getUint16(cursor + 10, true);
79
+ const crc = view.getUint32(cursor + 16, true), packed = view.getUint32(cursor + 20, true), size = view.getUint32(cursor + 24, true);
80
+ const nameLen = view.getUint16(cursor + 28, true), extraLen = view.getUint16(cursor + 30, true), commentLen = view.getUint16(cursor + 32, true);
81
+ if (flags & 1 || size > 32 * 1024 * 1024 || packed > 32 * 1024 * 1024 || cursor + 46 + nameLen + extraLen + commentLen > bytes.length) throw new Error('Unsupported HWPX ZIP entry');
82
+ const name = decoder.decode(bytes.subarray(cursor + 46, cursor + 46 + nameLen));
83
+ const offset = view.getUint32(cursor + 42, true);
84
+ if (offset + 30 > bytes.length || view.getUint32(offset, true) !== 0x04034b50) throw new Error('Invalid HWPX ZIP entry');
85
+ const start = offset + 30 + view.getUint16(offset + 26, true) + view.getUint16(offset + 28, true);
86
+ if (start + packed > bytes.length) throw new Error('Truncated HWPX ZIP entry');
87
+ const content = method === 0 ? bytes.slice(start, start + packed) : method === 8 ? inflateRawSync(bytes.subarray(start, start + packed), { maxOutputLength: 32 * 1024 * 1024 }) : null;
88
+ if (!content || content.length !== size || crc32(content) !== crc || entries.has(name)) throw new Error('Invalid HWPX ZIP entry: ' + name);
89
+ entries.set(name, content);
90
+ cursor += 46 + nameLen + extraLen + commentLen;
91
+ }
92
+ if (cursor !== directoryEnd) throw new Error('Invalid HWPX ZIP directory');
93
+ return entries;
94
+ }
95
+ function writeZip(files: [string, string][]): Uint8Array {
96
+ const parts: Uint8Array[] = [], directory: Uint8Array[] = [];
97
+ let offset = 0;
98
+ const uint16 = (view: DataView, at: number, n: number) => view.setUint16(at, n, true);
99
+ const uint32 = (view: DataView, at: number, n: number) => view.setUint32(at, n >>> 0, true);
100
+ for (const [name, value] of files) {
101
+ const filename = encoder.encode(name), body = encoder.encode(value);
102
+ const method = name === 'mimetype' ? 0 : 8;
103
+ const packed = method === 0 ? body : deflateRawSync(body);
104
+ const crc = crc32(body);
105
+ const header = new Uint8Array(30 + filename.length), h = new DataView(header.buffer);
106
+ uint32(h, 0, 0x04034b50); uint16(h, 4, 20); uint16(h, 6, 0x800); uint16(h, 8, method);
107
+ uint32(h, 14, crc); uint32(h, 18, packed.length); uint32(h, 22, body.length); uint16(h, 26, filename.length);
108
+ header.set(filename, 30);
109
+ parts.push(header, packed);
110
+ const central = new Uint8Array(46 + filename.length), c = new DataView(central.buffer);
111
+ uint32(c, 0, 0x02014b50); uint16(c, 4, 20); uint16(c, 6, 20); uint16(c, 8, 0x800); uint16(c, 10, method);
112
+ uint32(c, 16, crc); uint32(c, 20, packed.length); uint32(c, 24, body.length); uint16(c, 28, filename.length); uint32(c, 42, offset);
113
+ central.set(filename, 46); directory.push(central);
114
+ offset += header.length + packed.length;
115
+ }
116
+ const dirSize = directory.reduce((sum, part) => sum + part.length, 0);
117
+ const end = new Uint8Array(22), e = new DataView(end.buffer);
118
+ uint32(e, 0, 0x06054b50); uint16(e, 8, files.length); uint16(e, 10, files.length); uint32(e, 12, dirSize); uint32(e, 16, offset);
119
+ const result = new Uint8Array(offset + dirSize + end.length);
120
+ let pos = 0;
121
+ for (const part of [...parts, ...directory, end]) { result.set(part, pos); pos += part.length; }
122
+ return result;
123
+ }
124
+ function sections(zip: Map<string, Uint8Array>): { paths: string[]; paraProps: Map<string, XmlNode> } {
125
+ const container = zip.get('META-INF/container.xml');
126
+ if (!container) throw new Error('HWPX container.xml is missing');
127
+ const path = descendants(parseXml(decoder.decode(container)), 'rootfile')[0]?.attrs['full-path'];
128
+ const opf = path && zip.get(path);
129
+ if (!opf || !path) throw new Error('HWPX package is missing');
130
+ const pkg = parseXml(decoder.decode(opf));
131
+ const manifest = new Map(descendants(pkg, 'item').map(item => [item.attrs.id, item.attrs.href]));
132
+ const base = path.slice(0, path.lastIndexOf('/') + 1);
133
+ const ordered = descendants(pkg, 'itemref').map(ref => {
134
+ const href = manifest.get(ref.attrs.idref);
135
+ if (!href) return undefined;
136
+ const relative = [...base.split('/'), ...href.split('/')].reduce<string[]>((parts, part) => {
137
+ if (part === '..') parts.pop(); else if (part && part !== '.') parts.push(part);
138
+ return parts;
139
+ }, []).join('/');
140
+ return relative.endsWith('.xml') && zip.has(relative) && parseXml(decoder.decode(zip.get(relative)!)).name === 'sec' ? relative : undefined;
141
+ }).filter((name): name is string => !!name);
142
+ if (!ordered.length) throw new Error('HWPX section is missing');
143
+ const headerHref = [...manifest.values()].find(href => href?.endsWith('header.xml'));
144
+ const headerPath = headerHref && [...base.split('/'), ...headerHref.split('/')].reduce<string[]>((parts, part) => {
145
+ if (part === '..') parts.pop(); else if (part && part !== '.') parts.push(part);
146
+ return parts;
147
+ }, []).join('/');
148
+ const header = headerPath && zip.get(headerPath);
149
+ const paraProps = new Map<string, XmlNode>();
150
+ if (header) for (const prop of descendants(parseXml(decoder.decode(header)), 'paraPr')) {
151
+ if (prop.attrs.id !== undefined) paraProps.set(prop.attrs.id, prop);
152
+ }
153
+ return { paths: ordered, paraProps };
154
+ }
155
+ function tableMarkdown(table: XmlNode): string {
156
+ const cellText = (tc: XmlNode): string => {
157
+ const paragraphs = descendants(tc, 'p');
158
+ const text = paragraphs.length ? paragraphs.map(nodeText).join('\n') : nodeText(tc);
159
+ return text.replace(/\\/g, '\\\\').replace(/\|/g, '\\|').replace(/\r\n|\r|\n/g, '<br>');
160
+ };
161
+ const rows = children(table, 'tr').map(tr => children(tr, 'tc').map(cellText));
162
+ if (!rows.length) return '';
163
+ return [rows[0], rows[0]!.map(() => '---'), ...rows.slice(1)].map(row => '| ' + row.join(' | ') + ' |').join('\n');
164
+ }
165
+ function paragraphMarkdown(p: XmlNode, paraProps: Map<string, XmlNode>): string[] {
166
+ const blocks: string[] = [];
167
+ let text = '';
168
+ const prop = paraProps.get(p.attrs.paraPrIDRef ?? '');
169
+ const heading = prop && children(prop, 'heading')[0];
170
+ const level = heading?.attrs.type === 'OUTLINE' ? Number(heading.attrs.level) : NaN;
171
+ const prefix = Number.isInteger(level) && level >= 0 && level <= 5 ? '#'.repeat(level + 1) + ' ' : '';
172
+ const flush = () => {
173
+ if (text.trim()) blocks.push(prefix + text.trim());
174
+ text = '';
175
+ };
176
+ const visit = (node: XmlNode): void => {
177
+ if (node.name === 'tbl') {
178
+ flush();
179
+ const md = tableMarkdown(node);
180
+ if (md) blocks.push(md);
181
+ } else if (node.name === 't' || node.name === 'lineBreak' || node.name === 'tab') {
182
+ text += nodeText(node);
183
+ } else {
184
+ for (const part of node.content) if (typeof part !== 'string') visit(part);
185
+ }
186
+ };
187
+ for (const run of children(p, 'run')) visit(run);
188
+ flush();
189
+ return blocks;
190
+ }
191
+ export function hwpxToMarkdown(bytes: Uint8Array, filename = 'document.hwpx'): string {
192
+ const oleSignature = [0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1];
193
+ if (/\.hwp$/i.test(filename) || (bytes.length >= oleSignature.length && oleSignature.every((byte, i) => bytes[i] === byte))) throw new Error(HWP_NOTICE);
194
+ const zip = readZip(bytes);
195
+ if (decoder.decode(zip.get('mimetype') ?? new Uint8Array()) !== 'application/hwp+zip') throw new Error('Not an HWPX document');
196
+ const { paths, paraProps } = sections(zip);
197
+ const blocks = paths.flatMap(path => children(parseXml(decoder.decode(zip.get(path)!)), 'p').flatMap(p => paragraphMarkdown(p, paraProps)));
198
+ return blocks.length ? blocks.join('\n\n') + '\n' : '';
199
+ }
200
+ export function markdownToHwpx(markdown: string): Uint8Array {
201
+ const paragraphs = markdown.replace(/\r\n/g, '\n').trim().split(/\n\s*\n/).filter(Boolean).flatMap(block => {
202
+ const lines = block.split('\n');
203
+ if (lines.some(line => /^\|.*\|$/.test(line))) throw new Error('Markdown tables are not supported by the minimal HWPX writer');
204
+ const result: { style: number; text: string }[] = [];
205
+ let body: string[] = [];
206
+ const flush = () => {
207
+ if (body.length) result.push({ style: 0, text: body.join('\n') });
208
+ body = [];
209
+ };
210
+ for (const line of lines) {
211
+ const heading = /^(#{1,6}) +(.+)$/.exec(line);
212
+ if (heading) {
213
+ flush();
214
+ result.push({ style: heading[1]!.length, text: heading[2]! });
215
+ } else body.push(line);
216
+ }
217
+ flush();
218
+ return result;
219
+ }).map(({ style, text }) => {
220
+ const content = text.split('\n').map(line => line.split('\t').map(xmlEscape).join('<hp:tab/>')).join('<hp:lineBreak/>');
221
+ return `<hp:p id="0" paraPrIDRef="${style}" styleIDRef="0" pageBreak="0" columnBreak="0" merged="0"><hp:run charPrIDRef="0"><hp:t>${content}</hp:t></hp:run></hp:p>`;
222
+ }).join('');
223
+ const section = `<?xml version="1.0" encoding="UTF-8"?><hs:sec xmlns:hs="${HS}" xmlns:hp="${HP}"><hp:p id="0" paraPrIDRef="0" styleIDRef="0" pageBreak="0" columnBreak="0" merged="0"><hp:run charPrIDRef="0"><hp:secPr id="0" textDirection="HORIZONTAL" spaceColumns="0" tabStop="8000"><hp:pagePr landscape="WIDELY" width="59528" height="84188" gutterType="LEFT_ONLY"/><hp:pageMargin left="5660" right="5660" top="5660" bottom="5660"/></hp:secPr></hp:run></hp:p>${paragraphs}</hs:sec>`;
224
+ const langs = ['HANGUL', 'LATIN', 'HANJA', 'JAPANESE', 'OTHER', 'SYMBOL', 'USER'];
225
+ const fonts = langs.map(lang => `<hh:fontface lang="${lang}" fontCnt="1"><hh:font id="0" face="Malgun Gothic" type="TTF" isEmbedded="0"/></hh:fontface>`).join('');
226
+ const fontRefs = langs.map(lang => `${lang.toLowerCase()}="0"`).join(' ');
227
+ const charProps = `<hh:charPr id="0" height="1000" textColor="#000000" shadeColor="none" bold="0" italic="0"><hh:fontRef ${fontRefs}/></hh:charPr>`;
228
+ const paraProps = Array.from({ length: 7 }, (_, i) => `<hh:paraPr id="${i}" tabPrIDRef="0"><hh:heading type="${i ? 'OUTLINE' : 'NONE'}" level="${i ? i - 1 : 0}"/><hh:align horizontal="LEFT" vertical="BASELINE"/></hh:paraPr>`).join('');
229
+ const header = `<?xml version="1.0" encoding="UTF-8"?><hh:head xmlns:hh="${HH}" version="1.4" secCnt="1"><hh:beginNum page="1" footnote="1" endnote="1" pic="1" tbl="1" equation="1"/><hh:refList><hh:fontfaces itemCnt="1">${fonts}</hh:fontfaces><hh:charProperties itemCnt="1">${charProps}</hh:charProperties><hh:paraProperties itemCnt="7">${paraProps}</hh:paraProperties><hh:styles itemCnt="1"><hh:style id="0" type="PARA" name="Normal" engName="Normal" paraPrIDRef="0" charPrIDRef="0" nextStyleIDRef="0" langID="1042"/></hh:styles></hh:refList></hh:head>`;
230
+ const content = `<?xml version="1.0" encoding="UTF-8"?><opf:package xmlns:opf="${OPF}" version="1.0"><opf:metadata/><opf:manifest><opf:item id="header" href="header.xml" media-type="application/xml"/><opf:item id="section0" href="section0.xml" media-type="application/xml"/></opf:manifest><opf:spine><opf:itemref idref="header"/><opf:itemref idref="section0"/></opf:spine></opf:package>`;
231
+ return writeZip([
232
+ ['mimetype', 'application/hwp+zip'],
233
+ ['version.xml', '<?xml version="1.0" encoding="UTF-8"?><HWPVersion major="5" minor="0" micro="0" buildNumber="0"/>'],
234
+ ['META-INF/container.xml', '<?xml version="1.0" encoding="UTF-8"?><container xmlns="urn:oasis:names:tc:opendocument:xmlns:container"><rootfiles><rootfile full-path="Contents/content.hpf" media-type="application/hwpml-package+xml"/></rootfiles></container>'],
235
+ ['Contents/content.hpf', content], ['Contents/header.xml', header], ['Contents/section0.xml', section],
236
+ ]);
237
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "$schema": "https://antigravity.google/schemas/v1/plugin.json",
3
+ "name": "elanous-hwp",
4
+ "version": "0.1.0",
5
+ "main": "./plugin.ts",
6
+ "description": "Convert HWPX documents to Markdown and write minimal HWPX from Markdown; legacy HWP is not supported.",
7
+ "extensions": {
8
+ "ai.elanous": {
9
+ "capabilities": ["fs:workdir", "proc:bun"],
10
+ "connectors": [],
11
+ "pricing": { "model": "free" },
12
+ "trust": "official",
13
+ "category": "Productivity"
14
+ }
15
+ }
16
+ }
@@ -0,0 +1,40 @@
1
+ import { readFileSync, writeFileSync } from 'node:fs';
2
+ import type { ElanousPlugin } from '../../src/plugins/core/types.js';
3
+ import { hwpxToMarkdown, markdownToHwpx } from './converter.js';
4
+
5
+ const plugin: ElanousPlugin = {
6
+ name: 'elanous-hwp',
7
+ version: '0.1.0',
8
+ description: 'Convert HWPX to Markdown and write minimal HWPX from Markdown.',
9
+ initialState: () => undefined,
10
+ panes: {},
11
+ llmTools: [
12
+ {
13
+ name: 'HwpxToMarkdown',
14
+ description: 'Read an HWPX ZIP document as Markdown (paragraphs, headings, tables). Legacy .hwp is unsupported.',
15
+ parameters: { type: 'object', properties: { path: { type: 'string' } }, required: ['path'] },
16
+ async handler(args) {
17
+ const path = args.path;
18
+ if (typeof path !== 'string') throw new Error('path must be a string');
19
+ if (/\.hwp$/i.test(path)) throw new Error('HWP(옛 바이너리)는 지원 안 함 · hwpx 로 저장해 달라');
20
+ if (!/\.hwpx$/i.test(path)) throw new Error('Expected .hwpx input');
21
+ return { markdown: hwpxToMarkdown(readFileSync(path), path) };
22
+ },
23
+ },
24
+ {
25
+ name: 'MarkdownToHwpx',
26
+ description: 'Write paragraphs and headings from a Markdown file to a new HWPX ZIP document.',
27
+ parameters: { type: 'object', properties: { path: { type: 'string' }, output: { type: 'string' } }, required: ['path', 'output'] },
28
+ async handler(args) {
29
+ const { path, output } = args;
30
+ if (typeof path !== 'string' || typeof output !== 'string') throw new Error('path and output must be strings');
31
+ if (/\.hwp$/i.test(path) || /\.hwp$/i.test(output)) throw new Error('HWP(옛 바이너리)는 지원 안 함 · hwpx 로 저장해 달라');
32
+ if (!/\.md$/i.test(path) || !/\.hwpx$/i.test(output)) throw new Error('Expected .md input and .hwpx output');
33
+ writeFileSync(output, markdownToHwpx(readFileSync(path, 'utf8')), { flag: 'wx' });
34
+ return { output };
35
+ },
36
+ },
37
+ ],
38
+ };
39
+
40
+ export default plugin;
@@ -3,12 +3,15 @@
3
3
  * manifest transforms and private redactions applied, then the leak markers. A hit here fails the next prepare
4
4
  * (0.2.7: one test title), so `pr land` and the harness policy gates run it before landing.
5
5
  *
6
- * bun scripts/ci-public-export-leak-gate.ts --changed-files a b … # exit 1 on a hit · 0 when clean or not measurable
6
+ * bun scripts/ci-public-export-leak-gate.ts --changed-files a b … # exit 1 on a hit · 0 when clean
7
7
  */
8
8
  import { spawnSync } from 'node:child_process';
9
- import { existsSync } from 'node:fs';
10
- import { join, resolve } from 'node:path';
11
- import { privateRedactionsPath } from './public-export.js';
9
+ import { existsSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
10
+ import { tmpdir } from 'node:os';
11
+ import { join, relative, resolve } from 'node:path';
12
+ import { stringify } from 'yaml';
13
+ import { computeSkillBoundaries, filterFilesBySkillVerdict } from './skill-boundary.js';
14
+ import { loadExportConfig, privateRedactionsPath, selectExportFiles } from './public-export.js';
12
15
 
13
16
  export interface ExportLeakCheck {
14
17
  readonly measured: boolean;
@@ -16,29 +19,87 @@ export interface ExportLeakCheck {
16
19
  readonly detail?: string;
17
20
  }
18
21
 
19
- export function exportLeakCheck(changedFiles: readonly string[], root: string): ExportLeakCheck {
22
+ /** Changed source paths that the public manifest would export; unknown skill boundaries remain in scope. */
23
+ export function publicExportChangedFiles(changedFiles: readonly string[], root: string): string[] {
24
+ const config = loadExportConfig(root);
25
+ const selected = [...new Set([
26
+ ...selectExportFiles(changedFiles, config),
27
+ ...changedFiles.filter((file) => Object.values(config.replace).includes(file)),
28
+ ])];
29
+ if (!config.skills || !selected.some((file) => file.startsWith('skills/'))) return selected;
30
+ try {
31
+ const boundary = computeSkillBoundaries(root);
32
+ if (boundary.errors.length) return selected;
33
+ return filterFilesBySkillVerdict(selected, boundary, config.skills);
34
+ } catch { return selected; }
35
+ }
36
+
37
+ export function exportLeakCheck(changedFiles: readonly string[], root: string, spawn: typeof spawnSync = spawnSync): ExportLeakCheck {
20
38
  const script = join(root, 'scripts', 'public-export.ts');
21
39
  if (!existsSync(script)) return { measured: false, hits: [], detail: 'scripts/public-export.ts 없음' };
22
40
  // Without the private table (a Pod, a fresh machine) the markers still run; private identifiers are not checked.
23
41
  const privateList = existsSync(privateRedactionsPath()) ? [] : ['--no-private-list'];
24
- const r = spawnSync('bun', [script, '--leak-check', '--json', '--all', ...privateList, '--files', ...changedFiles], { cwd: root, encoding: 'utf-8', timeout: 180_000, maxBuffer: 64 * 1024 * 1024 });
42
+ const args = ['--leak-check', '--json', '--all', ...privateList, '--files', ...changedFiles];
43
+ const run = (flags: string[]) => spawn('bun', [script, ...flags], { cwd: root, encoding: 'utf-8', timeout: 180_000, maxBuffer: 64 * 1024 * 1024 });
44
+ const initial = run(args);
45
+ if (initial.status !== 2) return measuredLeaks(initial);
46
+
47
+ const hits = [...String(initial.stderr).matchAll(/^⛔ skill boundary: ([^:\r\n]+):[^\r\n]*$/gm)]
48
+ .map((match) => ({ file: `skills/${match[1]}/SKILL.md`, line: 1, marker: 'skill-boundary' }));
49
+ if (!hits.length || !String(initial.stderr).trim().split(/\r?\n/).every((line) => /^⛔ skill boundary: [^:\r\n]+:[^\r\n]*$/.test(line))) {
50
+ return { measured: false, hits: [], detail: 'rc=2' };
51
+ }
52
+
53
+ // The exporter stops before scanning any changed file on a boundary error. Re-run its
54
+ // same transforms and leak check with only boundary selection disabled; retain the boundary hits.
55
+ let dir: string | undefined;
56
+ try {
57
+ const config = loadExportConfig(root);
58
+ dir = mkdtempSync(join(tmpdir(), 'public-export-leak-'));
59
+ const configPath = join(dir, 'export.yaml');
60
+ writeFileSync(configPath, stringify({ ...config, skills: undefined }));
61
+ const measured = measuredLeaks(run(['--config', relative(root, configPath), ...args]));
62
+ return { ...measured, hits: [...hits, ...measured.hits] };
63
+ } catch {
64
+ return { measured: false, hits, detail: 'skill boundary 뒤 재검사 실패' };
65
+ } finally {
66
+ if (dir) rmSync(dir, { recursive: true, force: true });
67
+ }
68
+ }
69
+
70
+ function measuredLeaks(r: ReturnType<typeof spawnSync>): ExportLeakCheck {
25
71
  if (r.status !== 0 && r.status !== 1) return { measured: false, hits: [], detail: `rc=${r.status ?? r.signal}` };
26
72
  try {
27
- const parsed = JSON.parse(String(r.stdout).trim().split('\n').at(-1) ?? '') as { hits?: Array<{ file: string; line: number; marker: string }> };
28
- return { measured: true, hits: (parsed.hits ?? []).map((h) => ({ file: h.file, line: h.line, marker: h.marker })) };
73
+ const parsed: unknown = JSON.parse(String(r.stdout).trim().split('\n').at(-1) ?? '');
74
+ if (!parsed || typeof parsed !== 'object' || !('hits' in parsed) || !Array.isArray(parsed.hits)
75
+ || parsed.hits.some((h: unknown) => !h || typeof h !== 'object'
76
+ || typeof (h as { file?: unknown }).file !== 'string'
77
+ || typeof (h as { line?: unknown }).line !== 'number'
78
+ || typeof (h as { marker?: unknown }).marker !== 'string')) throw new Error('invalid hits');
79
+ return { measured: true, hits: parsed.hits.map((h: { file: string; line: number; marker: string }) => ({ file: h.file, line: h.line, marker: h.marker })) };
29
80
  } catch { return { measured: false, hits: [], detail: 'JSON 을 못 읽었다' }; }
30
81
  }
31
82
 
32
- /** Gate form for the harness policy gates: 1 on a hit, 0 when clean or not measurable (said in a line). */
83
+ /** Gate form for the harness policy gates: 1 on a hit, 0 when clean (said in a line). */
33
84
  export function runPublicExportLeakGate(io: { args: readonly string[]; cwd: string; log: (line: string) => void; error: (line: string) => void }): number {
34
85
  const at = io.args.indexOf('--changed-files');
35
86
  const files = at >= 0 ? io.args.slice(at + 1).filter((a) => !a.startsWith('--')) : [];
36
87
  if (files.length === 0) return 0;
37
- const result = exportLeakCheck(files, io.cwd);
38
- if (!result.measured) { io.log(`⚠ public-export-leak: 못 쟀다(${result.detail ?? '?'})`); return 0; }
39
- if (result.hits.length === 0) return 0;
40
- io.error(`✗ public-export-leak: 바뀐 파일 ${result.hits.length}곳이 공개본에 사적 흔적을 싣는다 — 다음 release prepare 가 여기서 실패한다:`);
41
- for (const h of result.hits) io.error(` ${h.file}:${h.line} ${h.marker}`);
88
+ let result: ExportLeakCheck;
89
+ try { result = exportLeakCheck(files, io.cwd); }
90
+ catch { result = { measured: false, hits: [], detail: '검사 실행 실패' }; }
91
+ if (!result.measured) {
92
+ let publicFiles: string[];
93
+ try { publicFiles = publicExportChangedFiles(files, io.cwd); }
94
+ catch { publicFiles = files; }
95
+ if (publicFiles.length === 0) { io.log('⚠ public-export-leak: 못 쟀다 — 공개 대상 변경 없음'); return 0; }
96
+ io.error(`✗ public-export-leak: 못 쟀다 — 막는다(${result.detail ?? '?'})`);
97
+ return 1;
98
+ }
99
+ const hits = result.hits.filter((hit) => hit.marker !== 'skill-boundary' || files.includes(hit.file));
100
+ if (hits.length === 0) return 0;
101
+ io.error(`✗ public-export-leak: 바뀐 파일 ${hits.length}곳이 공개본에 사적 흔적을 싣는다 — 다음 release prepare 가 여기서 실패한다:`);
102
+ for (const h of hits) io.error(` ${h.file}:${h.line} · ${h.marker}`);
42
103
  io.error(' 그 줄에서 표식·홈 경로·내부 문서 이름을 빼라(주석이면 공개본 변환이 지우는지 bun scripts/public-export.ts --leak-check --files <파일> 로 확인).');
43
104
  return 1;
44
105
  }
@@ -10,8 +10,8 @@
10
10
  * bun scripts/ci-public-leak-gate.ts --changed-files a b … # 그 파일들만 검사
11
11
  * bun scripts/ci-public-leak-gate.ts --update # 기준선 재스냅샷(치운 뒤)
12
12
  *
13
- * ⛔ 이 게이트는 아직 `pr land` 에 «배선하지 않았다» — 다른 트랙의 주석 관례(대표 표기 기호 등)가 매일 늘어 배선 즉시 착지가 막힌다.
14
- * 배선은 조율 채널 합의 뒤(2026-09-24 🅢).
13
+ * `pr land` 는 변경 파일을 PR 트리에서 스캔하고 기준선은 base ref 에서 읽어 방향을 경고 전용으로 표시한다.
14
+ * 단독 CLI 의 기본 기준선은 현재 트리의 파일이며, 증가는 exit 1 이다.
15
15
  */
16
16
  import { existsSync, readFileSync, writeFileSync } from 'node:fs';
17
17
  import { join, resolve } from 'node:path';
@@ -57,6 +57,19 @@ export function renderBaseline(counts: ReadonlyMap<string, number>): string {
57
57
 
58
58
  export interface LeakGrowth { readonly marker: string; readonly file: string; readonly allowed: number; readonly now: number }
59
59
 
60
+ /** 기준선과 현재 트리의 모든 차이(사라진 표지 포함). */
61
+ export function findLeakChanges(current: ReadonlyMap<string, number>, baseline: ReadonlyMap<string, number>, only?: ReadonlySet<string>): LeakGrowth[] {
62
+ const out: LeakGrowth[] = [];
63
+ for (const key of new Set([...baseline.keys(), ...current.keys()])) {
64
+ const [marker, file] = key.split('\t') as [string, string];
65
+ if (only && !only.has(file)) continue;
66
+ const allowed = baseline.get(key) ?? 0;
67
+ const now = current.get(key) ?? 0;
68
+ if (now !== allowed) out.push({ marker, file, allowed, now });
69
+ }
70
+ return out.sort((a, b) => a.file.localeCompare(b.file) || a.marker.localeCompare(b.marker));
71
+ }
72
+
60
73
  /** 기준선보다 «늘어난» (표지, 파일). `only` 가 있으면 그 파일만 본다. */
61
74
  export function findGrowth(current: ReadonlyMap<string, number>, baseline: ReadonlyMap<string, number>, only?: ReadonlySet<string>): LeakGrowth[] {
62
75
  const out: LeakGrowth[] = [];
@@ -81,6 +94,8 @@ export interface PublicLeakGateIo {
81
94
  readonly args?: readonly string[];
82
95
  readonly root?: string;
83
96
  readonly scan?: () => LeakHit[];
97
+ /** Read the baseline from the landing base commit, not the PR's working tree. */
98
+ readonly baselineRef?: string;
84
99
  readonly log?: (s: string) => void;
85
100
  readonly error?: (s: string) => void;
86
101
  }
@@ -100,14 +115,34 @@ export function runPublicLeakGate(io: PublicLeakGateIo = {}): number {
100
115
  log(`[public-leak-gate] baseline updated — ${[...current.values()].reduce((a, n) => a + n, 0)} hits · ${current.size} entries`);
101
116
  return 0;
102
117
  }
103
- if (!existsSync(baselinePath)) { error(`[public-leak-gate] FAIL — 기준선이 없다: ${BASELINE_REL} (--update 로 만든다)`); return 2; }
104
- const growth = findGrowth(current, parseBaseline(readFileSync(baselinePath, 'utf8')), only);
118
+ let baselineText: string;
119
+ if (io.baselineRef) {
120
+ const shown = spawnSync('git', ['-C', root, 'show', `${io.baselineRef}:${BASELINE_REL}`], { encoding: 'utf8', maxBuffer: 1 << 28 });
121
+ if (shown.status !== 0) { error(`[public-leak-gate] FAIL — ${io.baselineRef} 기준선을 못 읽었다: ${BASELINE_REL}`); return 2; }
122
+ baselineText = shown.stdout;
123
+ } else {
124
+ if (!existsSync(baselinePath)) { error(`[public-leak-gate] FAIL — 기준선이 없다: ${BASELINE_REL} (--update 로 만든다)`); return 2; }
125
+ baselineText = readFileSync(baselinePath, 'utf8');
126
+ }
127
+ const baseline = parseBaseline(baselineText);
128
+ const growth = findGrowth(current, baseline, only);
105
129
  const scope = only ? `변경 파일 ${only.size}개` : '전체';
106
- if (growth.length === 0) { log(`[public-leak-gate] PASS — 유출이 늘지 않았다 (${scope})`); return 0; }
107
- error(`[public-leak-gate] FAIL — 유출이 늘었다 (${scope} · ${growth.length}곳):`);
108
- for (const g of growth) error(` ${g.file} ${g.marker} ${g.allowed} → ${g.now}`);
109
- error(' 확인: bun scripts/public-export.ts --leak-check --path <파일> --all');
110
- return 1;
130
+ if (growth.length > 0) {
131
+ error(`[public-leak-gate] FAIL — 유출이 늘었다 (${scope} · ${growth.length}곳):`);
132
+ for (const g of growth) error(` ${g.file} ${g.marker} ${g.allowed} → ${g.now}`);
133
+ error(' 확인: bun scripts/public-export.ts --leak-check --path <파일> --all');
134
+ return 1;
135
+ }
136
+ if (io.baselineRef) {
137
+ const shrunk = findLeakChanges(current, baseline, only).filter((change) => change.now < change.allowed);
138
+ if (shrunk.length > 0) {
139
+ log(`[public-leak-gate] SHRANK — 유출이 줄었다 · 기준선 --update 권장 (${scope} · ${shrunk.length}곳):`);
140
+ for (const g of shrunk) log(` ${g.file} ${g.marker} ${g.allowed} → ${g.now}`);
141
+ return 3;
142
+ }
143
+ }
144
+ log(`[public-leak-gate] PASS — 유출이 늘지 않았다 (${scope})`);
145
+ return 0;
111
146
  }
112
147
 
113
148
  if (import.meta.main) process.exit(runPublicLeakGate());
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env bash
2
+ # Claude Code Stop hook; hook data travels only through stdin to the bounded extractor.
3
+ SCRIPT_DIR="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)" || exit 0
4
+ bun "$SCRIPT_DIR/emit.ts" stop </dev/stdin >/dev/null 2>&1 || true