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
|
@@ -28,6 +28,7 @@ export interface ChecklistHistory {
|
|
|
28
28
|
to: unknown;
|
|
29
29
|
released: string;
|
|
30
30
|
dev: string;
|
|
31
|
+
force?: true;
|
|
31
32
|
}
|
|
32
33
|
export interface Checklist {
|
|
33
34
|
version: string;
|
|
@@ -41,12 +42,44 @@ export function devVersion(): string {
|
|
|
41
42
|
return (JSON.parse(readFileSync(join(import.meta.dir, '..', '..', 'package.json'), 'utf8')) as { version: string }).version;
|
|
42
43
|
}
|
|
43
44
|
|
|
44
|
-
export function
|
|
45
|
-
|
|
45
|
+
export function parseOwner(value: string): { seat: string; sub?: string } {
|
|
46
|
+
const match = /^([A-Z]{2,8})(?:\/([a-z][a-z0-9-]{0,31}))?$/.exec(value);
|
|
47
|
+
if (!match || match[0] !== value) throw new CliUserError(`잘못된 담당: ${value}`, 'TC 또는 TC/rel');
|
|
48
|
+
return match[2] ? { seat: match[1]!, sub: match[2] } : { seat: match[1]! };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function ownerMatches(owner: string | undefined, filter: string): boolean {
|
|
52
|
+
if (owner === undefined) return false;
|
|
53
|
+
try {
|
|
54
|
+
const parsed = parseOwner(owner);
|
|
55
|
+
const requested = parseOwner(filter);
|
|
56
|
+
return parsed.seat === requested.seat && (requested.sub === undefined || parsed.sub === requested.sub);
|
|
57
|
+
} catch { return false; }
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// The ledger event schema has no force column; decode its claim target for every checklist snapshot.
|
|
61
|
+
export function decodeClaimHistoryEntry<T extends ChecklistHistory>(entry: T): T {
|
|
62
|
+
if (entry.field !== 'claim' || !entry.to || typeof entry.to !== 'object') return entry;
|
|
63
|
+
const target = entry.to as { owner?: unknown; force?: unknown };
|
|
64
|
+
return typeof target.owner === 'string' && target.force === true ? { ...entry, to: target.owner, force: true } as T : entry;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function decodeClaimHistory(data: Checklist): Checklist {
|
|
68
|
+
return { ...data, history: data.history.map(decodeClaimHistoryEntry) };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function listChecklist(v: string, root?: string): Checklist {
|
|
72
|
+
return decodeClaimHistory(root === undefined ? store.list(v) : store.list(v, undefined, undefined, root));
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** Status events in the requested window; other ledger fields never become status changes. */
|
|
76
|
+
export function statusChangesSince(history: readonly ChecklistHistory[], sinceIso: string): ChecklistHistory[] {
|
|
77
|
+
const since = Date.parse(sinceIso);
|
|
78
|
+
return history.filter((entry) => entry.field === 'status' && Date.parse(entry.at) >= since);
|
|
46
79
|
}
|
|
47
80
|
|
|
48
81
|
function mutate(v: string, apply: (data: Checklist) => boolean): Checklist {
|
|
49
|
-
return store.mutate(v, store.releasedVersion(), devVersion(), apply);
|
|
82
|
+
return decodeClaimHistory(store.mutate(v, store.releasedVersion(), devVersion(), apply));
|
|
50
83
|
}
|
|
51
84
|
|
|
52
85
|
function change(data: Checklist, id: string, field: string, from: unknown, to: unknown, by: string, at: string): void {
|
|
@@ -58,6 +91,7 @@ export function addItem(v: string, input: { id: string; title: string; owner?: s
|
|
|
58
91
|
if (!input.id.trim()) throw new CliUserError('칸 id 가 비었다');
|
|
59
92
|
if (!input.title.trim()) throw new CliUserError('칸 제목이 비었다');
|
|
60
93
|
if (input.kind !== undefined && input.kind !== 'screen') throw new CliUserError(`잘못된 종류: ${input.kind}`, 'screen');
|
|
94
|
+
if (input.owner !== undefined) parseOwner(input.owner);
|
|
61
95
|
return mutate(v, (data) => {
|
|
62
96
|
if (data.items.some((item) => item.id === input.id)) throw new CliUserError(`이미 있는 칸: ${input.id}`, 'set <id> 로 고친다');
|
|
63
97
|
const by = process.env.ELANOUS_TRACK || 'cli';
|
|
@@ -76,6 +110,12 @@ export function setItem(v: string, id: string, patch: { status?: ChecklistStatus
|
|
|
76
110
|
if (patch.status !== undefined && !['green', 'yellow', 'red', 'done'].includes(patch.status)) throw new CliUserError(`잘못된 상태: ${patch.status}`);
|
|
77
111
|
if (patch.disposition !== undefined && !['move', 'known-issue', 'block'].includes(patch.disposition)) throw new CliUserError(`잘못된 처분: ${patch.disposition}`);
|
|
78
112
|
if (patch.kind !== undefined && patch.kind !== 'screen') throw new CliUserError(`잘못된 종류: ${patch.kind}`, 'screen');
|
|
113
|
+
if (patch.owner !== undefined) {
|
|
114
|
+
parseOwner(patch.owner);
|
|
115
|
+
if (item.owner && patch.owner !== item.owner) {
|
|
116
|
+
throw new CliUserError(`지금 주인: ${item.owner} — --force 로만 바꾼다`, 'release checklist claim <id> --by <자리> --force');
|
|
117
|
+
}
|
|
118
|
+
}
|
|
79
119
|
const fields = (['evidence', 'owner', 'status', 'disposition', 'kind'] as const).filter((field) => patch[field] !== undefined && patch[field] !== item[field]);
|
|
80
120
|
if (fields.length === 0) return false;
|
|
81
121
|
const at = new Date().toISOString();
|
|
@@ -91,6 +131,34 @@ export function setItem(v: string, id: string, patch: { status?: ChecklistStatus
|
|
|
91
131
|
});
|
|
92
132
|
}
|
|
93
133
|
|
|
134
|
+
export function claimItem(v: string, id: string, by: string, options: { force?: boolean } = {}): Checklist {
|
|
135
|
+
const requested = parseOwner(by);
|
|
136
|
+
let outcome: 'claimed' | 'same' | 'refused' = 'refused';
|
|
137
|
+
let from: string | null = null;
|
|
138
|
+
try {
|
|
139
|
+
const result = mutate(v, (data) => {
|
|
140
|
+
const item = data.items.find((i) => i.id === id);
|
|
141
|
+
if (!item) throw new CliUserError(`없는 칸: ${id}`, 'list 로 칸 목록을 본다');
|
|
142
|
+
from = item.owner ?? null;
|
|
143
|
+
if (from === by) { outcome = 'same'; return false; }
|
|
144
|
+
if (from && !options.force && !(from === requested.seat && requested.sub)) {
|
|
145
|
+
throw new CliUserError(`지금 주인: ${from} — --force 로만 바꾼다`);
|
|
146
|
+
}
|
|
147
|
+
const at = new Date().toISOString();
|
|
148
|
+
item.owner = by;
|
|
149
|
+
item.updatedAt = at;
|
|
150
|
+
item.updatedBy = by;
|
|
151
|
+
data.history.push({ at, by, id, field: 'claim', from, to: options.force ? { owner: by, force: true } : by,
|
|
152
|
+
released: data.released, dev: data.dev });
|
|
153
|
+
outcome = 'claimed';
|
|
154
|
+
return true;
|
|
155
|
+
});
|
|
156
|
+
return result;
|
|
157
|
+
} finally {
|
|
158
|
+
debug.log('release-loop.checklist', 'claim', { version: v, id, from, to: by, force: options.force === true, outcome });
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
94
162
|
export function removeItem(v: string, id: string, by: string): Checklist {
|
|
95
163
|
return mutate(v, (data) => {
|
|
96
164
|
const index = data.items.findIndex((i) => i.id === id);
|
|
@@ -106,15 +174,21 @@ export function summarize(v: string): ChecklistSummary {
|
|
|
106
174
|
return summarizeChecklist(listChecklist(v));
|
|
107
175
|
}
|
|
108
176
|
|
|
109
|
-
export interface ChecklistSummary { green: number; yellow: number; red: number; done: number; blocked: string[]; byOwner: Record<string, number> }
|
|
177
|
+
export interface ChecklistSummary { green: number; yellow: number; red: number; done: number; blocked: string[]; byOwner: Record<string, number>; bySeat: Record<string, number> }
|
|
110
178
|
|
|
111
179
|
export function summarizeChecklist(data: Checklist): ChecklistSummary {
|
|
112
|
-
const summary = { green: 0, yellow: 0, red: 0, done: 0, blocked: [] as string[], byOwner: Object.create(null) as Record<string, number> };
|
|
180
|
+
const summary = { green: 0, yellow: 0, red: 0, done: 0, blocked: [] as string[], byOwner: Object.create(null) as Record<string, number>, bySeat: Object.create(null) as Record<string, number> };
|
|
113
181
|
for (const item of data.items) {
|
|
114
182
|
summary[item.status]++;
|
|
115
183
|
if (item.status === 'red') summary.blocked.push(item.id);
|
|
116
184
|
const owner = item.owner ?? 'unassigned';
|
|
117
185
|
summary.byOwner[owner] = (summary.byOwner[owner] ?? 0) + 1;
|
|
186
|
+
if (item.owner !== undefined) {
|
|
187
|
+
try {
|
|
188
|
+
const { seat } = parseOwner(item.owner);
|
|
189
|
+
summary.bySeat[seat] = (summary.bySeat[seat] ?? 0) + 1;
|
|
190
|
+
} catch { /* Preserve legacy owner in byOwner without assigning it to a seat. */ }
|
|
191
|
+
}
|
|
118
192
|
}
|
|
119
193
|
return summary;
|
|
120
194
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { chmodSync, existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, writeFileSync } from 'node:fs';
|
|
2
|
-
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
3
3
|
import { dirname, join } from 'node:path';
|
|
4
4
|
import { Database } from 'bun:sqlite';
|
|
5
5
|
import { releaseLedgerRoot } from '../instance/resolve.js';
|
|
@@ -17,8 +17,8 @@ function legacyVersions(): string[] {
|
|
|
17
17
|
if (!existsSync(root)) return [];
|
|
18
18
|
return readdirSync(root).filter((version) => /^\d+\.\d+\.\d+(?:-(?:rc|alpha|beta)\.\d+)?$/.test(version) && existsSync(jsonPath(version)));
|
|
19
19
|
}
|
|
20
|
-
export function releasedVersion(): string {
|
|
21
|
-
const root = join(
|
|
20
|
+
export function releasedVersion(ledgerRoot = releaseLedgerRoot()): string {
|
|
21
|
+
const root = join(ledgerRoot, 'release');
|
|
22
22
|
if (!existsSync(root)) return '';
|
|
23
23
|
return readdirSync(root).filter((version) => /^\d+\.\d+\.\d+$/.test(version) && existsSync(join(root, version, 'release.json')))
|
|
24
24
|
.filter((version) => {
|
|
@@ -30,8 +30,8 @@ export function releasedVersion(): string {
|
|
|
30
30
|
})[0] ?? '';
|
|
31
31
|
}
|
|
32
32
|
const walWait = new Int32Array(new SharedArrayBuffer(4));
|
|
33
|
-
function open(): Database {
|
|
34
|
-
const path = join(
|
|
33
|
+
function open(root = releaseLedgerRoot()): Database {
|
|
34
|
+
const path = join(root, 'release', 'features.sqlite');
|
|
35
35
|
mkdirSync(dirname(path), { recursive: true });
|
|
36
36
|
const db = new Database(path, { create: true, strict: true });
|
|
37
37
|
try {
|
|
@@ -52,11 +52,61 @@ function open(): Database {
|
|
|
52
52
|
CREATE TABLE IF NOT EXISTS assignments (feature_id TEXT NOT NULL REFERENCES features(id), version TEXT NOT NULL, status TEXT NOT NULL, disposition TEXT, evidence TEXT, title_override TEXT, owner TEXT, kind TEXT, updated_at TEXT NOT NULL, updated_by TEXT NOT NULL, PRIMARY KEY(feature_id, version));
|
|
53
53
|
CREATE TABLE IF NOT EXISTS events (seq INTEGER PRIMARY KEY AUTOINCREMENT, at TEXT NOT NULL, by TEXT NOT NULL, feature_id TEXT NOT NULL, version TEXT NOT NULL, field TEXT NOT NULL, "from" TEXT, "to" TEXT, released TEXT NOT NULL, dev TEXT NOT NULL);
|
|
54
54
|
CREATE TABLE IF NOT EXISTS evidence (feature_id TEXT NOT NULL, version TEXT NOT NULL, ref TEXT NOT NULL, at TEXT NOT NULL, by TEXT NOT NULL);
|
|
55
|
-
CREATE TABLE IF NOT EXISTS imported_versions (version TEXT PRIMARY KEY);`);
|
|
55
|
+
CREATE TABLE IF NOT EXISTS imported_versions (version TEXT PRIMARY KEY, json_hash TEXT, imported_at TEXT);`);
|
|
56
|
+
const columns = db.query('PRAGMA table_info(imported_versions)').all() as Array<{ name: string }>;
|
|
57
|
+
if (!columns.some((column) => column.name === 'json_hash')) db.exec('ALTER TABLE imported_versions ADD COLUMN json_hash TEXT');
|
|
58
|
+
if (!columns.some((column) => column.name === 'imported_at')) db.exec('ALTER TABLE imported_versions ADD COLUMN imported_at TEXT');
|
|
59
|
+
db.exec('CREATE TABLE IF NOT EXISTS release_schedules (version TEXT PRIMARY KEY, cut_at TEXT NOT NULL, land_by TEXT, updated_at TEXT NOT NULL, updated_by TEXT NOT NULL)');
|
|
56
60
|
return db;
|
|
57
61
|
} catch (error) { db.close(); throw error; }
|
|
58
62
|
}
|
|
59
63
|
|
|
64
|
+
export interface ScheduleRow { version: string; cutAt: string; landBy: string | null; updatedAt: string; updatedBy: string }
|
|
65
|
+
type StoredSchedule = { version: string; cut_at: string; land_by: string | null; updated_at: string; updated_by: string };
|
|
66
|
+
function scheduleRow(row: StoredSchedule): ScheduleRow {
|
|
67
|
+
return { version: row.version, cutAt: row.cut_at, landBy: row.land_by, updatedAt: row.updated_at, updatedBy: row.updated_by };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function readSchedule(version: string, root?: string): ScheduleRow | null {
|
|
71
|
+
validateVersion(version);
|
|
72
|
+
if (!existsSync(join(root ?? releaseLedgerRoot(), 'release', 'features.sqlite'))) return null;
|
|
73
|
+
const db = open(root);
|
|
74
|
+
try {
|
|
75
|
+
const row = db.query('SELECT * FROM release_schedules WHERE version = ?').get(version) as StoredSchedule | null;
|
|
76
|
+
return row ? scheduleRow(row) : null;
|
|
77
|
+
} finally { db.close(); }
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function readSchedules(root?: string): ScheduleRow[] {
|
|
81
|
+
const db = open(root);
|
|
82
|
+
try { return (db.query('SELECT * FROM release_schedules ORDER BY version').all() as StoredSchedule[]).map(scheduleRow); }
|
|
83
|
+
finally { db.close(); }
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function writeSchedule(version: string, patch: { cutAt?: string; landBy?: string }, by: string, root?: string): ScheduleRow {
|
|
87
|
+
validateVersion(version);
|
|
88
|
+
const db = open(root);
|
|
89
|
+
try {
|
|
90
|
+
return transaction(db, () => {
|
|
91
|
+
const previous = db.query('SELECT * FROM release_schedules WHERE version = ?').get(version) as StoredSchedule | null;
|
|
92
|
+
const cutAt = patch.cutAt ?? previous?.cut_at;
|
|
93
|
+
if (!cutAt) throw new CliUserError('새 판에는 --cut-at 이 필요하다');
|
|
94
|
+
const landBy = patch.landBy ?? previous?.land_by ?? null;
|
|
95
|
+
if (previous && previous.cut_at === cutAt && previous.land_by === landBy) return scheduleRow(previous);
|
|
96
|
+
const at = new Date().toISOString();
|
|
97
|
+
db.query(`INSERT INTO release_schedules (version, cut_at, land_by, updated_at, updated_by) VALUES (?, ?, ?, ?, ?)
|
|
98
|
+
ON CONFLICT(version) DO UPDATE SET cut_at=excluded.cut_at, land_by=excluded.land_by, updated_at=excluded.updated_at, updated_by=excluded.updated_by`)
|
|
99
|
+
.run(version, cutAt, landBy, at, by);
|
|
100
|
+
for (const [field, from, to] of [
|
|
101
|
+
['cut_at', previous?.cut_at ?? null, cutAt], ['land_by', previous?.land_by ?? null, landBy],
|
|
102
|
+
] as const) {
|
|
103
|
+
if (from !== to) record(db, version, { at, by, id: '@version', field, from, to, released: releasedVersion(root), dev: devVersion() });
|
|
104
|
+
}
|
|
105
|
+
return { version, cutAt, landBy, updatedAt: at, updatedBy: by };
|
|
106
|
+
});
|
|
107
|
+
} finally { db.close(); }
|
|
108
|
+
}
|
|
109
|
+
|
|
60
110
|
function transaction<T>(db: Database, work: () => T): T {
|
|
61
111
|
db.exec('BEGIN IMMEDIATE');
|
|
62
112
|
try { const result = work(); db.exec('COMMIT'); return result; }
|
|
@@ -105,14 +155,32 @@ function putItem(db: Database, version: string, item: ChecklistItem): void {
|
|
|
105
155
|
ON CONFLICT(feature_id, version) DO UPDATE SET status=excluded.status, disposition=excluded.disposition, evidence=excluded.evidence, owner=excluded.owner, kind=excluded.kind, updated_at=excluded.updated_at, updated_by=excluded.updated_by`)
|
|
106
156
|
.run(item.id, version, item.status, item.disposition ?? null, item.evidence ?? null, feature && feature.title !== item.title ? item.title : null, item.owner ?? null, item.kind ?? null, item.updatedAt, item.updatedBy);
|
|
107
157
|
}
|
|
158
|
+
function jsonHash(contents: string | Buffer): string { return createHash('sha256').update(contents).digest('hex'); }
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* When did the ledger take this item away from `version`? A `move` out of it, a `remove` at it, or (carry-forward
|
|
162
|
+
* without a move event) the item now living in another version. null = never left — a JSON-only item is new.
|
|
163
|
+
* OP 10-02: without this, an old build's checklist.json re-added items the ledger had moved or removed (duplicates).
|
|
164
|
+
*/
|
|
165
|
+
function ledgerLeftAt(db: Database, id: string, version: string): string | null {
|
|
166
|
+
const event = db.query(`SELECT MAX(at) AS at FROM events WHERE feature_id = ? AND
|
|
167
|
+
((field = 'move' AND "from" = ?) OR (field = 'remove' AND version = ?))`).get(id, JSON.stringify(version), version) as { at: string | null };
|
|
168
|
+
const elsewhere = db.query('SELECT MAX(updated_at) AS at FROM assignments WHERE feature_id = ? AND version <> ?').get(id, version) as { at: string | null };
|
|
169
|
+
const times = [event.at, elsewhere.at].filter((t): t is string => typeof t === 'string');
|
|
170
|
+
return times.length ? times.sort().at(-1)! : null;
|
|
171
|
+
}
|
|
172
|
+
|
|
108
173
|
function importOnDb(db: Database, version: string): boolean {
|
|
109
|
-
|
|
174
|
+
const previous = db.query('SELECT json_hash FROM imported_versions WHERE version = ?').get(version) as { json_hash: string | null } | null;
|
|
110
175
|
const path = jsonPath(version);
|
|
111
176
|
if (!existsSync(path)) {
|
|
112
|
-
db.query('INSERT INTO imported_versions (version) VALUES (?)').run(version);
|
|
177
|
+
if (!previous) db.query('INSERT INTO imported_versions (version) VALUES (?)').run(version);
|
|
113
178
|
return false;
|
|
114
179
|
}
|
|
115
|
-
const
|
|
180
|
+
const contents = readFileSync(path);
|
|
181
|
+
const hash = jsonHash(contents);
|
|
182
|
+
if (previous && previous.json_hash === hash) return false;
|
|
183
|
+
const data = JSON.parse(contents.toString('utf8')) as Checklist;
|
|
116
184
|
if (data.version !== version) throw new CliUserError(`체크리스트 판 불일치: ${path}`);
|
|
117
185
|
if (!Array.isArray(data.items) || !Array.isArray(data.history)) throw new CliUserError(`잘못된 체크리스트: ${path}`);
|
|
118
186
|
const duplicate = new Set<string>();
|
|
@@ -120,25 +188,83 @@ function importOnDb(db: Database, version: string): boolean {
|
|
|
120
188
|
if (!item.id?.trim() || !item.title?.trim() || duplicate.has(item.id) || !['green', 'yellow', 'red', 'done'].includes(item.status)) throw new CliUserError(`잘못된 체크리스트 칸: ${version} ${item.id}`);
|
|
121
189
|
duplicate.add(item.id);
|
|
122
190
|
}
|
|
191
|
+
if (!previous) {
|
|
192
|
+
for (const item of data.items) {
|
|
193
|
+
const existing = db.query('SELECT title FROM features WHERE id = ?').get(item.id) as { title: string } | null;
|
|
194
|
+
// Keep a legacy per-version title until an explicit retitle unifies the feature identity.
|
|
195
|
+
if (!existing) putItem(db, version, item);
|
|
196
|
+
else {
|
|
197
|
+
db.query('INSERT OR IGNORE INTO assignments (feature_id, version, status, disposition, evidence, title_override, owner, kind, updated_at, updated_by) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)')
|
|
198
|
+
.run(item.id, version, item.status, item.disposition ?? null, item.evidence ?? null, item.title === existing.title ? null : item.title, item.owner ?? null, item.kind ?? null, item.updatedAt, item.updatedBy);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
for (const entry of data.history) record(db, version, entry);
|
|
202
|
+
db.query('INSERT INTO imported_versions (version, json_hash, imported_at) VALUES (?, ?, ?)').run(version, hash, new Date().toISOString());
|
|
203
|
+
return true;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const ledger = new Map(items(db, version).map((item) => [item.id, item]));
|
|
207
|
+
const changed: string[] = [];
|
|
208
|
+
let added = 0;
|
|
209
|
+
const ledgerWonIds: string[] = [];
|
|
210
|
+
const leftIds: string[] = [];
|
|
123
211
|
for (const item of data.items) {
|
|
124
|
-
const
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
212
|
+
const current = ledger.get(item.id);
|
|
213
|
+
if (!current) {
|
|
214
|
+
// Moved or removed in the ledger: only a JSON edit made after that may bring it back.
|
|
215
|
+
const leftAt = ledgerLeftAt(db, item.id, version);
|
|
216
|
+
if (leftAt && !(Date.parse(item.updatedAt) > Date.parse(leftAt))) { leftIds.push(item.id); continue; }
|
|
217
|
+
const feature = db.query('SELECT title FROM features WHERE id = ?').get(item.id) as { title: string } | null;
|
|
218
|
+
putItem(db, version, item);
|
|
219
|
+
if (feature && feature.title !== item.title) db.query('UPDATE assignments SET title_override = ? WHERE feature_id = ? AND version = ?').run(item.title, item.id, version);
|
|
220
|
+
added++;
|
|
221
|
+
changed.push(item.id);
|
|
222
|
+
record(db, version, { at: new Date().toISOString(), by: item.updatedBy, id: item.id, field: 'reimport', from: null, to: item, released: data.released, dev: data.dev });
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
const fields = ['title', 'status', 'evidence', 'owner', 'disposition', 'kind'] as const;
|
|
226
|
+
const baseEvidence = (db.query('SELECT evidence FROM assignments WHERE feature_id = ? AND version = ?').get(item.id, version) as { evidence: string | null }).evidence ?? undefined;
|
|
227
|
+
const preserveRefs = item.evidence === baseEvidence || item.evidence === current.evidence;
|
|
228
|
+
const differs = fields.some((field) => field === 'evidence'
|
|
229
|
+
? (!preserveRefs && current.evidence !== item.evidence)
|
|
230
|
+
: current[field] !== item[field]);
|
|
231
|
+
if (!differs) continue;
|
|
232
|
+
if (!(Date.parse(item.updatedAt) > Date.parse(current.updatedAt))) {
|
|
233
|
+
ledgerWonIds.push(item.id);
|
|
234
|
+
continue;
|
|
130
235
|
}
|
|
236
|
+
const incoming: ChecklistItem = { ...current, status: item.status, updatedAt: item.updatedAt, updatedBy: item.updatedBy };
|
|
237
|
+
for (const field of fields) {
|
|
238
|
+
if (field === 'status') continue;
|
|
239
|
+
if (field === 'evidence' && preserveRefs) continue;
|
|
240
|
+
if (item[field] === undefined) delete (incoming as unknown as Record<string, unknown>)[field];
|
|
241
|
+
else (incoming as unknown as Record<string, unknown>)[field] = item[field];
|
|
242
|
+
}
|
|
243
|
+
if (!preserveRefs) db.query('DELETE FROM evidence WHERE feature_id = ? AND version = ?').run(item.id, version);
|
|
244
|
+
const persisted = preserveRefs ? { ...incoming, evidence: baseEvidence } : incoming;
|
|
245
|
+
putItem(db, version, persisted);
|
|
246
|
+
if (incoming.title !== current.title) {
|
|
247
|
+
db.query('UPDATE assignments SET title_override = ? WHERE feature_id = ? AND version = ?')
|
|
248
|
+
.run(incoming.title === (db.query('SELECT title FROM features WHERE id = ?').get(item.id) as { title: string }).title ? null : incoming.title, item.id, version);
|
|
249
|
+
}
|
|
250
|
+
changed.push(item.id);
|
|
251
|
+
record(db, version, { at: new Date().toISOString(), by: item.updatedBy, id: item.id, field: 'reimport', from: current, to: incoming, released: data.released, dev: data.dev });
|
|
131
252
|
}
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
253
|
+
db.query('UPDATE imported_versions SET json_hash = ?, imported_at = ? WHERE version = ?').run(hash, new Date().toISOString(), version);
|
|
254
|
+
const ledgerWon = ledgerWonIds.length;
|
|
255
|
+
debug.log('release.features', 'reimported', { version, changed: changed.length - added, added, ledgerWon, notReadded: leftIds.length });
|
|
256
|
+
// Two separate lines: what was taken from the old build's JSON, and where the JSON differs but the ledger is newer
|
|
257
|
+
// (a conflict a person should know about even though nothing changed in the ledger).
|
|
258
|
+
if (changed.length) console.error(`⚠ checklist ${version}: 옛 판이 checklist.json 에 쓴 ${changed.length}칸을 다시 들였다(${changed.slice(0, 5).join(', ')})`);
|
|
259
|
+
if (leftIds.length) console.error(`⚠ checklist ${version}: 원장에서 옮기거나 지운 칸 ${leftIds.length}은 옛 판 checklist.json 에 남아 있어도 다시 넣지 않았다(${leftIds.slice(0, 5).join(', ')})`);
|
|
260
|
+
if (ledgerWon) console.error(`⚠ checklist ${version}: 옛 판 checklist.json 과 원장이 다르다 — 원장이 더 새로워 원장 값 유지 ${ledgerWon}칸(${ledgerWonIds.slice(0, 5).join(', ')})`);
|
|
261
|
+
return false;
|
|
135
262
|
}
|
|
136
263
|
export function importJson(version: string): boolean {
|
|
137
264
|
validateVersion(version);
|
|
138
265
|
if (!existsSync(jsonPath(version))) return false;
|
|
139
266
|
const db = open();
|
|
140
267
|
try {
|
|
141
|
-
if (db.query('SELECT 1 FROM imported_versions WHERE version = ?').get(version)) return false;
|
|
142
268
|
const imported = transaction(db, () => importOnDb(db, version));
|
|
143
269
|
if (imported) debug.log('release.features', 'imported', { version });
|
|
144
270
|
return imported;
|
|
@@ -152,10 +278,12 @@ function importLegacyAll(): void {
|
|
|
152
278
|
} finally { db.close(); }
|
|
153
279
|
}
|
|
154
280
|
|
|
155
|
-
|
|
281
|
+
/** `root` reads another ledger (no legacy JSON import there); omitted = the default ledger as before. */
|
|
282
|
+
export function list(version: string, released?: string, dev = devVersion(), root?: string): Checklist {
|
|
156
283
|
validateVersion(version);
|
|
157
|
-
importJson(version);
|
|
158
|
-
|
|
284
|
+
if (root === undefined) importJson(version);
|
|
285
|
+
released ??= releasedVersion(root);
|
|
286
|
+
const db = open(root);
|
|
159
287
|
try { return readTransaction(db, () => snapshot(db, version, released, dev)); } finally { db.close(); }
|
|
160
288
|
}
|
|
161
289
|
|
|
@@ -315,13 +443,20 @@ export function exportJson(version: string, released = releasedVersion(), dev =
|
|
|
315
443
|
const path = jsonPath(version);
|
|
316
444
|
mkdirSync(dirname(path), { recursive: true });
|
|
317
445
|
const temp = `${path}.${process.pid}.${randomUUID()}.tmp`;
|
|
446
|
+
const contents = `${JSON.stringify(data, null, 2)}\n`;
|
|
318
447
|
try {
|
|
319
|
-
writeFileSync(temp,
|
|
448
|
+
writeFileSync(temp, contents, { mode: 0o600 });
|
|
320
449
|
chmodSync(temp, 0o600);
|
|
321
450
|
renameSync(temp, path);
|
|
322
451
|
} finally { if (existsSync(temp)) rmSync(temp); }
|
|
323
452
|
const db = open();
|
|
324
|
-
try { transaction(db, () => {
|
|
453
|
+
try { transaction(db, () => {
|
|
454
|
+
// Track only our bytes: a legacy replacement after rename must remain detectable.
|
|
455
|
+
const hash = jsonHash(contents);
|
|
456
|
+
db.query(`INSERT INTO imported_versions (version, json_hash, imported_at) VALUES (?, ?, ?)
|
|
457
|
+
ON CONFLICT(version) DO UPDATE SET json_hash = excluded.json_hash, imported_at = excluded.imported_at`)
|
|
458
|
+
.run(version, hash, new Date().toISOString());
|
|
459
|
+
}); }
|
|
325
460
|
finally { db.close(); }
|
|
326
461
|
return data;
|
|
327
462
|
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// Deterministic merge for `release/next.md` — two landings that each append a line to the same section
|
|
2
|
+
// keep both lines (union per section, same sentence once). Returns null when it cannot decide (the caller
|
|
3
|
+
// falls back to its usual resolver): a side deleted a line, or a stage is missing.
|
|
4
|
+
|
|
5
|
+
export const NEXT_MD_PATH = 'release/next.md';
|
|
6
|
+
|
|
7
|
+
type Section = { header: string | null; lines: string[] };
|
|
8
|
+
|
|
9
|
+
function sections(text: string): Section[] {
|
|
10
|
+
const out: Section[] = [{ header: null, lines: [] }];
|
|
11
|
+
for (const line of text.replace(/\r\n/g, '\n').split('\n')) {
|
|
12
|
+
if (/^## /.test(line)) out.push({ header: line.trim(), lines: [] });
|
|
13
|
+
else out[out.length - 1]!.lines.push(line);
|
|
14
|
+
}
|
|
15
|
+
return out;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const key = (line: string) => line.trim();
|
|
19
|
+
|
|
20
|
+
function added(base: Section | undefined, side: Section): string[] {
|
|
21
|
+
const before = new Set((base?.lines ?? []).map(key));
|
|
22
|
+
return side.lines.filter((line) => key(line) !== '' && !before.has(key(line)));
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function deletedAny(base: Section[], side: Section[]): boolean {
|
|
26
|
+
for (const b of base) {
|
|
27
|
+
const s = side.find((x) => x.header === b.header);
|
|
28
|
+
const kept = new Set((s?.lines ?? []).map(key));
|
|
29
|
+
if (b.lines.some((line) => key(line) !== '' && !kept.has(key(line)))) return true;
|
|
30
|
+
}
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Union of both sides' additions over `theirs` (the branch being merged in). Null = not decidable here. */
|
|
35
|
+
export function resolveNextMdConflict(base: string, ours: string, theirs: string): string | null {
|
|
36
|
+
const b = sections(base);
|
|
37
|
+
const o = sections(ours);
|
|
38
|
+
// Our side only appends (harness landings add one line); a deletion on our side is not ours to decide.
|
|
39
|
+
if (deletedAny(b, o)) return null;
|
|
40
|
+
const result = sections(theirs);
|
|
41
|
+
for (const section of o) {
|
|
42
|
+
const extra = added(b.find((x) => x.header === section.header), section);
|
|
43
|
+
if (extra.length === 0) continue;
|
|
44
|
+
let target = result.find((x) => x.header === section.header);
|
|
45
|
+
if (!target) {
|
|
46
|
+
target = { header: section.header, lines: [''] };
|
|
47
|
+
result.push(target);
|
|
48
|
+
}
|
|
49
|
+
const present = new Set(target.lines.map(key));
|
|
50
|
+
const fresh = extra.filter((line) => !present.has(key(line)));
|
|
51
|
+
if (fresh.length === 0) continue;
|
|
52
|
+
// Insert before the section's trailing blank lines so the blank separator stays last.
|
|
53
|
+
let end = target.lines.length;
|
|
54
|
+
while (end > 0 && key(target.lines[end - 1]!) === '') end -= 1;
|
|
55
|
+
target.lines.splice(end, 0, ...fresh);
|
|
56
|
+
}
|
|
57
|
+
return result.map((section) => (section.header === null ? section.lines : [section.header, ...section.lines]).join('\n')).join('\n');
|
|
58
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { closeSync, mkdirSync, openSync, readFileSync, readdirSync, renameSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
2
2
|
import { randomUUID } from 'node:crypto';
|
|
3
3
|
import { join } from 'node:path';
|
|
4
|
+
import { debug } from '../debug/log.js';
|
|
4
5
|
|
|
5
6
|
export interface ReleaseNoteFragment {
|
|
6
7
|
pr: number;
|
|
@@ -12,7 +13,8 @@ export interface ReleaseNoteFragment {
|
|
|
12
13
|
mergeSha?: string;
|
|
13
14
|
}
|
|
14
15
|
|
|
15
|
-
type
|
|
16
|
+
export type ReleaseNoteSection = Omit<ReleaseNoteFragment, 'pr' | 'source' | 'mergeSha'>;
|
|
17
|
+
type Section = ReleaseNoteSection;
|
|
16
18
|
|
|
17
19
|
/** Blank out fenced code blocks (``` / ~~~) line-for-line so example headings and fields inside them are never read as the real section. */
|
|
18
20
|
function blankFencedCode(text: string): string {
|
|
@@ -69,10 +71,10 @@ export function renderReleaseNoteSection(fragment: Section): string {
|
|
|
69
71
|
return `## 릴리스 노트\n- 한 줄: ${fragment.line}\n- 종류: ${fragment.kind}\n- 문서: ${'path' in fragment.docs ? fragment.docs.path : `없음(${fragment.docs.none})`}\n- 대상: ${fragment.target}\n`;
|
|
70
72
|
}
|
|
71
73
|
|
|
72
|
-
export function
|
|
74
|
+
export function parseGoalReleaseNoteSection(goalDocument: string): ReturnType<typeof parseReleaseNoteSection> & { hasSection: boolean } {
|
|
73
75
|
const document = blankFencedCode(goalDocument);
|
|
74
76
|
const goal = /^ {0,3}## 목표[ \t]*\r?$/m.exec(document);
|
|
75
|
-
let section = '';
|
|
77
|
+
let section = goal ? '' : document;
|
|
76
78
|
if (goal) {
|
|
77
79
|
const rest = document.slice(goal.index + goal[0].length);
|
|
78
80
|
let end = rest.length;
|
|
@@ -84,7 +86,20 @@ export function harnessReleaseNote(goalDocument: string, title: string): Section
|
|
|
84
86
|
}
|
|
85
87
|
section = rest.slice(0, end);
|
|
86
88
|
}
|
|
87
|
-
return
|
|
89
|
+
return {
|
|
90
|
+
...parseReleaseNoteSection(section),
|
|
91
|
+
hasSection: /(?:^|\n)## 릴리스 노트[ \t]*\r?(?:\n|$)/.test(section),
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function harnessReleaseNote(goalDocument: string, title: string, shard = false): Section {
|
|
96
|
+
const parsed = parseGoalReleaseNoteSection(goalDocument);
|
|
97
|
+
if (parsed.fragment) return parsed.fragment;
|
|
98
|
+
debug.log('release-note', 'harness-fallback', {
|
|
99
|
+
reason: parsed.hasSection ? 'invalid' : 'missing',
|
|
100
|
+
shard,
|
|
101
|
+
});
|
|
102
|
+
return {
|
|
88
103
|
line: title,
|
|
89
104
|
kind: 'internal',
|
|
90
105
|
docs: { none: '하니스 자동 생성' },
|
|
@@ -92,6 +107,46 @@ export function harnessReleaseNote(goalDocument: string, title: string): Section
|
|
|
92
107
|
};
|
|
93
108
|
}
|
|
94
109
|
|
|
110
|
+
/** Only the worktree's release notes enter the public cut. Keep repeats byte-for-byte idempotent. */
|
|
111
|
+
export function addNextMdReleaseNote(worktreePath: string, note: Section): boolean {
|
|
112
|
+
if (note.target !== 'next' || note.kind === 'internal') return false;
|
|
113
|
+
const path = join(worktreePath, 'release', 'next.md');
|
|
114
|
+
// Public next notes require the worktree's cut source; ENOENT must stop PR creation.
|
|
115
|
+
const text = readFileSync(path, 'utf8');
|
|
116
|
+
const sentence = note.line.trim();
|
|
117
|
+
const prefixed = sentence.startsWith(`${note.kind} — `) ? sentence : `${note.kind} — ${sentence}`;
|
|
118
|
+
const item = `- ${prefixed.replace(/\.$/, '')}. Documentation: ${'path' in note.docs ? note.docs.path : 'none'}. Target: next.`;
|
|
119
|
+
const lines = text.split('\n');
|
|
120
|
+
const sameSentence = (line: string) => line.replace(/\r$/, '')
|
|
121
|
+
.replace(/\. Documentation:.*$/, '').replace(/\.$/, '') === `- ${prefixed.replace(/\.$/, '')}`;
|
|
122
|
+
const existing = lines.findIndex(sameSentence);
|
|
123
|
+
if (existing >= 0) {
|
|
124
|
+
if (lines[existing]!.replace(/\r$/, '') === item) return false;
|
|
125
|
+
// Same sentence, different documentation: the PR body is the source — make next.md say the same thing.
|
|
126
|
+
lines[existing] = item;
|
|
127
|
+
writeFileSync(path, lines.join('\n'));
|
|
128
|
+
debug.log('release-note', 'next-md-updated', { kind: note.kind, docs: 'path' in note.docs ? 'path' : 'none' });
|
|
129
|
+
return true;
|
|
130
|
+
}
|
|
131
|
+
const heading = `## ${note.kind === 'feat' ? 'Feat' : note.kind === 'fix' ? 'Fix' : 'Security'}`;
|
|
132
|
+
let index = lines.findIndex((line) => line.trimEnd() === heading);
|
|
133
|
+
if (index < 0) {
|
|
134
|
+
const fix = lines.findIndex((line) => line.trimEnd() === '## Fix');
|
|
135
|
+
if (fix < 0) throw new Error(`release/next.md has no ## Fix section: ${path}`);
|
|
136
|
+
index = fix + 1;
|
|
137
|
+
while (index < lines.length && !/^##\s/.test(lines[index]!)) index++;
|
|
138
|
+
lines.splice(index, 0, heading, '');
|
|
139
|
+
index++;
|
|
140
|
+
}
|
|
141
|
+
index++;
|
|
142
|
+
while (index < lines.length && !/^##\s/.test(lines[index]!)) index++;
|
|
143
|
+
while (index > 0 && lines[index - 1] === '') index--;
|
|
144
|
+
if (index === lines.findIndex((line) => line.trimEnd() === heading) + 1) lines.splice(index++, 0, '');
|
|
145
|
+
lines.splice(index, 0, item);
|
|
146
|
+
writeFileSync(path, lines.join('\n'));
|
|
147
|
+
return true;
|
|
148
|
+
}
|
|
149
|
+
|
|
95
150
|
export function releaseNotesDir(instanceRoot: string): string {
|
|
96
151
|
return join(instanceRoot, 'release', 'notes');
|
|
97
152
|
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { CliUserError } from '../cli/cli-user-error.js';
|
|
2
|
+
import { debug } from '../debug/log.js';
|
|
3
|
+
import { readSchedule, readSchedules, writeSchedule, type ScheduleRow } from './feature-store.js';
|
|
4
|
+
|
|
5
|
+
export type ReleaseSchedule = ScheduleRow;
|
|
6
|
+
|
|
7
|
+
function utcIso(input: string): string {
|
|
8
|
+
const match = /^(\d{4}-\d{2}-\d{2})T(\d{2}:\d{2})(?::(\d{2})(?:\.\d{1,3})?)?(Z|([+-])(\d{2}):(\d{2}))$/.exec(input);
|
|
9
|
+
if (!match) throw new CliUserError(`시각에는 ISO 오프셋 필요: ${input}`);
|
|
10
|
+
const hours = Number(match[6] ?? 0), minutes = Number(match[7] ?? 0);
|
|
11
|
+
const offset = (match[5] === '-' ? -1 : 1) * (hours * 60 + minutes);
|
|
12
|
+
const millis = Date.parse(input);
|
|
13
|
+
if (hours > 23 || minutes > 59 || !Number.isFinite(millis) ||
|
|
14
|
+
new Date(millis + offset * 60_000).toISOString().slice(0, 19) !== `${match[1]}T${match[2]}:${match[3] ?? '00'}`) {
|
|
15
|
+
throw new CliUserError(`잘못된 ISO 시각: ${input}`);
|
|
16
|
+
}
|
|
17
|
+
return new Date(millis).toISOString();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function formatKst(utc: string): string {
|
|
21
|
+
const parts = new Intl.DateTimeFormat('ko-KR', { timeZone: 'Asia/Seoul', month: '2-digit', day: '2-digit', weekday: 'short', hour: '2-digit', minute: '2-digit', hourCycle: 'h23' }).formatToParts(new Date(utc));
|
|
22
|
+
const get = (type: Intl.DateTimeFormatPartTypes) => parts.find((part) => part.type === type)!.value;
|
|
23
|
+
return `${get('month')}-${get('day')}(${get('weekday')}) ${get('hour')}:${get('minute')} KST`;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function getSchedule(version: string, root?: string): ReleaseSchedule | null {
|
|
27
|
+
const schedule = readSchedule(version, root);
|
|
28
|
+
debug.log('release.schedule', 'read', { version, cutAt: schedule?.cutAt ?? null, landBy: schedule?.landBy ?? null });
|
|
29
|
+
return schedule;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function listSchedules(root?: string): ReleaseSchedule[] {
|
|
33
|
+
const schedules = readSchedules(root);
|
|
34
|
+
for (const { version, cutAt, landBy } of schedules) debug.log('release.schedule', 'read', { version, cutAt, landBy });
|
|
35
|
+
return schedules;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function setSchedule(version: string, patch: { cutAt?: string; landBy?: string }, by: string, root?: string): ReleaseSchedule {
|
|
39
|
+
const schedule = writeSchedule(version, {
|
|
40
|
+
...(patch.cutAt !== undefined ? { cutAt: utcIso(patch.cutAt) } : {}),
|
|
41
|
+
...(patch.landBy !== undefined ? { landBy: utcIso(patch.landBy) } : {}),
|
|
42
|
+
}, by, root);
|
|
43
|
+
debug.log('release.schedule', 'set', { version, cutAt: schedule.cutAt, landBy: schedule.landBy });
|
|
44
|
+
return schedule;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function formatSchedule(schedule: ReleaseSchedule): string {
|
|
48
|
+
const cut = formatKst(schedule.cutAt);
|
|
49
|
+
const deadline = schedule.landBy ? formatKst(schedule.landBy) : null;
|
|
50
|
+
const sameDay = schedule.landBy && new Date(Date.parse(schedule.landBy) + 9 * 60 * 60_000).toISOString().slice(0, 10)
|
|
51
|
+
=== new Date(Date.parse(schedule.cutAt) + 9 * 60 * 60_000).toISOString().slice(0, 10);
|
|
52
|
+
return `${schedule.version} 컷 ${cut}${deadline ? ` · 착지 마감 ${sameDay ? deadline.split(' ')[1] : deadline}` : ''}`;
|
|
53
|
+
}
|
package/src/repl/index.ts
CHANGED
|
@@ -21,6 +21,7 @@ import { createInterface, type Interface as ReadlineInterface } from 'node:readl
|
|
|
21
21
|
import { existsSync, readFileSync } from 'node:fs';
|
|
22
22
|
import { resolve as resolvePath, isAbsolute, basename } from 'node:path';
|
|
23
23
|
import * as ui from '../ui.js';
|
|
24
|
+
import { suggestProjectForFolder } from '../project/project-store.js';
|
|
24
25
|
import { runTurn } from '../session/chat.js';
|
|
25
26
|
import {
|
|
26
27
|
ensureCliSession,
|
|
@@ -450,6 +451,12 @@ export async function runRepl(opts: ReplOpts): Promise<number> {
|
|
|
450
451
|
const session = ensureCliSession(cfg, initialId);
|
|
451
452
|
setActiveSessionId(session.id);
|
|
452
453
|
setAmbientSessionId(session.id);
|
|
454
|
+
if (!opts.jsonOutput && (!initialId || session.id !== initialId)) {
|
|
455
|
+
try {
|
|
456
|
+
const project = suggestProjectForFolder(process.cwd());
|
|
457
|
+
if (project) ui.info(`Project suggestion: ${project.name} (${project.id})`);
|
|
458
|
+
} catch { /* suggestions must not interrupt the REPL */ }
|
|
459
|
+
}
|
|
453
460
|
const sessionIdRef = { current: session.id };
|
|
454
461
|
|
|
455
462
|
const tools = opts.enableTools ? buildReplTools() : undefined;
|