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
@@ -37,10 +37,50 @@ function firstObject(raw: string): Data {
37
37
  async function cli(bin: string, args: string[]): Promise<string> {
38
38
  const child = Bun.spawn([bin, ...args], { stdout: 'pipe', stderr: 'pipe', env: process.env });
39
39
  const [stdout, stderr, status] = await Promise.all([new Response(child.stdout).text(), new Response(child.stderr).text(), child.exited]);
40
- if (status !== 0) throw new Error(`${args[0]} 실패: ${(stderr || stdout).slice(0, 200)}`);
40
+ if (status !== 0) throw new Error(`${args[0]} 실패: ${(stderr || stdout).slice(0, 200)}`, { cause: `${stderr}\n${stdout}` });
41
41
  return stdout;
42
42
  }
43
43
 
44
+ const sleep = (ms: number) => new Promise<void>(resolve => setTimeout(resolve, ms));
45
+
46
+ function transientAskReason(error: unknown): string | null {
47
+ const message = error instanceof Error ? `${error.message}\n${String(error.cause ?? '')}` : String(error);
48
+ if (/socket connection was closed/i.test(message)) return 'socket';
49
+ if (/\bECONNRESET\b/i.test(message)) return 'ECONNRESET';
50
+ if (/\bETIMEDOUT\b/i.test(message)) return 'ETIMEDOUT';
51
+ if (/\bEPIPE\b/i.test(message)) return 'EPIPE';
52
+ if (/fetch failed/i.test(message)) return 'fetch';
53
+ const http = /\b(?:HTTP(?:\/[\d.]+)?\s*[:=]?\s*|status(?:\s+code)?\s*[:=]?\s*)(502|503|504|429)\b|\b(502|503|504|429)\s+(?:Bad Gateway|Service Unavailable|Gateway Timeout|Too Many Requests)\b/i.exec(message);
54
+ return http ? `HTTP${http[1] ?? http[2]}` : null;
55
+ }
56
+
57
+ export async function askWithRetry(payload: Data, run = cli, wait = sleep): Promise<Data> {
58
+ const args = ['ask', '--json', JSON.stringify(payload)];
59
+ let raw: string;
60
+ let retried = false;
61
+ try {
62
+ raw = await run(elanous, args);
63
+ } catch (error) {
64
+ const reason = transientAskReason(error);
65
+ if (!reason) throw error;
66
+ console.error(`card-followup: ask retry (${reason})`);
67
+ await wait(3000);
68
+ retried = true;
69
+ try {
70
+ raw = await run(elanous, args);
71
+ } catch (retryError) {
72
+ throw new Error(`${retryError instanceof Error ? retryError.message : String(retryError)} · 한 번 다시 했지만 실패`);
73
+ }
74
+ }
75
+ try {
76
+ const response = JSON.parse(raw) as Data;
77
+ return firstObject(typeof response.reply === 'string' ? response.reply : JSON.stringify(response));
78
+ } catch (error) {
79
+ if (retried) throw new Error(`${error instanceof Error ? error.message : String(error)} · 한 번 다시 했지만 실패`);
80
+ throw error;
81
+ }
82
+ }
83
+
44
84
  function absolute(path: string, label: string): string {
45
85
  const expanded = path.startsWith('~/') ? join(homedir(), path.slice(2)) : path;
46
86
  if (!isAbsolute(expanded)) throw new Error(`${label}: 절대 경로 필요`);
@@ -82,13 +122,15 @@ function sources(raw: string): { title: string; url: string; snippet: string }[]
82
122
  });
83
123
  }
84
124
 
