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.
- package/apps/pwa/out/404/index.html +1 -1
- package/apps/pwa/out/404.html +1 -1
- package/apps/pwa/out/_next/static/Y4zKzL62sakG5momz3SoQ/_buildManifest.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1159-0c5ef8bc21cb6afb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1394-556de6d71bbdb0fb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1452.93700483b9d4fc61.js +9 -0
- package/apps/pwa/out/_next/static/chunks/{6062-73e68343a9814f6a.js → 1829-b453a1b22d3999bb.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/2176-c379e3ebce874151.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2310-7a59f043f6db207e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2426.8c6ee0c668d53def.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2548-6e316138d347cefe.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3170-7ec60a56fa0e4728.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3278-ae6ef084769f6195.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3454-988a53dac60a1763.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3638-aa80b4641ea439db.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3735-fe3402553ac1c9c6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3869-a39d1d9921e10d43.js +1 -0
- package/apps/pwa/out/_next/static/chunks/390.c373e31b3b9d0c41.js +5 -0
- package/apps/pwa/out/_next/static/chunks/3992-2e522a8ac5f2afde.js +25 -0
- package/apps/pwa/out/_next/static/chunks/4215-c9d91dd644ec1719.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4508-5d0161d1e724d735.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4685-c3c1c625b71accd1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4733-623a79345b171e1c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4883.3bd969ea1d3aaaff.js +2 -0
- package/apps/pwa/out/_next/static/chunks/507-5acd98bd6dab2d8a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5445-745eb795c5963d79.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5532.6899a12958a89b72.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5845-e6c003ce8d5fd36a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5978-b27004c677119115.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{8046cadf-211ca3c0e00f2b72.js → 5cd7e3a9-404fd6b8d0ecd44c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/6048-4c5c33579bc4ef78.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6303-422dbf0f67c72aac.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6751-bb3595948b2e311b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7274-19b7279f18b47ab3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{4323.c5dfb6f503304775.js → 7334.a6679d9168f37aa8.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/7551-a0e0707b79164ddd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7777-79f845045aa1ed86.js +1 -0
- package/apps/pwa/out/_next/static/chunks/79f8be9e-957d68913b590b08.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8025-cc24ee5d8f2fb6f8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8039-df92e08fede5de8f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8206-c6684dcde12be721.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8421-1c4acfb1395a0edf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8605-ea5bee9dfae512c7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8725.bf46bb1395dd0fa2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8734-252d511cd276c0dc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8834-ca1cb09c1cb1a6bf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{2415.ed6428ff334c7ce2.js → 9269.d2a4795bc6812717.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/9538-2816b90923606459.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9656-c1540088f5859c80.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9906-482c6619b3fdda20.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-d16993889b125c42.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-1dcdc58fc2de2454.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-6a061bd97f0b9398.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/board/page-cb95a7432c9ee744.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/{page-f972fc1c00a62e88.js → page-b12b6d2f8d2d1aea.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-b9efe868bfd36255.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/page-7c97dcdfe2e37a60.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/consult/page-8fd36984f549270a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/control/page-5721537ca8a3598c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-b654240a572e4c48.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/editor/page-b9750abc897508c3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/exec/page-4cd1de371f8ad048.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/field/page-d99f89657828421c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-d16993889b125c42.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/inside/page-50cb2a0eb15e6c83.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/page-c8b1ccda6ba7ae98.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/layout-d6b1d156fb95e74b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live/page-2a9932ddb62d76bd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/page-9688f5b4f1ff950f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-6b15dd6456c5df09.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/morning/{page-adfbd2ba9598e59a.js → page-35414e0a007b4082.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-e7a529e0507ac86c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-e001823c35c9dd67.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-371423d1a573d64b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-4b764b125339ac17.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-cdf8e4b01067bcf1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-3a24072b35470157.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/page-94c82eb0ae5abb32.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-0be8d4b2ced75ba0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-d37cb0d52f9b6bd5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-686da0df9bf23e5c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-4dc614f3ce043e63.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/page-53f80edc6b8dc07c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-d02c782fc792e2af.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-d16993889b125c42.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/page-239c17b911b84e72.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/share/page-dc6a79fb003f746c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-0fffb84cafdc9da6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-1218753c82d585f6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/term/page-4b5935e67531efb5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/today/page-3285bc87caf1eaa5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/trace/page-ad553b0e06505ecd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/vault/page-fca096e3e860306f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-9ebfa78f0d9885c3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-3dc5f21f7e39622d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-c6da2b468454532d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-93bc70e36f595414.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{2ea96958-e8c7e112de50b067.js → f48170cc-d05d28bf87a43bf4.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/framework-172630e6aa5e2d7f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-app-f0fe2d7477845181.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-fc98ac70b44eccb0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_app-5bd7f10271f72413.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_error-97d0d85ea05fef9b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/webpack-9e4d02d567b98240.js +1 -0
- package/apps/pwa/out/_next/static/css/543f56bcb1f79fca.css +1 -0
- package/apps/pwa/out/approvals/index.html +1 -1
- package/apps/pwa/out/approvals/index.txt +23 -23
- package/apps/pwa/out/autopilot/index.html +1 -1
- package/apps/pwa/out/autopilot/index.txt +23 -23
- package/apps/pwa/out/board/index.html +1 -1
- package/apps/pwa/out/board/index.txt +23 -23
- package/apps/pwa/out/botlab/index.html +1 -1
- package/apps/pwa/out/botlab/index.txt +22 -22
- package/apps/pwa/out/bots/index.html +1 -1
- package/apps/pwa/out/bots/index.txt +22 -22
- package/apps/pwa/out/chat/index.html +1 -1
- package/apps/pwa/out/chat/index.txt +22 -22
- package/apps/pwa/out/consult/index.html +1 -1
- package/apps/pwa/out/consult/index.txt +23 -23
- package/apps/pwa/out/control/index.html +1 -1
- package/apps/pwa/out/control/index.txt +23 -23
- package/apps/pwa/out/design-check/index.html +1 -1
- package/apps/pwa/out/design-check/index.txt +23 -23
- package/apps/pwa/out/editor/index.html +1 -1
- package/apps/pwa/out/editor/index.txt +23 -23
- package/apps/pwa/out/exec/index.html +1 -1
- package/apps/pwa/out/exec/index.txt +23 -23
- package/apps/pwa/out/field/index.html +1 -1
- package/apps/pwa/out/field/index.txt +23 -23
- package/apps/pwa/out/healthz +1 -1
- package/apps/pwa/out/index.html +1 -1
- package/apps/pwa/out/index.txt +22 -22
- package/apps/pwa/out/inside/index.html +1 -0
- package/apps/pwa/out/inside/index.txt +31 -0
- package/apps/pwa/out/intake/index.html +1 -1
- package/apps/pwa/out/intake/index.txt +23 -23
- package/apps/pwa/out/live/index.html +1 -1
- package/apps/pwa/out/live/index.txt +23 -23
- package/apps/pwa/out/market/index.html +1 -1
- package/apps/pwa/out/market/index.txt +22 -22
- package/apps/pwa/out/missions/placeholder/index.html +1 -1
- package/apps/pwa/out/missions/placeholder/index.txt +22 -22
- package/apps/pwa/out/morning/index.html +1 -1
- package/apps/pwa/out/morning/index.txt +23 -23
- package/apps/pwa/out/observatory/index.html +1 -1
- package/apps/pwa/out/observatory/index.txt +23 -23
- package/apps/pwa/out/ops/checklist/index.html +1 -1
- package/apps/pwa/out/ops/checklist/index.txt +22 -22
- package/apps/pwa/out/ops/release/index.html +1 -1
- package/apps/pwa/out/ops/release/index.txt +22 -22
- package/apps/pwa/out/ops/seats/index.html +1 -0
- package/apps/pwa/out/ops/seats/index.txt +30 -0
- package/apps/pwa/out/outputs/index.html +1 -0
- package/apps/pwa/out/outputs/index.txt +30 -0
- package/apps/pwa/out/reflection/index.html +1 -1
- package/apps/pwa/out/reflection/index.txt +23 -23
- package/apps/pwa/out/scheduler/index.html +1 -1
- package/apps/pwa/out/scheduler/index.txt +23 -23
- package/apps/pwa/out/sessions/index.html +1 -1
- package/apps/pwa/out/sessions/index.txt +22 -22
- package/apps/pwa/out/settings/devices/index.html +1 -1
- package/apps/pwa/out/settings/devices/index.txt +23 -23
- package/apps/pwa/out/settings/index.html +1 -1
- package/apps/pwa/out/settings/index.txt +23 -23
- package/apps/pwa/out/setup/done/index.html +1 -1
- package/apps/pwa/out/setup/done/index.txt +23 -23
- package/apps/pwa/out/setup/index.html +1 -1
- package/apps/pwa/out/setup/index.txt +23 -23
- package/apps/pwa/out/share/index.html +1 -1
- package/apps/pwa/out/share/index.txt +23 -23
- package/apps/pwa/out/showroom/index.html +1 -1
- package/apps/pwa/out/showroom/index.txt +22 -22
- package/apps/pwa/out/tasks/index.html +1 -1
- package/apps/pwa/out/tasks/index.txt +23 -23
- package/apps/pwa/out/term/index.html +1 -1
- package/apps/pwa/out/term/index.txt +23 -24
- package/apps/pwa/out/today/index.html +1 -0
- package/apps/pwa/out/today/index.txt +33 -0
- package/apps/pwa/out/trace/index.html +1 -1
- package/apps/pwa/out/trace/index.txt +23 -23
- package/apps/pwa/out/vault/index.html +1 -1
- package/apps/pwa/out/vault/index.txt +23 -23
- package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
- package/apps/pwa/out/workflows/chat-ui/index.txt +23 -23
- package/apps/pwa/out/workflows/index.html +1 -1
- package/apps/pwa/out/workflows/index.txt +23 -23
- package/apps/pwa/out/workspace/index.html +1 -1
- package/apps/pwa/out/workspace/index.txt +23 -24
- package/apps/pwa/out/worktrees/index.html +1 -1
- package/apps/pwa/out/worktrees/index.txt +23 -23
- package/graphs/drafts/draft-cleanup.yaml +19 -0
- package/graphs/drafts/recipes.yaml +23 -0
- package/graphs/field/field-feed.yaml +2 -0
- package/graphs/implement-loop.yaml +1 -1
- package/graphs/intake/intake-daily.yaml +2 -1
- package/graphs/ops/recipes.yaml +3 -0
- package/graphs/ops/secret-exposure.yaml +24 -0
- package/graphs/release/recipes.yaml +3 -0
- package/graphs/release/release-loop.yaml +4 -1
- package/graphs/video/explainer-line.yaml +2 -2
- package/package.json +2 -1
- package/plugins/card-followup/graphs/run-step.ts +48 -13
- package/plugins/doc-draft/graphs/doc-draft.yaml +3 -3
- package/plugins/doc-draft/graphs/run-step.ts +13 -6
- package/plugins/elanous-hwp/README.md +25 -0
- package/plugins/elanous-hwp/cli.ts +22 -0
- package/plugins/elanous-hwp/converter.ts +237 -0
- package/plugins/elanous-hwp/fixtures/fixed-controls.hwpx +0 -0
- package/plugins/elanous-hwp/fixtures/fixed-table-controls.hwpx +0 -0
- package/plugins/elanous-hwp/fixtures/fixed.hwpx +0 -0
- package/plugins/elanous-hwp/plugin.json +16 -0
- package/plugins/elanous-hwp/plugin.ts +40 -0
- package/scripts/ci-public-export-leak-gate.ts +75 -14
- package/scripts/ci-public-leak-gate.ts +44 -9
- package/scripts/ci-pwa-build-gate.ts +160 -0
- package/scripts/context-hooks/claude-stop.sh +4 -0
- package/scripts/context-hooks/emit.ts +24 -0
- package/scripts/context-hooks/post-tool-use.sh +4 -0
- package/scripts/coord-channel-watch.sh +54 -8
- package/scripts/coord-digest.ts +199 -0
- package/scripts/coord-post.sh +60 -3
- package/scripts/coord-requests-bridge.ts +143 -0
- package/scripts/field-feed/lib.ts +4 -0
- package/scripts/fleet/node-setup.sh +20 -3
- package/scripts/homebrew-formula-bump.ts +69 -0
- package/scripts/install.ps1 +68 -9
- package/scripts/install.sh +39 -12
- package/scripts/lib/pwa-scenarios/scenarios.ts +20 -0
- package/scripts/lib/tui-regress-checks.ts +146 -0
- package/scripts/marketing/measure-site-numbers.ts +169 -0
- package/scripts/measure-gate-shards.ts +309 -0
- package/scripts/memory-lifecycle-cycle.ts +2 -2
- package/scripts/mission-request-judge.ts +31 -1
- package/scripts/morning-report.ts +2 -2
- package/scripts/opportunity-scan.ts +1 -1
- package/scripts/ops-now-probe.ts +327 -0
- package/scripts/publish-internal-dist.ts +11 -1
- package/scripts/release-loop/auto-approve-node.ts +21 -4
- package/scripts/release-loop/cut-branch.ts +84 -0
- package/scripts/release-loop/cutoff.ts +19 -8
- package/scripts/release-loop/gate-node.ts +14 -13
- package/scripts/release-loop/mac-smoke-node.ts +229 -0
- package/scripts/release-loop/node-verdict.ts +3 -3
- package/scripts/release-loop/ops-upgrade-node.ts +72 -16
- package/scripts/release-loop/release-readiness.ts +9 -0
- package/scripts/release-loop/tui-node-checks.ts +42 -0
- package/scripts/release-loop/tui-sim-node.ts +32 -8
- package/scripts/release-loop/unattended-release.ts +5 -3
- package/scripts/release-loop/version-node.ts +112 -13
- package/scripts/repo-watch-cycle.ts +1 -1
- package/scripts/retro-cycle.ts +2 -2
- package/scripts/schedule-health-report.ts +1 -1
- package/scripts/signal-digest-cycle.ts +2 -7
- package/scripts/signal-digest-photo.ts +11 -0
- package/scripts/storyboard/storyboard.ts +5 -161
- package/scripts/tech1-public-measure.ts +94 -0
- package/scripts/test-diet/lib.ts +30 -1
- package/scripts/test-diet/node.ts +83 -8
- package/scripts/test-timeout-audit.ts +114 -0
- package/scripts/tree-sync-alert.ts +1 -1
- package/scripts/tui-regress.ts +71 -0
- package/scripts/ux/demo-rehearsal.ts +243 -0
- package/scripts/ux/lib/rehearsal-verdict.ts +73 -0
- package/scripts/waitlist-daily-digest.ts +1 -1
- package/skills/diagram-master/SKILL.md +366 -0
- package/skills/diagram-master/references/engines/excalidraw/color-palette.md +67 -0
- package/skills/diagram-master/references/engines/excalidraw/element-templates.md +182 -0
- package/skills/diagram-master/references/engines/excalidraw/json-schema.md +71 -0
- package/skills/diagram-master/references/engines/excalidraw/render_excalidraw.py +189 -0
- package/skills/diagram-master/references/engines/excalidraw/render_template.html +57 -0
- package/skills/diagram-master/references/engines/matplotlib/matplotlib-patterns.md +235 -0
- package/skills/diagram-master/references/engines/matplotlib/render_matplotlib.py +184 -0
- package/skills/diagram-master/references/engines/mermaid/domain-templates.md +435 -0
- package/skills/diagram-master/references/engines/mermaid/render_mermaid.py +116 -0
- package/skills/diagram-master/references/engines/mermaid/syntax-reference.md +2247 -0
- package/skills/diagram-master/references/engines/nanobannana/render_nanobannana2.py +168 -0
- package/skills/diagram-master/references/engines/svg/color-palette.md +188 -0
- package/skills/diagram-master/references/engines/svg/html-base-template.html +332 -0
- package/skills/diagram-master/references/engines/svg/interactivity-patterns.md +435 -0
- package/skills/diagram-master/references/engines/svg/render_interactive_svg.py +212 -0
- package/skills/diagram-master/references/engines/svg/svg-element-templates.md +349 -0
- package/skills/diagram-master/references/examples/README.md +69 -0
- package/skills/diagram-master/references/examples/ref_grid_architecture.png +0 -0
- package/skills/diagram-master/references/examples/ref_layered_flow.png +0 -0
- package/skills/diagram-master/references/pyproject.toml +16 -0
- package/skills/diagram-master/references/quality-checklist.md +33 -0
- package/skills/diagram-master/references/routing-guide.md +164 -0
- package/skills/diagram-master/references/uv.lock +1356 -0
- package/skills/explainer-video/SKILL.md +4 -0
- package/skills/explainer-video/engine/reel.mjs +18 -13
- package/skills/explainer-video/engine/reel.sh +24 -5
- package/skills/explainer-video/engine/stitch.mjs +145 -0
- package/skills/google-workspace/SKILL.md +33 -0
- package/skills/google-workspace/scripts/gws-safe.sh +177 -0
- package/skills/grill-me/SKILL.md +1 -1
- package/skills/lecture-note-digitizer/SKILL.md +926 -0
- package/skills/lecture-note-digitizer/references/convert_to_pdf.py +30 -0
- package/skills/lecture-note-digitizer/references/extract_slide_diagram.py +73 -0
- package/skills/lecture-note-digitizer/references/html-template.md +167 -0
- package/skills/lecture-note-digitizer/references/katex-style-guide.md +165 -0
- package/skills/omni-crawl/SKILL.md +21 -11
- package/skills/omni-crawl/scripts/main.ts +6 -6
- package/skills/omni-crawl/src/router.ts +3 -3
- package/skills/omni-digest/SKILL.md +17 -9
- package/skills/omni-digest/digest.md +2 -2
- package/skills/omni-digest/src/fetch.ts +30 -3
- package/skills/pitch-deck/SKILL.md +36 -0
- package/skills/pitch-deck/template.html +80 -0
- package/skills/youtube-master/SKILL.md +163 -0
- package/skills/youtube-master/package-lock.json +558 -0
- package/skills/youtube-master/package.json +13 -0
- package/skills/youtube-master/references/study-note-template.md +124 -0
- package/skills/youtube-master/scripts/main.ts +435 -0
- package/skills/youtube-master/scripts/transcribe-local.ts +24 -0
- package/skills/youtube-master/src/audio.ts +63 -0
- package/skills/youtube-master/src/domain-staging.ts +50 -0
- package/skills/youtube-master/src/env.ts +36 -0
- package/skills/youtube-master/src/obsidian.ts +155 -0
- package/skills/youtube-master/src/router.ts +110 -0
- package/skills/youtube-master/src/stt.ts +199 -0
- package/skills/youtube-master/src/study-note.ts +175 -0
- package/skills/youtube-master/src/summarize.ts +358 -0
- package/skills/youtube-master/src/transcript.ts +76 -0
- package/skills/youtube-master/src/types.ts +91 -0
- package/skills/youtube-master/src/util.ts +93 -0
- package/skills/youtube-master/src/youtube.ts +70 -0
- package/skills/youtube-master/tsconfig.json +13 -0
- package/src/acp/codex-approval-adapter.ts +9 -3
- package/src/acp/core-turn-bridge.ts +117 -18
- package/src/acp/fact-question.ts +11 -0
- package/src/acp/server.ts +53 -9
- package/src/acp/tool-approval.ts +126 -0
- package/src/acp/turn-runner.ts +36 -0
- package/src/agent/monad-agent-turn.ts +8 -0
- package/src/agent-mission/driver.ts +80 -10
- package/src/agent-mission/pty-decision.ts +71 -0
- package/src/agent-mission/review-loop.ts +30 -0
- package/src/ask-user-question/pending-questions.ts +25 -10
- package/src/ask-user-question/types.ts +9 -0
- package/src/auth/setup-link-tokens.ts +16 -5
- package/src/autopilot/build/integrity-gate.ts +12 -0
- package/src/autopilot/build/llm-conflict-merge.ts +16 -3
- package/src/autopilot/tool-registry.ts +2 -0
- package/src/boot/daemon-prompt-turn.ts +8 -0
- package/src/boot/daemon-runtime.ts +9 -3
- package/src/boot/daemon-tools/index.ts +8 -0
- package/src/boot/daemon-tools/types.ts +2 -0
- package/src/boot/daemon-tools/web-search.ts +4 -0
- package/src/card-followup/core.ts +109 -0
- package/src/chat/ctrl-c-in-input.ts +14 -0
- package/src/chat/index.ts +53 -0
- package/src/chat/pickers/state.ts +6 -2
- package/src/cli/agent-cli.ts +7 -0
- package/src/cli/decisions-cli.ts +25 -3
- package/src/cli/doctor-bundle-upload.ts +68 -0
- package/src/cli/doctor-cli.ts +38 -4
- package/src/cli/drive-input-jail.ts +101 -0
- package/src/cli/hooks-cli.ts +17 -3
- package/src/cli/make-cli.ts +43 -0
- package/src/cli/ops-cli.ts +20 -0
- package/src/cli/persona-cli.ts +90 -4
- package/src/cli/plugin-cli.ts +31 -22
- package/src/cli/pod-cli.ts +61 -2
- package/src/cli/pr-cli.ts +82 -17
- package/src/cli/pty-drive-cli.ts +41 -7
- package/src/cli/pty-drive-verdict.ts +40 -0
- package/src/cli/pwa-stop.ts +163 -13
- package/src/cli/pwa-test.ts +3 -0
- package/src/cli/release-cli.ts +80 -24
- package/src/cli/self-update.ts +14 -0
- package/src/cli/setup-cli.ts +43 -1
- package/src/cli/skills-cli.ts +17 -1
- package/src/cli/storyboard-cli.ts +37 -0
- package/src/cli/test-state-dir-flag.ts +11 -4
- package/src/connectors/linear.ts +146 -0
- package/src/context-bus/claude-hooks-install.ts +10 -0
- package/src/context-bus/context-now-surfaces.ts +48 -0
- package/src/context-bus/context-now.ts +104 -0
- package/src/context-bus/coord-events.ts +115 -0
- package/src/context-bus/external-events.ts +52 -0
- package/src/context-bus/long-term-memory.ts +108 -0
- package/src/dashboard/dashboard-boot-fixture.ts +23 -0
- package/src/dashboard/index.ts +63 -21
- package/src/dashboard/slash-runtime/dashboard-handlers.ts +14 -1
- package/src/dashboard/slash-runtime/help-from-registry.ts +43 -28
- package/src/decisions/decision-cards.ts +58 -3
- package/src/decisions/decision-ledger.ts +85 -6
- package/src/decisions/decision-linear-projection.ts +157 -0
- package/src/decisions/web-push-decision-cards.ts +50 -0
- package/src/directive/interpretation-card.ts +36 -0
- package/src/discord/sprint21-runtime.ts +5 -1
- package/src/discord-self-message.ts +32 -0
- package/src/discord-slash-wire.ts +5 -1
- package/src/discord.ts +19 -2
- package/src/domains/coo-admin-tool.ts +62 -0
- package/src/domains/core-tools.ts +13 -4
- package/src/domains/elanous-card.ts +29 -0
- package/src/domains/ops-facts-tool.ts +76 -0
- package/src/domains/outbound-alert.ts +46 -13
- package/src/domains/release-tool.ts +168 -0
- package/src/domains/telegram-kind-route.ts +65 -9
- package/src/exec-requests/default-outputs.ts +25 -0
- package/src/exec-requests/planner.ts +106 -28
- package/src/exec-requests/runner.ts +123 -12
- package/src/exec-requests/store.ts +11 -2
- package/src/execution-loop/launch-gate.ts +100 -0
- package/src/field/field-media.ts +10 -7
- package/src/field/field-reel.ts +19 -5
- package/src/field-note/index.ts +201 -0
- package/src/field-note/transcribe.ts +248 -0
- package/src/git-fs/worktree.ts +45 -6
- package/src/graph-kinds/registry.ts +3 -3
- package/src/graph-runner/graph-approval-card.ts +113 -0
- package/src/graph-runner/graph-cli.ts +189 -4
- package/src/graph-runner/graph-grow-llm.ts +37 -0
- package/src/graph-runner/graph-grow.ts +47 -0
- package/src/graph-runner/graph-notify.ts +25 -3
- package/src/graph-runner/runner.ts +453 -35
- package/src/grok/agent-search.ts +8 -2
- package/src/guardian/secret-exposure-scan.ts +101 -0
- package/src/harness/browser-verify.ts +25 -2
- package/src/harness/harness-cli-command.ts +478 -42
- package/src/harness/harness-incidents-writer.fixture.ts +24 -0
- package/src/harness/harness-incidents.ts +129 -0
- package/src/harness/harness-pod-dispatch.ts +43 -23
- package/src/harness/harness-seams.ts +38 -4
- package/src/harness/harness-stop.ts +75 -6
- package/src/harness/launch-registry.ts +91 -0
- package/src/harness/process-garbage.ts +5 -3
- package/src/hitl/clarification-policy.ts +3 -1
- package/src/hooks/dispatch.ts +82 -0
- package/src/hooks/expose.ts +254 -0
- package/src/hooks/github-shadow.ts +108 -0
- package/src/hooks/providers.ts +71 -5
- package/src/hooks/queue.ts +1 -1
- package/src/hooks/receiver.ts +21 -20
- package/src/index.ts +206 -49
- package/src/intake-plane/author-goals.ts +37 -4
- package/src/intake-plane/categorize.ts +18 -46
- package/src/intake-plane/check.ts +3 -0
- package/src/intake-plane/discord-seat-work.ts +57 -2
- package/src/intake-plane/front-route-classifier.ts +33 -39
- package/src/intake-plane/persona-answer.ts +30 -0
- package/src/intake-plane/runtime-callables.ts +8 -1
- package/src/intake-plane/seat-doc-route.ts +170 -6
- package/src/intake-plane/telegram-seat-work.ts +80 -4
- package/src/intake-plane/telegram-work.ts +1 -0
- package/src/intake-plane/wish-folder.ts +67 -0
- package/src/intent-gate/gate.ts +30 -10
- package/src/live/detail-switch.ts +5 -0
- package/src/llm/difficulty.ts +57 -0
- package/src/llm/judge-layer.ts +101 -0
- package/src/llm/mission-router.ts +33 -19
- package/src/loops/contract/examples/cmo-seat.loop.yaml +51 -0
- package/src/loops/contract/examples/orchestrator.loop.yaml +57 -0
- package/src/loops/contract/examples/steward.loop.yaml +50 -0
- package/src/loops/contract/loop-agent.schema.json +138 -0
- package/src/loops/contract/validate.ts +64 -0
- package/src/loops/registry.ts +20 -6
- package/src/market/signed-index.ts +4 -0
- package/src/maturity/bot-command-maturity.ts +45 -0
- package/src/maturity/cli-maturity.ts +34 -0
- package/src/maturity/feature-maturity.ts +120 -1
- package/src/maturity/tui-slash-maturity.ts +31 -0
- package/src/memory.ts +32 -4
- package/src/model-tier/index.ts +2 -2
- package/src/model-tier/tier-resolver.ts +20 -20
- package/src/model-tier/types.ts +6 -6
- package/src/mss/logging/log-store.ts +15 -0
- package/src/nexus/api/config-model-tier.ts +5 -4
- package/src/nexus/api/exec-requests.ts +33 -3
- package/src/nexus/api/fabric-plans.ts +53 -0
- package/src/nexus/api/harness-api.ts +5 -1
- package/src/nexus/api/health.ts +2 -0
- package/src/nexus/api/http-server.ts +89 -1
- package/src/nexus/api/inside-events.ts +226 -0
- package/src/nexus/api/meta-api.ts +32 -8
- package/src/nexus/api/missions.ts +6 -1
- package/src/nexus/api/ops-api.ts +10 -1
- package/src/nexus/api/ops-seats-sources.ts +37 -7
- package/src/nexus/api/ops-seats.ts +49 -6
- package/src/nexus/api/outputs-route.ts +44 -0
- package/src/nexus/api/seat-requests.ts +132 -10
- package/src/nexus/api/sessions-store.ts +42 -0
- package/src/nexus/api/setup-codex-login.ts +160 -0
- package/src/nexus/api/sub-seat-charters.ts +34 -0
- package/src/nexus/api/tabs-mutations.ts +3 -1
- package/src/nexus/api/trace.ts +18 -6
- package/src/nexus/api/usage-api.ts +102 -0
- package/src/nexus/api/workflow-run-event-bridge.ts +1 -0
- package/src/nexus/api/workflows.ts +80 -6
- package/src/nexus/index.ts +47 -4
- package/src/nexus/kinds/webterm.ts +2 -1
- package/src/nexus/webterm/pty.ts +6 -2
- package/src/onboarding/cli-auth-status.ts +80 -0
- package/src/onboarding/first-run.ts +89 -0
- package/src/onboarding/lan-address.ts +45 -0
- package/src/onboarding/voice-ai.ts +3 -3
- package/src/onboarding/web-first.ts +119 -24
- package/src/onboarding.ts +4 -0
- package/src/ops-now/ops-now-refresh.ts +100 -0
- package/src/org/seat-budget.ts +48 -0
- package/src/org/stall-escalation.ts +63 -0
- package/src/outputs/ledger.ts +120 -0
- package/src/persona/persona-todo.ts +79 -0
- package/src/persona/presets.ts +116 -2
- package/src/persona/team-set.ts +155 -0
- package/src/plugins/install/plugin-install.ts +14 -0
- package/src/plugins/maker/market-bundle.ts +109 -0
- package/src/plugins/maker/plugin-maker.ts +254 -11
- package/src/plugins/maker/wizard-generate.ts +103 -0
- package/src/pod-lease/admission.ts +150 -0
- package/src/pod-lease/dependency-state.ts +85 -0
- package/src/pod-lease/host-lease.ts +127 -0
- package/src/project/project-store.ts +115 -0
- package/src/project/project-suggestion.ts +16 -0
- package/src/project/session-context.ts +24 -0
- package/src/release-loop/checklist.ts +79 -5
- package/src/release-loop/feature-store.ts +159 -24
- package/src/release-loop/next-md-merge.ts +58 -0
- package/src/release-loop/release-note.ts +59 -4
- package/src/release-loop/release-schedule.ts +53 -0
- package/src/repl/index.ts +7 -0
- package/src/seat-dispatch/ceo-commands.ts +41 -8
- package/src/seat-dispatch/ceo-intent.ts +38 -0
- package/src/seat-dispatch/seat-ask.ts +130 -0
- package/src/seat-dispatch/seat-questions.ts +100 -0
- package/src/seat-loop/seat-loop.ts +750 -0
- package/src/seat-loop/seat-report.ts +99 -0
- package/src/self-dev/decompose.ts +66 -11
- package/src/self-dev/dev-cli.ts +4 -1
- package/src/self-dev/dev-pipeline.ts +37 -1
- package/src/self-dev/draft-sweep.ts +38 -33
- package/src/self-dev/draft-triage-rules.ts +8 -5
- package/src/self-dev/fabric-plan-core.ts +297 -0
- package/src/self-dev/orchestrate.ts +36 -4
- package/src/self-dev/release-path-guard.ts +62 -0
- package/src/self-dev/run-store.ts +10 -1
- package/src/self-dev/shard-aggregate.ts +230 -0
- package/src/self-dev/shard-placement.ts +60 -0
- package/src/self-dev/shard-plan.ts +48 -0
- package/src/self-dev/shard-pod-run.ts +77 -0
- package/src/self-dev/shard-run.ts +105 -0
- package/src/self-implement/execution-clarification.ts +115 -0
- package/src/self-implement/gate-baseline.ts +1 -1
- package/src/self-implement/gate-cli.ts +46 -4
- package/src/self-implement/gate-scope.ts +37 -7
- package/src/self-implement/graph-variant.ts +171 -0
- package/src/self-implement/harness-policy-gates.ts +1 -1
- package/src/self-implement/headless-elanous-driver.ts +22 -3
- package/src/self-implement/host-regate.ts +22 -1
- package/src/self-implement/importer-test-index.ts +2 -1
- package/src/self-implement/l8-merge-queue.ts +130 -0
- package/src/self-implement/orchestrator.ts +279 -12
- package/src/self-implement/pipeline-shape.ts +1 -1
- package/src/self-implement/route-consumer-test-index.ts +122 -0
- package/src/self-implement/run-status-mapping.ts +5 -3
- package/src/self-implement/seams.ts +72 -11
- package/src/self-implement/test-seams.ts +1 -0
- package/src/self-implement/uncommitted-work.ts +59 -0
- package/src/session/chat.ts +2 -0
- package/src/session/index.ts +45 -13
- package/src/shell-env-bootstrap.ts +17 -0
- package/src/skills/gift-install.ts +215 -0
- package/src/skills/tools/web-search.ts +4 -1
- package/src/steward/directive.ts +51 -2
- package/src/steward/launch.ts +266 -0
- package/src/steward/outcome-notify.ts +115 -0
- package/src/steward/steward-cards.ts +25 -11
- package/src/steward/triage-plan.ts +32 -4
- package/src/steward/triage.ts +129 -13
- package/src/steward/working-backwards.ts +64 -0
- package/src/storyboard/storyboard.ts +141 -0
- package/src/task-cards/card-cli.ts +25 -1
- package/src/task-cards/card-store.ts +2 -2
- package/src/task-orchestrator/mission.ts +5 -0
- package/src/task-orchestrator/store.ts +12 -3
- package/src/task-orchestrator/surfaces/pod-command-job.ts +29 -7
- package/src/task-orchestrator/surfaces/pod-lease.ts +397 -0
- package/src/task-orchestrator/surfaces/pod-pool.ts +127 -26
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +214 -40
- package/src/task-orchestrator/surfaces/self-implement.ts +15 -2
- package/src/task-orchestrator/types.ts +2 -0
- package/src/telegram-card-followup.ts +153 -0
- package/src/telegram-commands.ts +10 -1
- package/src/telegram-project-command.ts +71 -0
- package/src/telegram-report.ts +29 -19
- package/src/telegram-run.ts +7 -0
- package/src/telegram.ts +58 -11
- package/src/tool-runtime/self-cognition-runtimes.ts +13 -3
- package/src/tool-runtime/types.ts +4 -0
- package/src/tui-client/dashboard-session.ts +2 -0
- package/src/user-config.ts +216 -27
- package/src/version/packed-revision.json +1 -1
- package/src/web-push/notify-exec-request.ts +32 -0
- package/src/web-search/grok.ts +7 -1
- package/src/web-search/index.ts +28 -1
- package/src/web-search/provider.ts +4 -0
- package/src/web-search/x-intent.ts +5 -0
- package/src/workflow-runtime/executor.ts +277 -47
- package/src/workflow-runtime/index.ts +1 -0
- package/src/workflow-runtime/nodes/subworkflow.ts +60 -0
- package/src/workflow-runtime/plugin-kind-node.ts +1 -1
- package/src/workflow-runtime/schema.ts +79 -4
- package/src/workflow-runtime/types.ts +35 -4
- package/apps/pwa/out/_next/static/bFI5EEwg7T6aG88ibX4Zq/_buildManifest.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1105-c473ed46272fba79.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1201-09613bce624467c0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1377-1efeffbbac0963ff.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1470.98a6ab61ccb4131c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/216-5acd01cbde4acda7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2330-c2f07246c8788af1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2375-cb6debea56cce4fb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2929-617f1e66a55be184.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2977-e38442e86289f1d3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2982-2a2df543be3d2e5a.js +0 -25
- package/apps/pwa/out/_next/static/chunks/3403.4bc97b4066945a94.js +0 -5
- package/apps/pwa/out/_next/static/chunks/3441-a1dd4acfeda14488.js +0 -1
- package/apps/pwa/out/_next/static/chunks/371-4ad6af55dad02ced.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4114-3e63117452378807.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5080-1613752891fcadb4.js +0 -10
- package/apps/pwa/out/_next/static/chunks/5609-012bb3557211f07e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5791-bb82743fa6299562.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6090-85a3f05f954e5697.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6284-69db415193d5f8bd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6320-baac91d68843a9a6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6416-505ba707cacafbba.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6725-68ab7cc0b1faf00d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6998-a654fe5c58271600.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7032-f4ef698518fba61c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7647.2fcd30d894d5ee30.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7673-2eda1507d38dadb8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7990-e12efba616f46e03.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8067-d976b441a104ab74.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8142-744998d65d7bb17e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8519-5632377cce994f86.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8619-4365e03a2862830f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8630-cdfc61e82c653ffc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8894-e263b332208e3957.js +0 -1
- package/apps/pwa/out/_next/static/chunks/920-f7dceb78f1c585ef.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9454-139040d37c79dbc9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9651-3cd291a6fa1370d2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9740-8f5ff96c522b5f55.js +0 -1
- package/apps/pwa/out/_next/static/chunks/ae787e4c-f2998de7f1ba72a4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-859cea1570b6227a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-fb7c2d458f32fcad.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-35e2713597dd24d1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-918b656f064577ba.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-5f30e00693527ebd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/chat/page-7fbae173b3c103a7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-3b4edcce364062ea.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/control/page-c5794f484c251f44.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-1130d8eefcd87e76.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-973996d584f008c1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/exec/page-016e2bda04eecb1f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/field/page-b4d6d9a3b56f2940.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-859cea1570b6227a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/intake/page-ec49a466121233ba.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-f72db356744ceade.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-a6fd9b50d1b85378.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/market/page-8b0402a46a749a3d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-037f83e2a1edc8e2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-46fedff57d4bec67.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-0cf318e9dcee2bbf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-899b327cb8b6845c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-6c1e6ba743a29dfe.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-1a8e4807292f6b17.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-28deba6bcb834916.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-de68e2205794037d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-c71c33a573230672.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-0df30e0c7e50148a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/page-4fc56790fe0d2682.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-59df2aebb38a7c1e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-859cea1570b6227a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/page-4647be5dbcbd0795.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/share/page-e025892bda74b743.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-a0955de73b89f078.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-369846d2921af584.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/term/page-72cbc1c8cdd0f65d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-77b28bfb0996d5ea.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-3220c4934f49c6c2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-90670589b87abea8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-7df9243ed021a4cc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-4498c3853f4f688d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-35bedde6d885f08f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/framework-ea806bb57f2b8923.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-8d2302399c2c8f87.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-app-6870f30ea99f8346.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_app-b10928c60f0b8488.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_error-689039227d443fab.js +0 -1
- package/apps/pwa/out/_next/static/chunks/webpack-e5bea36f183b25e6.js +0 -1
- package/apps/pwa/out/_next/static/css/f92f63a5df0ba229.css +0 -1
- package/src/dashboard/turn-typeahead-echo.ts +0 -23
- /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
|
|
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
|
|
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
|
|
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:
|
|
7
|
-
- { node_id: check, kind: agent, recipe: 'cmd:check', max_visits:
|
|
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
|
|
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
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
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, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
|
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
|
+
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -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;
|