125
+ const elanous = process.env.CARD_FOLLOWUP_ELANOUS_BIN || 'elanous';
126
+
127
+ if (import.meta.main) {
85
128
  const contextFile = process.env.ELANOUS_GRAPH_CONTEXT;
86
129
  if (!contextFile) throw new Error('ELANOUS_GRAPH_CONTEXT 필요');
87
130
  const context = object(JSON.parse(readFileSync(contextFile, 'utf8')));
88
131
  const input = object(context.input);
89
132
  const outputs = object(context.outputs);
90
133
  const step = process.argv[2];
91
- const elanous = process.env.CARD_FOLLOWUP_ELANOUS_BIN || 'elanous';
92
134
  const codex = process.env.CARD_FOLLOWUP_CODEX_BIN || 'codex';
93
135
  const result = (data: Data) => console.log(JSON.stringify(data.outcome ? data : { outcome: 'ok', ...data }));
94
136
 
@@ -155,11 +197,7 @@ try {
155
197
  const read = object(outputs['read-card']);
156
198
  const card = object(read.card ?? read);
157
199
  const research = object(outputs.research);
158
- const ask = async (payload: Data): Promise<Data> => {
159
- const response = JSON.parse(await cli(elanous, ['ask', '--json', JSON.stringify(payload)])) as Data;
160
- return firstObject(typeof response.reply === 'string' ? response.reply : JSON.stringify(response));
161
- };
162
- const strategy = await buildStrategy({ card, research, context: text(input.context), offer: text(input.offer), ask });
200
+ const strategy = await buildStrategy({ card, research, context: text(input.context), offer: text(input.offer), ask: askWithRetry });
163
201
  const fallback = dirname(contextFile).endsWith('.json.contexts') ? dirname(contextFile).slice(0, -'.json.contexts'.length) : dirname(contextFile);
164
202
  const outDir = input.outDir === undefined ? fallback : absolute(text(input.outDir), 'outDir');
165
203
  const crm = input.crm === undefined ? join(outDir, 'crm.csv') : absolute(text(input.crm), 'crm');
@@ -182,10 +220,6 @@ try {
182
220
  const marker = /\s*\[S(\d+)\]/g;
183
221
  const strip = (value: string) => value.replace(marker, '').trim();
184
222
  const sentencesOf = (value: string) => value.split(/(?<=[.!?。!?])\s+|\n+/).map(s => s.trim()).filter(Boolean);
185
- const ask = async (payload: Data): Promise<Data> => {
186
- const response = JSON.parse(await cli(elanous, ['ask', '--json', JSON.stringify(payload)])) as Data;
187
- return firstObject(typeof response.reply === 'string' ? response.reply : JSON.stringify(response));
188
- };
189
223
  // A Korean meeting note on an English card used to be pasted verbatim into the English mail
190
224
  // («Hi Joosung, 마케터의 밤에서 인사.» · 10-01 dry run). Across languages the note is translated, not copied.
191
225
  const crossLanguage = language === 'en' && /[가-힣]/.test(contextLine);
@@ -200,7 +234,7 @@ try {
200
234
  let accepted: { warnings: string[]; draft: Data; facts: { text: string; url: string }[] } | undefined;
201
235
  let lastProblem = '';
202
236
  for (let attempt = 0; attempt < 3 && !accepted; attempt++) {
203
- const draft = await ask({ task: 'draft', instruction: instruction + feedback, card, context: contextLine, sender: text(input.sender), language, evidence: numbered, approach: { problem: text(approach.problem), proposal: text(approach.proposal), channel: text(approach.channel) } });
237
+ const draft = await askWithRetry({ task: 'draft', instruction: instruction + feedback, card, context: contextLine, sender: text(input.sender), language, evidence: numbered, approach: { problem: text(approach.problem), proposal: text(approach.proposal), channel: text(approach.channel) } });
204
238
  const parts = { subject: text(draft.subject), body: text(draft.body), linkedin: text(draft.linkedin), question: text(draft.question) };
205
239
  if (!parts.subject || !parts.body || !parts.linkedin || !parts.question) { lastProblem = '초안 필수 필드 없음'; feedback = ' 이전 응답에 필드가 빠졌다.'; continue; }
206
240
  const body = strip(parts.body), linkedin = strip(parts.linkedin);
@@ -218,7 +252,7 @@ try {
218
252
  if (badCite) shape.push(`없는 출처 번호: ${badCite.raw}`);
219
253
  if (shape.length) { lastProblem = shape.join(' · '); feedback = ` 이전 초안 문제: ${lastProblem}. 모두 고쳐 다시 작성하라.`; continue; }
220
254
  // Every sentence is judged against the card, the context and only the evidence it cites — a company name alone is identity, not a claim.
221
- const verdict = await ask({
255
+ const verdict = await askWithRetry({
222
256
  task: 'verify',
223
257
  instruction: '각 문장에 대해, 받는 사람·그 회사·시장에 관한 사실 주장이 명함 필드, context, 또는 그 문장이 인용한 evidence 로 뒷받침되는지 판정하라. 인사·호칭·명함에 있는 이름/회사/직함 언급·제안·질문·보내는 사람의 의도는 사실 주장이 아니다. JSON 객체만 출력: {"unsupported":[뒷받침 안 되는 문장 번호]}',
224
258
  card, context: contextLine,
@@ -274,3 +308,4 @@ try {
274
308
  } catch (error) {
275
309
  result({ outcome: 'fail', reason: error instanceof Error ? error.message : String(error) });
276
310
  }
311
+ }
@@ -3,12 +3,12 @@ version: 1
3
3
  entry_node: draft
4
4
  terminal_nodes: [done, failed]
5
5
  nodes:
6
- - { node_id: draft, kind: agent, recipe: 'cmd:draft', max_visits: 1 }
7
- - { node_id: check, kind: agent, recipe: 'cmd:check', max_visits: 1 }
6
+ - { node_id: draft, kind: agent, recipe: 'cmd:draft', max_visits: 2 }
7
+ - { node_id: check, kind: agent, recipe: 'cmd:check', max_visits: 2 }
8
8
  - { node_id: report, kind: agent, recipe: 'cmd:report', max_visits: 1 }
9
9
  - { node_id: done, kind: gate, max_visits: 1 }
10
10
  - { node_id: failed, kind: gate, max_visits: 1 }
11
11
  edges:
12
12
  - { from: draft, on: outcome, map: { ok: check, fail: failed } }
13
- - { from: check, on: outcome, map: { ok: report, fail: failed } }
13
+ - { from: check, on: outcome, map: { ok: report, revise: draft, fail: failed } }
14
14
  - { from: report, on: outcome, map: { ok: done, fail: failed } }
@@ -80,17 +80,24 @@ try {
80
80
  if (!topic) throw new Error('topic is required');
81
81
  const step = process.argv[2] ?? '';
82
82
  if (step === 'draft') {
83
- const prompt = `Write ONE ${kind} document in ${text(input.language) || 'ko'} for ${text(input.audience) || 'the intended reader'}. Request: ${topic}. Context (source material, not a claim to invent): ${text(input.context) || '(none)'}. Return Markdown only: one # title line and a substantive body of 15–${limits[typedKind]} whitespace-separated words. Cover every item in the request. ${kind === 'exec-onepager' ? 'Include exactly these four ## sections with substantive content: 요약, 핵심 숫자, 결정 요청, 다음 단계. For unknown numeric figures write “자료 미제공 — 확인 필요” under 핵심 숫자, rather than inventing numbers.' : ''} No empty placeholders or fabricated facts. This is a DRAFT only: never claim it was published, sent, paid for, or approved; do not perform those actions.`;
83
+ const previousReason = text(object(outputs.check).reason);
84
+ const previousDraft = text(object(outputs.draft).markdown);
85
+ const prompt = `Write ONE ${kind} document in ${text(input.language) || 'ko'} for ${text(input.audience) || 'the intended reader'}. Request: ${topic}. Context (source material, not a claim to invent): ${text(input.context) || '(none)'}. Return Markdown only: one # title line and a substantive body of 15–${limits[typedKind]} whitespace-separated words. Cover every item in the request. ${kind === 'exec-onepager' ? 'Include exactly these four ## sections with substantive content: 요약, 핵심 숫자, 결정 요청, 다음 단계. For unknown numeric figures write “자료 미제공 — 확인 필요” under 핵심 숫자, rather than inventing numbers.' : ''} No empty placeholders or fabricated facts. This is a DRAFT only: never claim it was published, sent, paid for, or approved; do not perform those actions.${previousReason ? ` The previous draft failed check for this reason — fix it and rewrite: ${previousReason}. Previous draft: ${previousDraft}` : ''}`;
84
86
  const markdown = await ask(prompt);
85
87
  console.log(JSON.stringify({ outcome: 'ok', markdown }));
86
88
  } else if (step === 'check') {
87
89
  const markdown = text(object(outputs.draft).markdown);
88
90
  const error = validate(markdown, typedKind);
89
- if (error) throw new Error(error);
90
- const response = await ask(`Review this draft against the original request. Check length, empty fields, missing requested items, and invented claims (including claims it was published, sent, paid for, or approved). Request: ${topic}. Context: ${text(input.context) || '(none)'}. Draft: ${markdown}. Return ONLY JSON {"ok":true|false,"reason":"..."}. Set ok=false for any missing request item or unverifiable factual claim. A clearly labelled missing number is allowed; do not invent facts.`);
91
- const verdict = object(JSON.parse(response.replace(/^```(?:json)?\s*|\s*```$/gi, '').trim()) as unknown);
92
- if (verdict.ok !== true) throw new Error(text(verdict.reason) || 'draft review failed');
93
- console.log(JSON.stringify({ outcome: 'ok', markdown, words: wordCount(markdown) }));
91
+ let reason = error;
92
+ if (!reason) {
93
+ const response = await ask(`Review this draft against the original request. Check length, empty fields, missing requested items, and invented claims (including claims it was published, sent, paid for, or approved). Request: ${topic}. Context: ${text(input.context) || '(none)'}. Draft: ${markdown}. Return ONLY JSON {"ok":true|false,"reason":"..."}. Set ok=false for any missing request item or unverifiable factual claim. A clearly labelled missing number is allowed; do not invent facts.`);
94
+ const verdict = object(JSON.parse(response.replace(/^```(?:json)?\s*|\s*```$/gi, '').trim()) as unknown);
95
+ if (verdict.ok !== true) reason = text(verdict.reason) || 'draft review failed';
96
+ }
97
+ if (reason) {
98
+ const outcome = object(outputs.check).outcome === 'revise' ? 'fail' : 'revise';
99
+ console.log(JSON.stringify({ outcome, reason, ...(outcome === 'fail' ? { error: reason } : {}) }));
100
+ } else console.log(JSON.stringify({ outcome: 'ok', markdown, words: wordCount(markdown) }));
94
101
  } else if (step === 'report') {
95
102
  const checked = object(outputs.check);
96
103
  if (checked.outcome !== 'ok') throw new Error('missing checked draft');
@@ -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;