elanous 0.2.6 → 0.2.8
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/.env.example +10 -7
- package/README.md +4 -0
- package/apps/pwa/out/404/index.html +1 -1
- package/apps/pwa/out/404.html +1 -1
- package/apps/pwa/out/_next/static/chunks/1105-c473ed46272fba79.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1201-09613bce624467c0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1377-1efeffbbac0963ff.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1470.98a6ab61ccb4131c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/216-5acd01cbde4acda7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2330-c2f07246c8788af1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{3995-bee512a7063036ce.js → 2375-cb6debea56cce4fb.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/2415.ed6428ff334c7ce2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2929-617f1e66a55be184.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2977-e38442e86289f1d3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2982-2a2df543be3d2e5a.js +25 -0
- package/apps/pwa/out/_next/static/chunks/{8d0d9faf-97887ff99713b84e.js → 2ea96958-e8c7e112de50b067.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{5329.8b7e4bca684b6eec.js → 3403.4bc97b4066945a94.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/3441-a1dd4acfeda14488.js +1 -0
- package/apps/pwa/out/_next/static/chunks/371-4ad6af55dad02ced.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4114-3e63117452378807.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{7633.0654fe6ea259c1ee.js → 4323.c5dfb6f503304775.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/4836-3084c58c1725a40b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{2984-977cfa339fcd65a5.js → 5080-1613752891fcadb4.js} +3 -3
- package/apps/pwa/out/_next/static/chunks/5609-012bb3557211f07e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{763-7d0e1913b5074e98.js → 5791-bb82743fa6299562.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{4240-7153881b0b377306.js → 6062-73e68343a9814f6a.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/6090-85a3f05f954e5697.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6284-69db415193d5f8bd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6320-baac91d68843a9a6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6416-505ba707cacafbba.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6725-68ab7cc0b1faf00d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6998-a654fe5c58271600.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7032-f4ef698518fba61c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7647.2fcd30d894d5ee30.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7673-2eda1507d38dadb8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7990-e12efba616f46e03.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{91ff6dda-3b33d42d20657429.js → 8046cadf-211ca3c0e00f2b72.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/8067-d976b441a104ab74.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8142-744998d65d7bb17e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8519-5632377cce994f86.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8619-4365e03a2862830f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8894-7317998f6cb97d8c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/920-f7dceb78f1c585ef.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9454-a3e0953307216c58.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9651-3cd291a6fa1370d2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9740-8f5ff96c522b5f55.js +1 -0
- package/apps/pwa/out/_next/static/chunks/ae787e4c-f2998de7f1ba72a4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-859cea1570b6227a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-9aa47b0bc6d570ff.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-f4d2ecbbae6088d6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/board/page-2053e49930c82748.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-d4b6df905f185055.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/bots/page-a70ba27c33003dd0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/page-186fc6952f027acc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/consult/page-18166f885d831d3e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/control/page-1c9d6cfe68b121c2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-cbbe03c0da7b081a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/editor/page-c108ff1fdb665c97.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/exec/page-147a328753d983e2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/field/page-799395a9df7d95aa.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-859cea1570b6227a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/page-6a8158b71d1fe9d7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/layout-9567560270857531.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live/page-e7ed3d010153d818.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/page-c0c0d19d51342d3e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-2e002ad8faa43291.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/morning/page-b672e02e4bec8deb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/not-found-1ba4a5d857276334.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-9db2226f38c542fb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/page-70ee65f5f7a151dc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-0bb8e43203efb23b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-092e1bcf27d751cc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-eb52580c4b75b905.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-7464c1987dd76e6d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/page-e573437b0a1c728f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-294300625e49e4d3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-859cea1570b6227a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/page-085847b088675978.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/share/page-9fe9d38c36999eb4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-d8ab4d1489223459.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-6141f67f531d8b6a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/term/page-295a02b1026b8255.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/trace/page-1eb9a0e1e054209d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/vault/page-82efa177a30f3741.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-d0be51cec4f62606.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-2e367075ffdbc062.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-647ed2b66c1d49f1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-ba058c56e38f2792.js +1 -0
- package/apps/pwa/out/_next/static/chunks/framework-ea806bb57f2b8923.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-8d2302399c2c8f87.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-app-6870f30ea99f8346.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_app-b10928c60f0b8488.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_error-689039227d443fab.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{webpack-37b8bf78a3c6eec5.js → webpack-e5bea36f183b25e6.js} +1 -1
- package/apps/pwa/out/_next/static/css/5ef72a618a8994f3.css +1 -0
- package/apps/pwa/out/_next/static/lywc8m_HDzH9MKSDQhyEj/_buildManifest.js +1 -0
- package/apps/pwa/out/approvals/index.html +1 -1
- package/apps/pwa/out/approvals/index.txt +22 -22
- package/apps/pwa/out/autopilot/index.html +1 -1
- package/apps/pwa/out/autopilot/index.txt +22 -22
- package/apps/pwa/out/board/index.html +1 -1
- package/apps/pwa/out/board/index.txt +22 -22
- package/apps/pwa/out/botlab/index.html +1 -1
- package/apps/pwa/out/botlab/index.txt +21 -21
- package/apps/pwa/out/bots/index.html +1 -1
- package/apps/pwa/out/bots/index.txt +21 -21
- package/apps/pwa/out/chat/index.html +1 -1
- package/apps/pwa/out/chat/index.txt +21 -21
- package/apps/pwa/out/consult/index.html +1 -0
- package/apps/pwa/out/consult/index.txt +32 -0
- package/apps/pwa/out/control/index.html +1 -1
- package/apps/pwa/out/control/index.txt +22 -22
- package/apps/pwa/out/design-check/index.html +1 -1
- package/apps/pwa/out/design-check/index.txt +22 -22
- package/apps/pwa/out/editor/index.html +1 -1
- package/apps/pwa/out/editor/index.txt +22 -22
- package/apps/pwa/out/exec/index.html +1 -0
- package/apps/pwa/out/exec/index.txt +32 -0
- package/apps/pwa/out/field/index.html +1 -0
- package/apps/pwa/out/field/index.txt +32 -0
- package/apps/pwa/out/healthz +1 -1
- package/apps/pwa/out/index.html +1 -1
- package/apps/pwa/out/index.txt +21 -21
- package/apps/pwa/out/intake/index.html +1 -1
- package/apps/pwa/out/intake/index.txt +22 -22
- package/apps/pwa/out/live/index.html +1 -1
- package/apps/pwa/out/live/index.txt +22 -22
- package/apps/pwa/out/market/index.html +1 -1
- package/apps/pwa/out/market/index.txt +21 -21
- package/apps/pwa/out/missions/placeholder/index.html +1 -1
- package/apps/pwa/out/missions/placeholder/index.txt +21 -21
- package/apps/pwa/out/morning/index.html +1 -1
- package/apps/pwa/out/morning/index.txt +22 -22
- package/apps/pwa/out/observatory/index.html +1 -1
- package/apps/pwa/out/observatory/index.txt +22 -22
- package/apps/pwa/out/reflection/index.html +1 -1
- package/apps/pwa/out/reflection/index.txt +22 -22
- package/apps/pwa/out/scheduler/index.html +1 -1
- package/apps/pwa/out/scheduler/index.txt +22 -22
- package/apps/pwa/out/sessions/index.html +1 -1
- package/apps/pwa/out/sessions/index.txt +21 -21
- package/apps/pwa/out/settings/devices/index.html +1 -1
- package/apps/pwa/out/settings/devices/index.txt +22 -22
- package/apps/pwa/out/settings/index.html +1 -1
- package/apps/pwa/out/settings/index.txt +22 -22
- package/apps/pwa/out/setup/done/index.html +1 -1
- package/apps/pwa/out/setup/done/index.txt +22 -22
- package/apps/pwa/out/setup/index.html +1 -1
- package/apps/pwa/out/setup/index.txt +22 -22
- package/apps/pwa/out/share/index.html +1 -1
- package/apps/pwa/out/share/index.txt +22 -22
- package/apps/pwa/out/showroom/index.html +1 -1
- package/apps/pwa/out/showroom/index.txt +21 -21
- package/apps/pwa/out/tasks/index.html +1 -1
- package/apps/pwa/out/tasks/index.txt +22 -22
- package/apps/pwa/out/term/index.html +1 -1
- package/apps/pwa/out/term/index.txt +22 -22
- package/apps/pwa/out/trace/index.html +1 -1
- package/apps/pwa/out/trace/index.txt +22 -22
- package/apps/pwa/out/vault/index.html +1 -1
- package/apps/pwa/out/vault/index.txt +22 -22
- package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
- package/apps/pwa/out/workflows/chat-ui/index.txt +22 -22
- package/apps/pwa/out/workflows/index.html +1 -1
- package/apps/pwa/out/workflows/index.txt +22 -22
- package/apps/pwa/out/workspace/index.html +1 -1
- package/apps/pwa/out/workspace/index.txt +22 -22
- package/apps/pwa/out/worktrees/index.html +1 -1
- package/apps/pwa/out/worktrees/index.txt +22 -22
- package/bin/elanous.cjs +107 -0
- package/graphs/field/field-feed.yaml +24 -0
- package/graphs/field/recipes.yaml +12 -0
- package/graphs/landing/landing-heal.yaml +3 -1
- package/graphs/landing/recipes.yaml +3 -0
- package/graphs/release/recipes.yaml +10 -0
- package/graphs/release/release-loop.yaml +10 -3
- package/graphs/test-diet/recipes.yaml +9 -0
- package/graphs/test-diet/test-diet.yaml +22 -0
- package/package.json +6 -5
- package/persona-presets/_index.yaml +10 -0
- package/persona-presets/_schema.json +146 -0
- package/persona-presets/dana.yaml +26 -0
- package/persona-presets/haru.yaml +27 -0
- package/persona-presets/kai.yaml +27 -0
- package/persona-presets/leo.yaml +27 -0
- package/persona-presets/mira.yaml +30 -0
- package/persona-presets/nari.yaml +27 -0
- package/persona-presets/sora.yaml +27 -0
- package/persona-presets/yuna.yaml +27 -0
- package/plugins/card-followup/README.md +13 -4
- package/plugins/card-followup/graphs/card-followup.yaml +3 -1
- package/plugins/card-followup/graphs/recipes.yaml +5 -1
- package/plugins/card-followup/graphs/run-step.ts +122 -18
- package/plugins/card-followup/graphs/sales-strategy.ts +173 -0
- package/plugins/card-followup/plugin.json +1 -1
- package/plugins/doc-draft/README.md +13 -0
- package/plugins/doc-draft/examples/brief-sample.json +8 -0
- package/plugins/doc-draft/graphs/doc-draft.yaml +14 -0
- package/plugins/doc-draft/graphs/recipes.yaml +9 -0
- package/plugins/doc-draft/graphs/run-step.ts +113 -0
- package/plugins/doc-draft/plugin.json +24 -0
- package/plugins/geo-check/README.md +3 -1
- package/plugins/geo-check/graphs/run-step.ts +58 -13
- package/plugins/geo-check/plugin.json +1 -1
- package/plugins/job-coach/.codex-plugin/plugin.json +1 -1
- package/plugins/job-coach/README.md +1 -1
- package/plugins/job-coach/graphs/run-step.ts +2 -2
- package/plugins/job-coach/plugin.json +1 -1
- package/plugins/job-coach/plugin.ts +1 -1
- package/scripts/affinity/endcard.sh +3 -2
- package/scripts/affinity/reframe.sh +3 -1
- package/scripts/ci-public-export-leak-gate.ts +48 -0
- package/scripts/codex-quota-alert.ts +122 -258
- package/scripts/coord-tracks.json +4 -4
- package/scripts/field-feed/deliver-node.ts +57 -0
- package/scripts/field-feed/feed-node.ts +67 -0
- package/scripts/field-feed/lib.ts +164 -0
- package/scripts/field-feed/reel-node.ts +24 -0
- package/scripts/field-feed/start.ts +62 -0
- package/scripts/fixtures/vflow-sample.ndjson +6 -0
- package/scripts/install.sh +109 -21
- package/scripts/landing-heal/clean-checkout.ts +114 -0
- package/scripts/landing-heal/run-mission.ts +38 -3
- package/scripts/lib/pwa-scenarios/scenarios.ts +2 -1
- package/scripts/persona-presets/lint.ts +79 -0
- package/scripts/public-export.ts +8 -4
- package/scripts/release-loop/baseline-from-logs.ts +92 -0
- package/scripts/release-loop/checklist-gate-node.ts +2 -2
- package/scripts/release-loop/env-known-failures.ts +31 -0
- package/scripts/release-loop/gate-node.ts +108 -22
- package/scripts/release-loop/known-issues-node.ts +159 -0
- package/scripts/release-loop/npm-publish-node.ts +124 -0
- package/scripts/release-loop/ops-upgrade-node.ts +95 -0
- package/scripts/release-loop/publish-node.ts +23 -1
- package/scripts/release-loop/release-readiness.ts +58 -0
- package/scripts/release-loop/shard-plan.ts +17 -0
- package/scripts/release-loop/unattended-release.ts +93 -0
- package/scripts/storyboard/storyboard.ts +167 -0
- package/scripts/test-diet/lib.ts +88 -0
- package/scripts/test-diet/node.ts +102 -0
- package/scripts/ux/measure-first-text.ts +102 -0
- package/scripts/vflow-collect.ts +20 -7
- package/scripts/vflow-search.ts +73 -0
- package/scripts/video-free-line-test-walker.ts +36 -0
- package/scripts/video-pipeline.ts +2 -2
- package/skills/explainer-video/SKILL.md +12 -9
- package/skills/explainer-video/engine/build.mjs +1 -1
- package/skills/explainer-video/engine/reel.mjs +138 -31
- package/skills/explainer-video/engine/reel.sh +2 -1
- package/skills/explainer-video/examples/elanous-0.2.5/script.json +1 -1
- package/skills/grill-me/SKILL.md +1 -1
- package/skills/omni-digest/docs/diagrams/noncode-terminal-promotion-path-2026-08-13.mmd +1 -1
- package/src/a2a/server.ts +2 -0
- package/src/acp/auth-methods.ts +30 -0
- package/src/acp/elanous-aware-peers.ts +30 -0
- package/src/acp/server.ts +96 -20
- package/src/agent/monad-agent-turn.ts +25 -6
- package/src/agent/shared-app-tools.ts +5 -3
- package/src/agent-substrate/review-intent.ts +6 -2
- package/src/agent-substrate/shard-boundary-intent.ts +70 -0
- package/src/auth/github-app-token.ts +4 -1
- package/src/autopilot/track-agent-shadow.ts +151 -0
- package/src/autopilot/track-agent.ts +23 -3
- package/src/boot/acp-server-help.ts +16 -0
- package/src/boot/daemon-prompt-turn.ts +31 -10
- package/src/boot/daemon-tools/index.ts +7 -1
- package/src/budget/codex-credit-pace.ts +84 -0
- package/src/budget/codex-credit-plan.ts +154 -0
- package/src/budget/codex-reset-credit-state.ts +2 -0
- package/src/channel-import/detect.ts +191 -0
- package/src/channel-import/run.ts +111 -0
- package/src/chat/chat-cli.ts +4 -1
- package/src/chat/short-question.ts +40 -0
- package/src/cli/agent-cli.ts +24 -15
- package/src/cli/ask-bare.ts +42 -0
- package/src/cli/cli-user-error.ts +25 -0
- package/src/cli/codex-credit-plan-view.ts +25 -0
- package/src/cli/config-test-sync.ts +4 -0
- package/src/cli/decisions-cli.ts +13 -3
- package/src/cli/dependence-gauge.ts +237 -0
- package/src/cli/doctor-bundle.ts +128 -0
- package/src/cli/doctor-cli.ts +41 -1
- package/src/cli/doctor-local-llm.ts +95 -0
- package/src/cli/doctor-modes.ts +48 -0
- package/src/cli/import-cli.ts +60 -0
- package/src/cli/intake-cli.ts +12 -2
- package/src/cli/intake-source-cli.ts +58 -0
- package/src/cli/machine-cli.ts +23 -0
- package/src/cli/mcp-login.ts +13 -4
- package/src/cli/msg-cli.ts +137 -0
- package/src/cli/persona-cli.ts +116 -0
- package/src/cli/plugin-cli.ts +25 -0
- package/src/cli/pr-cli.ts +36 -0
- package/src/cli/provider-cli.ts +5 -0
- package/src/cli/release-cli.ts +50 -8
- package/src/cli/release-run-if-ready.ts +48 -0
- package/src/cli/setup-cli.ts +27 -6
- package/src/cli/setup-recommend.ts +8 -0
- package/src/cli/skills-cli.ts +173 -0
- package/src/cli/start-cli.ts +13 -4
- package/src/cli/usage-cli.ts +11 -1
- package/src/codex/setup.ts +37 -16
- package/src/dashboard/index.ts +18 -2
- package/src/dashboard/slash-runtime/dashboard-handlers.ts +2 -2
- package/src/dashboard/slash-runtime/help-from-registry.ts +4 -2
- package/src/decisions/decision-cards.ts +291 -0
- package/src/decisions/decision-ledger.ts +5 -2
- package/src/decisions/discord-decision-cards.ts +133 -0
- package/src/decisions/telegram-decision-cards.ts +100 -0
- package/src/directives/version-at.ts +77 -0
- package/src/discord.ts +73 -6
- package/src/domains/core-tools.ts +37 -14
- package/src/domains/outbound-alert.ts +94 -12
- package/src/domains/repeated-failure.ts +1 -1
- package/src/error-report/report.ts +200 -0
- package/src/exec-requests/planner.ts +146 -0
- package/src/exec-requests/runner.ts +378 -0
- package/src/exec-requests/store.ts +78 -0
- package/src/field/field-feed-auto.ts +48 -0
- package/src/field/field-media.ts +8 -3
- package/src/field/field-reel.ts +203 -0
- package/src/grok/credential.ts +7 -5
- package/src/harness/aside-run-status.ts +33 -0
- package/src/harness/browser-verify.ts +48 -17
- package/src/harness/harness-cli-command.ts +25 -3
- package/src/harness/image-load-state.ts +21 -0
- package/src/harness/process-garbage.ts +45 -0
- package/src/hooks/error-report.ts +210 -0
- package/src/hooks/receiver.ts +41 -1
- package/src/import/apply.ts +128 -0
- package/src/import/plan.ts +197 -0
- package/src/index.ts +168 -21
- package/src/input-core/text-entry.ts +13 -5
- package/src/instance/leader.ts +13 -1
- package/src/intake-plane/collect-github.ts +5 -3
- package/src/intake-plane/collect-telegram-saved.ts +15 -3
- package/src/intake-plane/discord-seat-work.ts +56 -0
- package/src/intake-plane/intake-sources.ts +202 -0
- package/src/intake-plane/items.ts +28 -12
- package/src/intake-plane/seat-answer.ts +96 -0
- package/src/intake-plane/seat-doc-route.ts +221 -0
- package/src/intake-plane/submit-intake-work.ts +9 -1
- package/src/intake-plane/telegram-seat-work.ts +72 -0
- package/src/llm/model-defaults.ts +24 -1
- package/src/llm.ts +5 -4
- package/src/machines/machine-ledger.ts +127 -0
- package/src/market/pack-internal-refs.ts +28 -0
- package/src/market/publish.ts +31 -4
- package/src/maturity/feature-maturity.ts +65 -0
- package/src/model-tier/turn-tier.ts +24 -0
- package/src/msg/msg-store.ts +149 -0
- package/src/native-tool-catalog.ts +2 -1
- package/src/nexus/api/chat-fast-path-config.ts +28 -0
- package/src/nexus/api/consult-requests.ts +128 -0
- package/src/nexus/api/discord-trigger-bot.ts +3 -0
- package/src/nexus/api/error-reports.ts +108 -0
- package/src/nexus/api/exec-requests.ts +51 -0
- package/src/nexus/api/field-uploads.ts +40 -3
- package/src/nexus/api/graph-approvals-feed.ts +140 -0
- package/src/nexus/api/graph-approvals.ts +71 -4
- package/src/nexus/api/http-server.ts +82 -8
- package/src/nexus/api/outbound-report.ts +7 -0
- package/src/nexus/api/running-runs-background.ts +68 -0
- package/src/nexus/api/running-runs-child.ts +6 -0
- package/src/nexus/api/seat-requests.ts +254 -0
- package/src/nexus/api/share-captures.ts +91 -0
- package/src/nexus/api/skill-problems.ts +56 -0
- package/src/nexus/api/terminals.ts +24 -8
- package/src/nexus/api/trace-run-scan.ts +37 -0
- package/src/nexus/api/trace.ts +19 -3
- package/src/nexus/index.ts +28 -1
- package/src/nexus/setup-status.ts +11 -7
- package/src/notify/llm-budget-alert.ts +16 -10
- package/src/oauth/browser-availability.ts +14 -0
- package/src/oauth/codex-account-rotation.ts +26 -4
- package/src/oauth/codex-account-store.ts +20 -2
- package/src/oauth/codex-browser-login.ts +173 -0
- package/src/oauth/codex-quota-policy.ts +16 -0
- package/src/oauth/codex.ts +4 -1
- package/src/oauth/fallback-chain.ts +5 -5
- package/src/oauth/store.ts +42 -5
- package/src/onboarding/continue-here.ts +34 -0
- package/src/onboarding/entry-hints.ts +8 -0
- package/src/onboarding/full-screen-io.ts +35 -3
- package/src/onboarding/help/skills.md +1 -1
- package/src/onboarding/non-interactive.ts +1 -0
- package/src/onboarding/subscription-hint.ts +27 -0
- package/src/onboarding/web-first.ts +90 -0
- package/src/onboarding.ts +287 -26
- package/src/persona/presets.ts +105 -0
- package/src/plugins/maker/node-install.ts +92 -0
- package/src/plugins/maker/node-maker.ts +139 -0
- package/src/plugins/maker/plugin-maker.ts +19 -7
- package/src/policy/llm-policy.ts +268 -0
- package/src/prompt-library/session-guidance.ts +7 -1
- package/src/prompt-library/universal-preamble.ts +2 -2
- package/src/relay/relay-cli.ts +20 -0
- package/src/relay/relay-server.ts +220 -0
- package/src/release-loop/checklist.ts +37 -13
- package/src/self-dev/decompose.ts +1 -0
- package/src/self-dev/dev-pipeline.ts +6 -3
- package/src/self-dev/orchestrate.ts +18 -11
- package/src/self-dev/run-record-mirror.ts +44 -0
- package/src/self-dev/run-store.ts +12 -3
- package/src/self-implement/harness-policy-gates.ts +4 -1
- package/src/self-implement/report-origin.ts +12 -1
- package/src/session/chat.ts +17 -5
- package/src/shell-env-bootstrap.ts +44 -7
- package/src/skills/connect-claude-plugin.ts +142 -0
- package/src/skills/connect.ts +70 -0
- package/src/skills/index.ts +29 -1
- package/src/skills/repair.ts +124 -0
- package/src/skills/runner.ts +11 -2
- package/src/skills/shared-agent-skill-roots.ts +25 -0
- package/src/skills/trigger-extract.ts +1 -1
- package/src/steward/failure-streak.ts +78 -0
- package/src/steward/steward-cards.ts +24 -0
- package/src/steward/triage-budget.ts +127 -0
- package/src/steward/triage-plan.ts +75 -0
- package/src/steward/triage.ts +244 -42
- package/src/task-cards/card-store.ts +1 -1
- package/src/task-orchestrator/surfaces/pod-command-job.ts +16 -8
- package/src/task-orchestrator/surfaces/pod-ledger-collect.ts +11 -5
- package/src/task-orchestrator/surfaces/pod-ledger-events.ts +68 -0
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +78 -4
- package/src/telegram-commands.ts +13 -0
- package/src/telegram.ts +72 -2
- package/src/tool-runtime/elanous-skills-list-runtime.ts +3 -3
- package/src/tool-runtime/self-cognition-runtimes.ts +160 -2
- package/src/user-config.ts +189 -15
- package/src/version/packed-revision.json +1 -1
- package/src/video-pipeline/recipes/character.ts +42 -5
- package/src/video-pipeline/recipes/ffmpeg.ts +1 -1
- package/src/video-pipeline/recipes/free-line.ts +16 -3
- package/src/video-pipeline/recipes/remote.ts +7 -1
- package/src/video-pipeline/vflow-reference.ts +109 -0
- package/apps/pwa/out/_next/static/chunks/1169-696263ac7af8c4d4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1883.c91a2648af4842fd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1947-9d64872eeb9cfdec.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2808-dc06482994b40f70.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3165-95515ca5f299a120.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3190-e9282753e7ebde85.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4259-99614149577e77cb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4283-329eabeaf233e6cd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4328-cb5058b1acc2de5e.js +0 -25
- package/apps/pwa/out/_next/static/chunks/4801-ecee6ef62b349b18.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5110-8058791dd25a2e14.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5330-c41e9670f594c38f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5423-e95fd125d1794349.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5478-b5eae07dc79ea619.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5787-96ee6642b9a661e5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6448-79d72368589f98bc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6522-9866c002b5531836.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6729-b4a558b7aa891e0c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6969-cb03f9410b7d2e03.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7007-3b9861091bb8dd21.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7930-8dc7e20fc6fc19ca.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8071-187edbc22a86741f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8198-d4101ad70cb9a579.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8250.4a2c5553a07cdf47.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8293-123b91e7b0330dc0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8296-d6d5d87dfa911d41.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8305-603963c33ee8eec3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8612-4a0066f20951b8fd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9181-cf4e1b61c4783f92.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9262-26c2ecbd5a67e4a2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9328-45abdebce4fbecb7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9355-dbc67205adf8745f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9763.255b90dd69875689.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9933-66d31ec25c0231af.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-bd06fb55f4da0381.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-4e586142da455a25.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-889ad5cddcf45b0d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-47ba401d56572c27.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-0b54f9c83458e038.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-4fcc296ef6c8ed90.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/chat/page-fe9fda494a87f4e5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/control/page-f9642e25ce129590.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-e1063e8ba5f3e2e7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-9d0df30027d8aa9d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-bd06fb55f4da0381.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/intake/page-d97adf2c9455b8d0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-236b80d202110362.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-21a30f35f1a276ad.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/market/page-b1a5be357c4fa25d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-1705a60303f7f573.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/morning/page-76b337c11ad0e237.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-7c0f85fbb56f64c6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-65ca16b20617b44d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-e7f9b8fc29411c1d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-92ea6213d1aeec04.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-a913a005aacb5476.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-2d78804a40dfdef6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-08a99148e780ac00.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/page-deb674e7ed36f815.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-49f8de0534d192d7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-bd06fb55f4da0381.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/page-ba162a28d2176e18.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/share/page-1ff1a37b550ab41a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-ccaa698f56c0fbdb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-53b944082de1d7db.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/term/page-9b70adf1552789bf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-30d482aa63241fe8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-aecf786f54651d8a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-6ab9b62c7b6e08f2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-08949735455e0c9a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-a1479005234a46c2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-c3890e90bd232732.js +0 -1
- package/apps/pwa/out/_next/static/chunks/beb5cf10-9adcc10b83930090.js +0 -1
- package/apps/pwa/out/_next/static/chunks/framework-d68d41c88e05c801.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-app-11c63971b7a9593b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-d09452353bd0d9cc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_app-184d727cebeca767.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_error-21bbbbac99b720b1.js +0 -1
- package/apps/pwa/out/_next/static/css/eaa74e8bb2f62269.css +0 -1
- package/apps/pwa/out/_next/static/qMI0jnlZaBjjm4aIDYNGM/_buildManifest.js +0 -1
- /package/apps/pwa/out/_next/static/{qMI0jnlZaBjjm4aIDYNGM → lywc8m_HDzH9MKSDQhyEj}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import type { Command } from 'commander';
|
|
2
|
+
import { canonicalSeatId, openMsgStore, type Message, type MsgStore } from '../msg/msg-store.js';
|
|
3
|
+
|
|
4
|
+
export interface MsgCliDeps {
|
|
5
|
+
openStore?: () => MsgStore;
|
|
6
|
+
out?: Pick<Console, 'log' | 'error'>;
|
|
7
|
+
readStdin?: () => Promise<string>;
|
|
8
|
+
sleep?: (ms: number) => Promise<void>;
|
|
9
|
+
setExitCode?: (code: number) => void;
|
|
10
|
+
signal?: AbortSignal;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
type CommonOptions = { json?: boolean };
|
|
14
|
+
type RecipientOptions = CommonOptions & { to?: string; after?: string; limit?: string };
|
|
15
|
+
|
|
16
|
+
function seat(raw: string | undefined, flag: string): string {
|
|
17
|
+
if (raw === undefined) throw new Error(`${flag} is required`);
|
|
18
|
+
return canonicalSeatId(raw);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function integer(raw: string | undefined, flag: string, fallback: number, min: number, max: number): number {
|
|
22
|
+
if (raw === undefined) return fallback;
|
|
23
|
+
if (!/^(0|[1-9]\d*)$/.test(raw)) throw new Error(`${flag} must be an integer between ${min} and ${max}`);
|
|
24
|
+
const value = Number(raw);
|
|
25
|
+
if (!Number.isSafeInteger(value) || value < min || value > max) throw new Error(`${flag} must be an integer between ${min} and ${max}`);
|
|
26
|
+
return value;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function formatMessage(message: Message): string {
|
|
30
|
+
return `#${message.id} ${message.createdAt} ${message.from} → ${message.to}${message.kind ? ` [${message.kind}]` : ''}: ${message.body}`;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Register the durable, recipient-addressed CLI. Reading never advances the acknowledgement cursor. */
|
|
34
|
+
export function registerMsgCommands(program: Command, deps: MsgCliDeps = {}): void {
|
|
35
|
+
const open = deps.openStore ?? (() => openMsgStore());
|
|
36
|
+
const out = deps.out ?? console;
|
|
37
|
+
const readStdin = deps.readStdin ?? (() => Bun.stdin.text());
|
|
38
|
+
const sleep = deps.sleep ?? ((ms: number) => new Promise<void>(resolve => setTimeout(resolve, ms)));
|
|
39
|
+
const setExitCode = deps.setExitCode ?? ((code: number) => { process.exitCode = code; });
|
|
40
|
+
const msg = program.command('msg').description('Durable seat-addressed messages');
|
|
41
|
+
|
|
42
|
+
const run = (action: () => void | Promise<void>) => async () => {
|
|
43
|
+
try { await action(); }
|
|
44
|
+
catch (error) {
|
|
45
|
+
out.error(`msg: ${error instanceof Error ? error.message : String(error)}`);
|
|
46
|
+
setExitCode(2);
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
const withStore = <T>(action: (store: MsgStore) => T): T => {
|
|
50
|
+
const store = open();
|
|
51
|
+
try { return action(store); }
|
|
52
|
+
finally { store.close(); }
|
|
53
|
+
};
|
|
54
|
+
const emit = (value: unknown, json: boolean | undefined, human: () => void) => {
|
|
55
|
+
if (json) out.log(JSON.stringify(value));
|
|
56
|
+
else human();
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
msg.command('post [body]').description('Post a message; use - or omit body to read stdin')
|
|
60
|
+
.option('--from <seat>').option('--to <seat>').option('--kind <kind>').option('--body <text>').option('--json')
|
|
61
|
+
.action((body: string | undefined, opts: CommonOptions & { from?: string; to?: string; kind?: string; body?: string }) => run(async () => {
|
|
62
|
+
const from = seat(opts.from, '--from');
|
|
63
|
+
const to = seat(opts.to, '--to');
|
|
64
|
+
if (body !== undefined && body !== '-' && opts.body !== undefined) throw new Error('provide body once (argument or --body)');
|
|
65
|
+
const text = opts.body ?? (body !== undefined && body !== '-' ? body : await readStdin());
|
|
66
|
+
const posted = withStore(store => store.post({ from, to, body: text, ...(opts.kind === undefined ? {} : { kind: opts.kind }) }));
|
|
67
|
+
emit(posted, opts.json, () => out.log(formatMessage(posted)));
|
|
68
|
+
})());
|
|
69
|
+
|
|
70
|
+
msg.command('list').description('List messages for one recipient without acknowledging them')
|
|
71
|
+
.option('--to <seat>').option('--after <id>', 'exclusive message ID (default: acknowledged cursor)')
|
|
72
|
+
.option('--limit <count>', 'maximum messages (1–1000)').option('--json')
|
|
73
|
+
.action((opts: RecipientOptions) => run(() => {
|
|
74
|
+
const to = seat(opts.to, '--to');
|
|
75
|
+
const limit = integer(opts.limit, '--limit', 100, 1, 1000);
|
|
76
|
+
const after = opts.after === undefined ? undefined : integer(opts.after, '--after', 0, 0, Number.MAX_SAFE_INTEGER);
|
|
77
|
+
const messages = withStore(store => store.list(to, after ?? store.getCursor(to), limit));
|
|
78
|
+
emit(messages, opts.json, () => {
|
|
79
|
+
if (!messages.length) out.log('No messages.');
|
|
80
|
+
for (const message of messages) out.log(formatMessage(message));
|
|
81
|
+
});
|
|
82
|
+
})());
|
|
83
|
+
|
|
84
|
+
msg.command('ack [id]').description('Explicitly acknowledge messages through this ID for one recipient')
|
|
85
|
+
.option('--to <seat>').option('--json')
|
|
86
|
+
.action((id: string | undefined, opts: CommonOptions & { to?: string }) => run(() => {
|
|
87
|
+
const to = seat(opts.to, '--to');
|
|
88
|
+
if (id === undefined) throw new Error('message ID is required');
|
|
89
|
+
const cursor = integer(id, 'message ID', 0, 0, Number.MAX_SAFE_INTEGER);
|
|
90
|
+
const acknowledged = withStore(store => store.ack(to, cursor));
|
|
91
|
+
emit({ recipient: to, cursor: acknowledged }, opts.json, () => out.log(`${to} acknowledged through #${acknowledged}`));
|
|
92
|
+
})());
|
|
93
|
+
|
|
94
|
+
msg.command('unread').description('Show recipients with unacknowledged messages')
|
|
95
|
+
.option('--to <seat>').option('--json')
|
|
96
|
+
.action((opts: CommonOptions & { to?: string }) => run(() => {
|
|
97
|
+
const to = opts.to === undefined ? undefined : seat(opts.to, '--to');
|
|
98
|
+
const rows = withStore(store => store.unread().filter(row => to === undefined || row.recipient === to));
|
|
99
|
+
emit(rows, opts.json, () => {
|
|
100
|
+
if (!rows.length) out.log('No unread messages.');
|
|
101
|
+
for (const row of rows) out.log(`${row.recipient}: ${row.count} unread`);
|
|
102
|
+
});
|
|
103
|
+
})());
|
|
104
|
+
|
|
105
|
+
msg.command('watch').description('Poll for new messages without acknowledging them (Ctrl-C to stop)')
|
|
106
|
+
.option('--to <seat>').option('--after <id>', 'exclusive message ID (default: acknowledged cursor)')
|
|
107
|
+
.option('--interval <ms>', 'poll interval in milliseconds', '1000')
|
|
108
|
+
.option('--once', 'poll once and exit').option('--json')
|
|
109
|
+
.action((opts: RecipientOptions & { interval?: string; once?: boolean }) => run(async () => {
|
|
110
|
+
const to = seat(opts.to, '--to');
|
|
111
|
+
const interval = integer(opts.interval, '--interval', 1000, 1, 60000);
|
|
112
|
+
const after = opts.after === undefined ? undefined : integer(opts.after, '--after', 0, 0, Number.MAX_SAFE_INTEGER);
|
|
113
|
+
const store = open();
|
|
114
|
+
try {
|
|
115
|
+
let cursor = after ?? store.getCursor(to);
|
|
116
|
+
do {
|
|
117
|
+
let messages: Message[];
|
|
118
|
+
do {
|
|
119
|
+
messages = store.list(to, cursor, 1000);
|
|
120
|
+
for (const message of messages) {
|
|
121
|
+
emit(message, opts.json, () => out.log(formatMessage(message)));
|
|
122
|
+
cursor = message.id;
|
|
123
|
+
}
|
|
124
|
+
} while (messages.length === 1000 && !deps.signal?.aborted);
|
|
125
|
+
if (opts.once || deps.signal?.aborted) break;
|
|
126
|
+
await sleep(interval);
|
|
127
|
+
} while (!deps.signal?.aborted);
|
|
128
|
+
} finally { store.close(); }
|
|
129
|
+
})());
|
|
130
|
+
|
|
131
|
+
// Commander reports option/argument errors before action handlers run. Scope their exit code
|
|
132
|
+
// to msg so unrelated commands keep their own error and help behavior.
|
|
133
|
+
for (const command of [msg, ...msg.commands]) {
|
|
134
|
+
const reportError = command.error.bind(command);
|
|
135
|
+
command.error = (message, options) => reportError(message, { ...options, exitCode: 2 });
|
|
136
|
+
}
|
|
137
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
// PS1 — `elanous persona list|add|show`: 프리셋(persona-presets/)을 보고, 골라서 «내 페르소나»를 만들고, 만든 것을 본다.
|
|
2
|
+
// 만든 페르소나는 기존 페르소나 저장소(resolveStatePersonaDir · Discord·PWA 가 읽는 곳)의 `<id>.yaml` 한 파일이다.
|
|
3
|
+
import type { Command } from 'commander';
|
|
4
|
+
import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
5
|
+
import { join } from 'node:path';
|
|
6
|
+
import { parse as parseYaml } from 'yaml';
|
|
7
|
+
import { debug } from '../debug/log.js';
|
|
8
|
+
import { parsePersonaYaml } from '../persona/loader.js';
|
|
9
|
+
import { resolveRepositoryPersonaDir, resolveStatePersonaDir } from '../persona/global-registry.js';
|
|
10
|
+
import { choosePersonaId, describePreset, findPreset, loadPresetIndex, loadPresets, presetToProfileYaml } from '../persona/presets.js';
|
|
11
|
+
|
|
12
|
+
interface StoredPersona { personaId: string; displayName: string; description?: string; preset?: string; title?: string; path: string }
|
|
13
|
+
|
|
14
|
+
/** Personas the person made (state dir), parsed with the same loader the runtime uses. */
|
|
15
|
+
export function readStoredPersonas(dir = resolveStatePersonaDir()): StoredPersona[] {
|
|
16
|
+
if (!existsSync(dir)) return [];
|
|
17
|
+
const out: StoredPersona[] = [];
|
|
18
|
+
for (const file of readdirSync(dir).filter((f) => f.endsWith('.yaml') && !f.startsWith('_')).sort()) {
|
|
19
|
+
const path = join(dir, file);
|
|
20
|
+
const text = readFileSync(path, 'utf8');
|
|
21
|
+
const parsed = parsePersonaYaml(text, path);
|
|
22
|
+
if (!parsed.ok) continue;
|
|
23
|
+
const origin = (parseYaml(text) as { preset?: { id?: unknown; title?: unknown } } | null)?.preset;
|
|
24
|
+
const preset = typeof origin?.id === 'string' ? origin.id : undefined;
|
|
25
|
+
const title = typeof origin?.title === 'string' ? origin.title : undefined;
|
|
26
|
+
out.push({ personaId: parsed.profile.personaId, displayName: parsed.profile.displayName, ...(parsed.profile.description ? { description: parsed.profile.description } : {}), ...(preset ? { preset } : {}), ...(title ? { title } : {}), path });
|
|
27
|
+
}
|
|
28
|
+
return out.sort((a, b) => a.personaId.localeCompare(b.personaId));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function takenIds(stateDir: string): Set<string> {
|
|
32
|
+
const ids = new Set(readStoredPersonas(stateDir).map((p) => p.personaId));
|
|
33
|
+
const repo = resolveRepositoryPersonaDir();
|
|
34
|
+
if (existsSync(repo)) for (const f of readdirSync(repo)) if (f.endsWith('.yaml')) ids.add(f.slice(0, -5));
|
|
35
|
+
if (existsSync(stateDir)) for (const f of readdirSync(stateDir)) if (f.endsWith('.yaml')) ids.add(f.slice(0, -5));
|
|
36
|
+
return ids;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function registerPersonaCommands(program: Command): void {
|
|
40
|
+
const persona = program.command('persona').description('Persona presets — AI coworkers you pick and customise (list · add · show)');
|
|
41
|
+
|
|
42
|
+
persona.command('list')
|
|
43
|
+
.description('List the persona presets, then the personas you have made')
|
|
44
|
+
.option('--json', 'JSON output')
|
|
45
|
+
.action((opts: { json?: boolean }) => {
|
|
46
|
+
const presets = loadPresets();
|
|
47
|
+
const mine = readStoredPersonas();
|
|
48
|
+
debug.log('persona.cli', 'list', { presets: presets.length, mine: mine.length });
|
|
49
|
+
if (opts.json) { console.log(JSON.stringify({ presets: presets.map((p) => ({ id: p.personaId, displayName: p.displayName, role: p.role, oneLine: p.oneLine })), mine }, null, 2)); return; }
|
|
50
|
+
if (!presets.length) console.log('페르소나 프리셋을 찾지 못했습니다(persona-presets/ 가 이 설치본에 없습니다).');
|
|
51
|
+
else {
|
|
52
|
+
console.log('프리셋 — 골라서 내 페르소나로 만들기: elanous persona add <프리셋> [--name <이름>]');
|
|
53
|
+
const width = Math.max(...presets.map((p) => p.personaId.length));
|
|
54
|
+
for (const p of presets) console.log(` ${p.personaId.padEnd(width)} ${p.displayName} — ${p.role} · ${p.oneLine}`);
|
|
55
|
+
}
|
|
56
|
+
const made = mine.filter((p) => p.preset);
|
|
57
|
+
if (made.length) {
|
|
58
|
+
console.log('\n내 페르소나:');
|
|
59
|
+
for (const p of made) console.log(` ${p.personaId} ${p.displayName}${p.title ? ` (${p.title})` : ''} (프리셋 ${p.preset})`);
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
persona.command('add <preset>')
|
|
64
|
+
.description('Make your own persona from a preset (id, name or role) — the preset file is not changed')
|
|
65
|
+
.option('--name <name>', 'What to call it (default: the preset’s first name)')
|
|
66
|
+
.option('--title <title>', 'Optional title, e.g. CMO')
|
|
67
|
+
.option('--json', 'JSON output')
|
|
68
|
+
.action((query: string, opts: { name?: string; title?: string; json?: boolean }) => {
|
|
69
|
+
const presets = loadPresets();
|
|
70
|
+
const preset = findPreset(presets, query);
|
|
71
|
+
if (!preset) {
|
|
72
|
+
console.error(`프리셋 «${query}» 을 찾지 못했습니다. 있는 것: ${presets.map((p) => p.personaId).join(', ')}`);
|
|
73
|
+
process.exitCode = 1;
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
const dir = resolveStatePersonaDir();
|
|
77
|
+
const personaId = choosePersonaId(preset, opts.name, takenIds(dir));
|
|
78
|
+
const yaml = presetToProfileYaml(preset, loadPresetIndex().common, { personaId, ...(opts.name ? { name: opts.name } : {}), ...(opts.title ? { title: opts.title } : {}) });
|
|
79
|
+
const path = join(dir, `${personaId}.yaml`);
|
|
80
|
+
// The runtime loader must accept what we write — check before it lands on disk.
|
|
81
|
+
const check = parsePersonaYaml(yaml, path);
|
|
82
|
+
if (!check.ok) { console.error(`만든 페르소나를 읽을 수 없습니다: ${check.error.message}`); process.exitCode = 1; return; }
|
|
83
|
+
mkdirSync(dir, { recursive: true });
|
|
84
|
+
writeFileSync(path, yaml, { flag: 'wx' });
|
|
85
|
+
debug.log('persona.cli', 'added', { preset: preset.personaId, personaId, named: Boolean(opts.name) });
|
|
86
|
+
if (opts.json) { console.log(JSON.stringify({ personaId, displayName: check.profile.displayName, preset: preset.personaId, path }, null, 2)); return; }
|
|
87
|
+
console.log(`만들었습니다 — ${check.profile.displayName} (id ${personaId} · 프리셋 ${preset.personaId})`);
|
|
88
|
+
console.log(` 처음 묻는 것: ${preset.firstQuestions.join(' / ')}`);
|
|
89
|
+
console.log(` 보기: elanous persona show ${personaId}`);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
persona.command('show <name>')
|
|
93
|
+
.description('Show one of your personas (id or name), or a preset')
|
|
94
|
+
.option('--json', 'JSON output')
|
|
95
|
+
.action((query: string, opts: { json?: boolean }) => {
|
|
96
|
+
const q = query.trim().toLowerCase();
|
|
97
|
+
const mine = readStoredPersonas().find((p) => p.personaId === q || p.displayName.toLowerCase().startsWith(q));
|
|
98
|
+
const presets = loadPresets();
|
|
99
|
+
if (mine) {
|
|
100
|
+
const text = readFileSync(mine.path, 'utf8');
|
|
101
|
+
const parsed = parsePersonaYaml(text, mine.path);
|
|
102
|
+
if (opts.json) { console.log(JSON.stringify({ kind: 'persona', ...mine, systemPrompt: parsed.ok ? parsed.profile.systemPrompt : undefined }, null, 2)); return; }
|
|
103
|
+
console.log(`${mine.displayName}${mine.title ? ` (${mine.title})` : ''} (id ${mine.personaId}${mine.preset ? ` · 프리셋 ${mine.preset}` : ''})`);
|
|
104
|
+
if (mine.description) console.log(` ${mine.description}`);
|
|
105
|
+
const preset = mine.preset ? presets.find((p) => p.personaId === mine.preset) : undefined;
|
|
106
|
+
if (preset) for (const line of describePreset(preset).slice(2)) console.log(line);
|
|
107
|
+
console.log(` 파일: ${mine.path}`);
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
const preset = findPreset(presets, query);
|
|
111
|
+
if (!preset) { console.error(`«${query}» 페르소나도 프리셋도 없습니다. 목록: elanous persona list`); process.exitCode = 1; return; }
|
|
112
|
+
if (opts.json) { console.log(JSON.stringify({ kind: 'preset', ...preset }, null, 2)); return; }
|
|
113
|
+
console.log('(프리셋 — 아직 만들지 않았습니다. 만들기: elanous persona add ' + preset.personaId + ')');
|
|
114
|
+
for (const line of describePreset(preset)) console.log(line);
|
|
115
|
+
});
|
|
116
|
+
}
|
package/src/cli/plugin-cli.ts
CHANGED
|
@@ -12,6 +12,7 @@ import { OFFICIAL_INDEX_KEYS } from '../market/official-keys.js';
|
|
|
12
12
|
import { installPlugin, listInstalledPlugins, PluginInstallError, removePlugin, type InstallEvent } from '../plugins/install/plugin-install.js';
|
|
13
13
|
import { credentialStatus, setPluginCredentials } from '../plugins/install/plugin-credentials.js';
|
|
14
14
|
import { makePlugin } from '../plugins/maker/plugin-maker.js';
|
|
15
|
+
import { addAndInstallNode } from '../plugins/maker/node-install.js';
|
|
15
16
|
|
|
16
17
|
function keys(): ReadonlyArray<{ keyId: string; publicKey: string }> {
|
|
17
18
|
const configPath = userConfigPath();
|
|
@@ -83,6 +84,30 @@ export function registerPluginCommands(program: Command): void {
|
|
|
83
84
|
process.exitCode = 1;
|
|
84
85
|
}
|
|
85
86
|
});
|
|
87
|
+
plugin.command('node').description('Manage plugin workflow nodes')
|
|
88
|
+
.command('add <dir> <request>').description('Write, validate and install a workflow node in a plugin')
|
|
89
|
+
.option('--kind <slug>', 'Workflow node kind')
|
|
90
|
+
.option('--yes', 'Accept requested capabilities')
|
|
91
|
+
.option('--json', 'Print the result as one JSON line')
|
|
92
|
+
.action(async (dir: string, request: string, opts: { kind?: string; yes?: boolean; json?: boolean }) => {
|
|
93
|
+
try {
|
|
94
|
+
const result = await addAndInstallNode({ dir, request, kind: opts.kind, yes: opts.yes,
|
|
95
|
+
deps: { consent: capabilities => askConsent(capabilities, !!opts.json) } });
|
|
96
|
+
if (opts.json) stdout.write(JSON.stringify(result) + '\n');
|
|
97
|
+
else {
|
|
98
|
+
console.log(`작성 ${result.timings.write}ms · 검증 ${result.timings.validate}ms${result.timings.repair === undefined ? '' : ` · 수리 ${result.timings.repair}ms`}`);
|
|
99
|
+
console.log(`설치 ${result.plugin ? '완료' : result.status === 'failed' && result.timings.install === 0 ? '미실행' : '실패'} · ${result.timings.install}ms`);
|
|
100
|
+
console.log(`동기화 ${result.status === 'installed' ? '완료' : result.plugin ? '실패' : '미실행'} · ${result.timings.sync}ms`);
|
|
101
|
+
for (const error of result.errors) console.error(error);
|
|
102
|
+
}
|
|
103
|
+
if (result.status === 'failed') process.exitCode = 1;
|
|
104
|
+
} catch (error) {
|
|
105
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
106
|
+
if (opts.json) stdout.write(JSON.stringify({ status: 'failed', errors: [message] }) + '\n');
|
|
107
|
+
else console.error(`plugin node add failed: ${message}`);
|
|
108
|
+
process.exitCode = 1;
|
|
109
|
+
}
|
|
110
|
+
});
|
|
86
111
|
plugin.command('add <spec>').description('Install a local path, pinned git plugin or signed market plugin')
|
|
87
112
|
.option('--yes', 'Accept requested capabilities')
|
|
88
113
|
.option('--json', 'NDJSON progress events')
|
package/src/cli/pr-cli.ts
CHANGED
|
@@ -17,6 +17,7 @@ import { runMockModuleRestoreGate } from '../../scripts/ci-mock-module-restore-g
|
|
|
17
17
|
import { runModelHardcodeGate } from '../../scripts/ci-model-hardcode-gate.js';
|
|
18
18
|
import { runDaemonPortGate } from '../../scripts/ci-daemon-port-gate.js';
|
|
19
19
|
import { runPublicLeakGate } from '../../scripts/ci-public-leak-gate.js';
|
|
20
|
+
import { exportLeakCheck, type ExportLeakCheck } from '../../scripts/ci-public-export-leak-gate.js';
|
|
20
21
|
import { checkCommands, extractElanousCommands, type Finding as DocsCliFinding } from '../../scripts/docs-cli-check.js';
|
|
21
22
|
import { runTestInterferenceGate } from '../../scripts/ci-test-interference-gate.js';
|
|
22
23
|
import { isGoalDocumentFileName } from '../self-implement/goal-document.js';
|
|
@@ -68,6 +69,8 @@ export interface PrLandOpts {
|
|
|
68
69
|
excludeActiveRunFiles?: boolean;
|
|
69
70
|
excludeActiveRunSources?: boolean;
|
|
70
71
|
landReason?: string;
|
|
72
|
+
/** Land even though changed files would leak into the public export (the reason goes in --land-reason). */
|
|
73
|
+
allowPublicLeak?: boolean;
|
|
71
74
|
}
|
|
72
75
|
|
|
73
76
|
export interface PrGranularityOpts {
|
|
@@ -101,6 +104,8 @@ export interface PrLandDeps {
|
|
|
101
104
|
runDaemonPortGate?: (out: { log: (message: string) => void; error: (message: string) => void }) => boolean;
|
|
102
105
|
/** 공개 유출 래칫(경고 전용) — 반환 0 통과 · 1 늘었다 · 2 못 쟀다. */
|
|
103
106
|
runPublicLeakGate?: (changedFiles: readonly string[], out: { log: (message: string) => void; error: (message: string) => void }) => number;
|
|
107
|
+
/** Test seam — the post-transform export leak check on changed files. */
|
|
108
|
+
runExportLeakCheck?: (changedFiles: readonly string[], cwd: string) => ExportLeakCheck;
|
|
104
109
|
/** 공개 문서의 `elanous …` 호출 ↔ 실제 `--help` 대조(경고 전용) — 바뀐 공개 문서 경로를 받아 어긋남 목록을 돌려준다. */
|
|
105
110
|
runDocsCliCheck?: (docFiles: readonly string[]) => DocsCliFinding[];
|
|
106
111
|
/** 변경 시험 파일 간 간섭 검사 심(시험 주입용). */
|
|
@@ -269,6 +274,35 @@ function runPrLandPublicLeakGate(changedFiles: readonly string[], out: { log: (m
|
|
|
269
274
|
return runPublicLeakGate({ args: ['--changed-files', ...changedFiles], log: out.log, error: out.log });
|
|
270
275
|
}
|
|
271
276
|
|
|
277
|
+
/** The prepare check on this landing's changed files — scripts/ci-public-export-leak-gate.ts. */
|
|
278
|
+
export function runPrLandExportLeakCheck(changedFiles: readonly string[], cwd: string): ExportLeakCheck {
|
|
279
|
+
const top = runGitCommand(cwd, ['rev-parse', '--show-toplevel'], { encoding: 'utf-8' });
|
|
280
|
+
return exportLeakCheck(changedFiles, top.status === 0 && String(top.stdout ?? '').trim() ? String(top.stdout).trim() : cwd);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/** Blocks the landing when changed files would leak into the public export, unless allowPublicLeak. Returns false to stop. */
|
|
284
|
+
export function exportLeakBlock(
|
|
285
|
+
changedFiles: readonly string[] | undefined,
|
|
286
|
+
check: () => ExportLeakCheck,
|
|
287
|
+
allow: boolean,
|
|
288
|
+
out: { log: (message: string) => void; error: (message: string) => void },
|
|
289
|
+
): boolean {
|
|
290
|
+
if (!changedFiles || changedFiles.length === 0) return true;
|
|
291
|
+
let result: ExportLeakCheck;
|
|
292
|
+
try { result = check(); } catch (error) { result = { measured: false, hits: [], detail: error instanceof Error ? error.message : String(error) }; }
|
|
293
|
+
record('public-export-leak', result.measured ? result.hits.length === 0 || allow : true, { measured: result.measured, hits: result.hits.length, allowed: allow && result.hits.length > 0 });
|
|
294
|
+
if (!result.measured) { out.log(`⚠ public-export-leak: 못 쟀다(${result.detail ?? '?'}) — 착지는 막지 않는다. 확인: bun scripts/public-export.ts --leak-check --files <파일…>`); return true; }
|
|
295
|
+
if (result.hits.length === 0) { out.log('✓ public-export-leak: 바뀐 파일이 공개본에 사적 흔적을 싣지 않는다(prepare 와 같은 변환 뒤).'); return true; }
|
|
296
|
+
const write = allow ? out.log : out.error;
|
|
297
|
+
write(`${allow ? '⚠' : '✗'} public-export-leak: 바뀐 파일 ${result.hits.length}곳이 공개본에 사적 흔적을 싣는다 — 다음 release prepare 가 여기서 실패한다:`);
|
|
298
|
+
for (const h of result.hits.slice(0, 20)) write(` ${h.file}:${h.line} ${h.marker}`);
|
|
299
|
+
if (result.hits.length > 20) write(` … 외 ${result.hits.length - 20}곳`);
|
|
300
|
+
write(' 확인: bun scripts/public-export.ts --leak-check --all --files <파일…>');
|
|
301
|
+
if (allow) { out.log(' --allow-public-leak 로 넘긴다(이유는 --land-reason).'); return true; }
|
|
302
|
+
out.error(' 고치거나, 공개본과 무관한 것이 확실하면 --allow-public-leak --land-reason "<이유>" 로 넘긴다.');
|
|
303
|
+
return false;
|
|
304
|
+
}
|
|
305
|
+
|
|
272
306
|
export function publicLeakWarning(
|
|
273
307
|
changedFiles: readonly string[] | undefined,
|
|
274
308
|
gate: (changedFiles: readonly string[], out: { log: (message: string) => void; error: (message: string) => void }) => number,
|
|
@@ -1286,6 +1320,7 @@ export async function runPrLand(opts: PrLandOpts = {}, deps: PrLandDeps = {}): P
|
|
|
1286
1320
|
|
|
1287
1321
|
const changedPaths = currentChangePaths(run, cwd, staged, base, out);
|
|
1288
1322
|
publicLeakWarning(changedPaths, deps.runPublicLeakGate ?? runPrLandPublicLeakGate, out);
|
|
1323
|
+
if (!exportLeakBlock(changedPaths, () => (deps.runExportLeakCheck ?? runPrLandExportLeakCheck)(changedPaths ?? [], cwd), opts.allowPublicLeak === true, out)) return 1;
|
|
1289
1324
|
if (publicDocPaths(changedPaths, () => true).length > 0) {
|
|
1290
1325
|
const top = run('git', ['rev-parse', '--show-toplevel'], { cwd });
|
|
1291
1326
|
const docsRoot = top.ok && top.out.trim() ? top.out.trim() : cwd;
|
|
@@ -1549,6 +1584,7 @@ export function registerPrCommands(program: Command, deps: PrLandDeps = {}): voi
|
|
|
1549
1584
|
.option('--exclude-active-run-files', '도는 런과 겹친 파일을 이번 착지에서 제외')
|
|
1550
1585
|
.option('--exclude-active-run-sources', '⚠ 위험: --exclude-active-run-files 가 골 문서가 아닌 파일(소스)을 빼도 착지를 강행')
|
|
1551
1586
|
.option('--land-reason <text>', '권고를 보고도 지금 내는 이유(예: 남이 기다리는 차단 해제)')
|
|
1587
|
+
.option('--allow-public-leak', '바뀐 파일이 공개본에 사적 흔적을 실어도 착지(이유는 --land-reason)')
|
|
1552
1588
|
.action(async (opts: PrLandOpts) => {
|
|
1553
1589
|
// ⛔⭐⭐⭐ **관측 sink 를 먼저 건다** — 라이브 도그푸드가 잡은 결함(2026-08-03).
|
|
1554
1590
|
// 단위 테스트는 `debug.log` 가 **불렸다**를 단언하지만, standalone CLI 는 sink 를 등록하지
|
package/src/cli/provider-cli.ts
CHANGED
|
@@ -239,6 +239,9 @@ export function registerProviderCommands(program: Command): void {
|
|
|
239
239
|
// ⛔⭐ 「후보」는 판정기가 «자기 자신을 뺀» 것이다 — 표면이 현재 계정을 후보로 보여 주면
|
|
240
240
|
// ***있지도 않은 선택지를 말한다***(리뷰 must-fix). 판정기와 같은 기준(storeKey)으로 거른다.
|
|
241
241
|
const shownCandidates = s.candidates.filter((c) => c.storeKey !== s.current.storeKey);
|
|
242
|
+
const { codexCreditPlanFromDisk } = await import('./codex-credit-plan-view.js');
|
|
243
|
+
const { formatCodexCreditPlan } = await import('../budget/codex-credit-plan.js');
|
|
244
|
+
const creditPlan = codexCreditPlanFromDisk(now);
|
|
242
245
|
const ageMinOf = (home: string | undefined): number | null => {
|
|
243
246
|
if (!home) return null;
|
|
244
247
|
const at = home === s.currentHome ? s.currentObservedAt : s.observedAtByHome[home];
|
|
@@ -258,6 +261,7 @@ export function registerProviderCommands(program: Command): void {
|
|
|
258
261
|
if (opts.json) {
|
|
259
262
|
await writeStdoutJson(JSON.stringify({
|
|
260
263
|
policy: { value: s.policy.policy, source: s.policy.source },
|
|
264
|
+
creditPlan,
|
|
261
265
|
universe: {
|
|
262
266
|
instanceRoot, signalDir, authStore: authStorePath(),
|
|
263
267
|
// ⭐ JSON 에도 싣는다 — 화면만 알면 스크립트가 못 센다
|
|
@@ -303,6 +307,7 @@ export function registerProviderCommands(program: Command): void {
|
|
|
303
307
|
const policySource = s.policy.source === 'legacy-credits' ? ' · 옛 codexCreditsAllowed 에서'
|
|
304
308
|
: s.policy.source === 'default' ? ' · 기본값' : '';
|
|
305
309
|
console.log(`정책 ${s.policy.policy} (${CODEX_QUOTA_POLICY_LABEL[s.policy.policy]} · llm.codexQuotaPolicy)${policySource}`);
|
|
310
|
+
for (const line of formatCodexCreditPlan(creditPlan)) console.log(line);
|
|
306
311
|
console.log(`지금 계정 ${s.current.name} (source=${s.current.source})`);
|
|
307
312
|
console.log(` 홈=${s.currentHome ?? '(모름)'} 사용=${s.currentUsedPercent ?? '?'}% 찼나=${s.currentReached ?? '모름'} 신호=${ageOf(s.currentHome)} 크레딧=${creditText(s.currentCreditBalance, s.currentHasCredits)}`);
|
|
308
313
|
console.log('후보');
|
package/src/cli/release-cli.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
// elanous release tag --version <x.y.z> --source <commit> [--yes]
|
|
6
6
|
// elanous release verify [--version <x.y.z>]
|
|
7
7
|
// elanous release notes --from <ref> [--to <ref>]
|
|
8
|
+
// elanous release run --version <x.y.z> [--dry-run] [--json] [--if-ready]
|
|
8
9
|
//
|
|
9
10
|
// 🩸 계기(2026-09-25 v0.1.0 첫 공개): 손으로 밟은 절차에서 둘을 빠뜨릴 뻔했다 — 공개본 git 커밋(판 커밋이 공개본 커밋이 된다) ·
|
|
10
11
|
// PWA 빌드(빌드 산출은 공개본에 안 실린다 → 빠뜨리면 «웹 화면 없는 판»). 절차를 명령으로 굳힌다.
|
|
@@ -22,8 +23,11 @@ import { effectiveInstanceRoot, releaseLedgerRoot } from '../instance/resolve.js
|
|
|
22
23
|
import { getElanousConfigDirOverride } from '../elanous-config-dir.js';
|
|
23
24
|
import { envLiteral } from '../platform/env-literal.js';
|
|
24
25
|
import { userConfigPath } from '../user-config.js';
|
|
25
|
-
import { addItem, devVersion, listChecklist, removeItem, seedFromRoadmap, setItem, summarizeChecklist, type ChecklistStatus, type ChecklistDisposition } from '../release-loop/checklist.js';
|
|
26
|
+
import { addItem, devVersion, listChecklist, removeItem, seedFromRoadmap, setItem, summarizeChecklist, type ChecklistStatus, type ChecklistDisposition, type ChecklistKind } from '../release-loop/checklist.js';
|
|
26
27
|
import { writeStdoutJson } from './stdout-json.js';
|
|
28
|
+
import { runUnattendedRelease, type UnattendedReleaseDeps } from '../../scripts/release-loop/unattended-release.js';
|
|
29
|
+
import { releaseReadiness } from '../../scripts/release-loop/release-readiness.js';
|
|
30
|
+
import { runReleaseIfReady } from './release-run-if-ready.js';
|
|
27
31
|
|
|
28
32
|
export const DEFAULT_PUBLIC_REPO = 'ElanvitalAI/elanous';
|
|
29
33
|
const SEMVER = /^\d+\.\d+\.\d+(?:-(?:rc|alpha|beta)\.\d+)?$/;
|
|
@@ -482,7 +486,7 @@ async function checklistOutput(v: string, codenames: Record<string, string>, mod
|
|
|
482
486
|
}
|
|
483
487
|
}
|
|
484
488
|
|
|
485
|
-
export function registerReleaseCommands(program: Command): void {
|
|
489
|
+
export function registerReleaseCommands(program: Command, releaseRunDeps: UnattendedReleaseDeps = {}): void {
|
|
486
490
|
const release = program.command('release').description('공개 배포 한 판 — prepare(로컬) → publish(--yes) → verify (docs/manual/MANUAL-versioning-and-release-2026-09-25.md)');
|
|
487
491
|
const checklist = release.command('checklist').description('판별 확인표 조회·갱신')
|
|
488
492
|
.option('--version <v>', '판 또는 별칭(기본: package.json 의 개발판에서 -dev.N 제거)')
|
|
@@ -501,20 +505,21 @@ export function registerReleaseCommands(program: Command): void {
|
|
|
501
505
|
const { version, codenames, json } = context(cmd);
|
|
502
506
|
await checklistOutput(version, codenames, 'status', json);
|
|
503
507
|
});
|
|
504
|
-
withContext(checklist.command('add <id> <title>').description('칸 추가')).option('--owner <owner>', '담당').
|
|
508
|
+
withContext(checklist.command('add <id> <title>').description('칸 추가')).option('--owner <owner>', '담당').option('--kind <kind>', 'screen = 다섯 화면 짝 칸')
|
|
509
|
+
.action((id: string, title: string, opts: { owner?: string; kind?: string }, cmd: Command) => {
|
|
505
510
|
const { version, json } = context(cmd);
|
|
506
|
-
const data = addItem(version, { id, title, ...(opts.owner !== undefined ? { owner: opts.owner } : {}) });
|
|
511
|
+
const data = addItem(version, { id, title, ...(opts.owner !== undefined ? { owner: opts.owner } : {}), ...(opts.kind !== undefined ? { kind: opts.kind as ChecklistKind } : {}) });
|
|
507
512
|
if (json) console.log(JSON.stringify(data)); else console.log(`✅ ${id} 추가`);
|
|
508
513
|
});
|
|
509
514
|
withContext(checklist.command('set <id>').description('칸 상태·근거·담당·처분 갱신'))
|
|
510
515
|
.option('--status <status>', 'green|yellow|red|done').option('--evidence <evidence>', '근거').option('--owner <owner>', '담당')
|
|
511
|
-
.option('--disposition <disposition>', 'move|known-issue|block')
|
|
512
|
-
.action((id: string, opts: { status?: string; evidence?: string; owner?: string; disposition?: string }, cmd: Command) => {
|
|
516
|
+
.option('--disposition <disposition>', 'move|known-issue|block').option('--kind <kind>', 'screen = 다섯 화면 짝 칸')
|
|
517
|
+
.action((id: string, opts: { status?: string; evidence?: string; owner?: string; disposition?: string; kind?: string }, cmd: Command) => {
|
|
513
518
|
const { version, json } = context(cmd);
|
|
514
519
|
if (opts.status !== undefined && !['green', 'yellow', 'red', 'done'].includes(opts.status)) throw new Error(`잘못된 상태: ${opts.status}`);
|
|
515
520
|
if (opts.disposition !== undefined && !['move', 'known-issue', 'block'].includes(opts.disposition)) throw new Error(`잘못된 처분: ${opts.disposition}`);
|
|
516
|
-
if (opts.status === undefined && opts.evidence === undefined && opts.owner === undefined && opts.disposition === undefined) throw new Error('갱신할 칸을 지정하라');
|
|
517
|
-
const data = setItem(version, id, { ...(opts.status !== undefined ? { status: opts.status as ChecklistStatus } : {}), ...(opts.evidence !== undefined ? { evidence: opts.evidence } : {}), ...(opts.owner !== undefined ? { owner: opts.owner } : {}), ...(opts.disposition !== undefined ? { disposition: opts.disposition as ChecklistDisposition } : {}) }, process.env.ELANOUS_TRACK || 'cli');
|
|
521
|
+
if (opts.status === undefined && opts.evidence === undefined && opts.owner === undefined && opts.disposition === undefined && opts.kind === undefined) throw new Error('갱신할 칸을 지정하라');
|
|
522
|
+
const data = setItem(version, id, { ...(opts.status !== undefined ? { status: opts.status as ChecklistStatus } : {}), ...(opts.evidence !== undefined ? { evidence: opts.evidence } : {}), ...(opts.owner !== undefined ? { owner: opts.owner } : {}), ...(opts.disposition !== undefined ? { disposition: opts.disposition as ChecklistDisposition } : {}), ...(opts.kind !== undefined ? { kind: opts.kind as ChecklistKind } : {}) }, process.env.ELANOUS_TRACK || 'cli');
|
|
518
523
|
if (json) console.log(JSON.stringify(data)); else console.log(`✅ ${id} 갱신`);
|
|
519
524
|
});
|
|
520
525
|
withContext(checklist.command('rm <id>').description('칸 삭제')).action((id: string, _opts: unknown, cmd: Command) => {
|
|
@@ -587,4 +592,41 @@ export function registerReleaseCommands(program: Command): void {
|
|
|
587
592
|
const { draftReleaseNotes, readLandedCommits, renderReleaseNotes } = await import('../../scripts/release-notes.js');
|
|
588
593
|
console.log(renderReleaseNotes(draftReleaseNotes(readLandedCommits(o.from, o.to), o.from, o.to)));
|
|
589
594
|
});
|
|
595
|
+
release.command('run')
|
|
596
|
+
.description('릴리스 루프 실행 — 원장 직전 판과 release.loop 설정으로 그래프 입력 구성 · --dry-run 은 입력만 출력')
|
|
597
|
+
.requiredOption('--version <v>', '공개할 판(x.y.z)')
|
|
598
|
+
.option('--dry-run', '체크리스트·그래프 실행 없이 전체 입력 보기')
|
|
599
|
+
.option('--if-ready', '준비되지 않았으면 사유를 출력하고 성공으로 건너뛴다')
|
|
600
|
+
.option('--json', '결과 한 줄 JSON(stdout) · 사람 줄은 stderr')
|
|
601
|
+
.action(async (o: { version: string; dryRun?: boolean; ifReady?: boolean; json?: boolean }) => {
|
|
602
|
+
if (o.ifReady && !o.dryRun) {
|
|
603
|
+
try {
|
|
604
|
+
const outcome = await runReleaseIfReady(o.version, {
|
|
605
|
+
ledgerRoot: releaseRunDeps.ledgerRoot,
|
|
606
|
+
readiness: (version, options) => releaseReadiness(version, { ledgerRoot: releaseRunDeps.ledgerRoot, checklist: releaseRunDeps.checklist, ...options }),
|
|
607
|
+
run: () => runUnattendedRelease({ version: o.version }, releaseRunDeps),
|
|
608
|
+
});
|
|
609
|
+
if (outcome.skipped) {
|
|
610
|
+
if (o.json) await writeStdoutJson(`${JSON.stringify(outcome)}\n`);
|
|
611
|
+
else console.log(`· 준비 안 됨 ${o.version} · ${outcome.reason} · ${outcome.detail}`);
|
|
612
|
+
return;
|
|
613
|
+
}
|
|
614
|
+
const result = outcome.result;
|
|
615
|
+
const ok = result.dryRun || result.state?.status === 'done' || result.state?.status === 'awaiting-approval';
|
|
616
|
+
(o.json ? console.error : console.log)(`${result.dryRun ? '· 드라이런' : '▶ 릴리스 루프'} ${result.input.version} · 입력 ${JSON.stringify(result.input)}`);
|
|
617
|
+
if (o.json) await writeStdoutJson(`${JSON.stringify({ ok, ...result })}\n`);
|
|
618
|
+
if (!ok && !process.exitCode) process.exitCode = 1;
|
|
619
|
+
} catch (e) {
|
|
620
|
+
if (o.json) await writeStdoutJson(`${JSON.stringify({ ok: false, error: (e as Error).message })}\n`);
|
|
621
|
+
else console.error(`⛔ ${(e as Error).message}`);
|
|
622
|
+
process.exitCode = 1;
|
|
623
|
+
}
|
|
624
|
+
return;
|
|
625
|
+
}
|
|
626
|
+
await jsonAction(o.json, async (log) => {
|
|
627
|
+
const result = await runUnattendedRelease({ version: o.version, dryRun: o.dryRun }, releaseRunDeps);
|
|
628
|
+
log(`${result.dryRun ? '· 드라이런' : '▶ 릴리스 루프'} ${result.input.version} · 입력 ${JSON.stringify(result.input)}`);
|
|
629
|
+
return result;
|
|
630
|
+
}, (r) => r.dryRun || r.state?.status === 'done' || r.state?.status === 'awaiting-approval');
|
|
631
|
+
});
|
|
590
632
|
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { closeSync, mkdirSync, openSync, rmSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { debug } from '../debug/log.js';
|
|
4
|
+
import { releaseLedgerRoot } from '../instance/resolve.js';
|
|
5
|
+
import type { ReleaseReadiness } from '../../scripts/release-loop/release-readiness.js';
|
|
6
|
+
|
|
7
|
+
export type ReleaseIfReadySkip = { skipped: true; reason: Exclude<ReleaseReadiness, { ready: true }>['reason']; detail: string };
|
|
8
|
+
|
|
9
|
+
export async function runReleaseIfReady<T>(version: string, deps: {
|
|
10
|
+
readiness: (version: string, options?: { isPidAlive?: (pid: number) => boolean }) => ReleaseReadiness | Promise<ReleaseReadiness>;
|
|
11
|
+
run: () => Promise<T>;
|
|
12
|
+
ledgerRoot?: string;
|
|
13
|
+
pid?: number;
|
|
14
|
+
now?: () => Date;
|
|
15
|
+
}): Promise<ReleaseIfReadySkip | { skipped: false; result: T }> {
|
|
16
|
+
const skip = (readiness: Exclude<ReleaseReadiness, { ready: true }>): ReleaseIfReadySkip => {
|
|
17
|
+
const detail = readiness.reason === 'checklist-blocked'
|
|
18
|
+
? [...readiness.details.red, ...readiness.details.undecided, ...readiness.details.blocked].join(', ')
|
|
19
|
+
: readiness.details;
|
|
20
|
+
debug.log('release-loop.readiness', readiness.reason, { version, detail });
|
|
21
|
+
return { skipped: true, reason: readiness.reason, detail };
|
|
22
|
+
};
|
|
23
|
+
const readiness = await deps.readiness(version);
|
|
24
|
+
if (!readiness.ready) return skip(readiness);
|
|
25
|
+
|
|
26
|
+
const dir = join(deps.ledgerRoot ?? releaseLedgerRoot(), 'release', version);
|
|
27
|
+
mkdirSync(dir, { recursive: true });
|
|
28
|
+
const lock = join(dir, 'run.lock');
|
|
29
|
+
let fd: number;
|
|
30
|
+
try { fd = openSync(lock, 'wx', 0o600); }
|
|
31
|
+
catch (error) {
|
|
32
|
+
if ((error as NodeJS.ErrnoException).code !== 'EEXIST') throw error;
|
|
33
|
+
const detail = 'run.lock already exists';
|
|
34
|
+
debug.log('release-loop.readiness', 'already-running', { version, detail });
|
|
35
|
+
return { skipped: true, reason: 'already-running', detail };
|
|
36
|
+
}
|
|
37
|
+
try {
|
|
38
|
+
writeFileSync(fd, `${JSON.stringify({ pid: deps.pid ?? process.pid, startedAt: (deps.now ?? (() => new Date()))().toISOString() })}\n`);
|
|
39
|
+
// This call still checks publication and checklist, but must not treat our own lock as another runner.
|
|
40
|
+
const insideLock = await deps.readiness(version, { isPidAlive: () => false });
|
|
41
|
+
if (!insideLock.ready) return skip(insideLock);
|
|
42
|
+
debug.log('release-loop.readiness', 'ready', { version });
|
|
43
|
+
return { skipped: false, result: await deps.run() };
|
|
44
|
+
} finally {
|
|
45
|
+
closeSync(fd);
|
|
46
|
+
rmSync(lock);
|
|
47
|
+
}
|
|
48
|
+
}
|
package/src/cli/setup-cli.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from 'node:fs';
|
|
2
2
|
import { homedir } from 'node:os';
|
|
3
3
|
import { join } from 'node:path';
|
|
4
|
-
import
|
|
5
|
-
import { formatDoctorReport, runDoctor, type DoctorOptions, type DoctorReport } from './doctor-cli.js';
|
|
4
|
+
import { Command } from 'commander';
|
|
5
|
+
import { formatDoctorReport, registerDoctorCommand, runDoctor, type DoctorOptions, type DoctorReport } from './doctor-cli.js';
|
|
6
6
|
import { applyClaudePluginSetup, planClaudePluginSetup } from './claude-plugin-setup.js';
|
|
7
|
+
import { recommendedSetup } from './setup-recommend.js';
|
|
7
8
|
import { debug } from '../debug/log.js';
|
|
8
9
|
import { getUserConfig, saveUserConfig, type UserConfig } from '../user-config.js';
|
|
9
10
|
|
|
@@ -20,6 +21,7 @@ type SetupStep = {
|
|
|
20
21
|
|
|
21
22
|
export interface SetupCliDeps extends DoctorOptions {
|
|
22
23
|
runDoctor?: (options: DoctorOptions) => DoctorReport;
|
|
24
|
+
runDoctorFix?: (args: readonly string[]) => Promise<number>;
|
|
23
25
|
readFile?: (path: string) => string;
|
|
24
26
|
exists?: (path: string) => boolean;
|
|
25
27
|
homeDir?: () => string;
|
|
@@ -154,14 +156,16 @@ export function registerSetupCommand(program: Command, deps: SetupCliDeps = {}):
|
|
|
154
156
|
const setup = program.command('setup')
|
|
155
157
|
.description('Check OpenAI Codex setup and guide each missing credential step')
|
|
156
158
|
.option('--non-interactive', 'Report setup state without prompts or writes')
|
|
157
|
-
.
|
|
159
|
+
.option('--yes', 'Set a missing Codex provider and apply recommended doctor repairs without asking')
|
|
160
|
+
.action(async (opts: { nonInteractive?: boolean; yes?: boolean }) => {
|
|
158
161
|
const doctor = doctorRunner(deps);
|
|
159
162
|
if (opts.nonInteractive) {
|
|
160
163
|
out.log(formatSetupReport(doctor, setupSteps(deps), true));
|
|
164
|
+
for (const line of recommendedSetup((deps.getUserConfig ?? getUserConfig)())) out.log(line);
|
|
161
165
|
setExitCode(0);
|
|
162
166
|
return;
|
|
163
167
|
}
|
|
164
|
-
if (!isStdinTty()) {
|
|
168
|
+
if (!isStdinTty() && !opts.yes) {
|
|
165
169
|
(out.error ?? out.log)('대화형 온보딩은 stdin TTY가 있는 자리에서만 실행할 수 있다. 무인 설정은 `elanous setup --non-interactive`를 사용하라.');
|
|
166
170
|
setExitCode(1);
|
|
167
171
|
return;
|
|
@@ -169,8 +173,8 @@ export function registerSetupCommand(program: Command, deps: SetupCliDeps = {}):
|
|
|
169
173
|
let steps = setupSteps(deps);
|
|
170
174
|
const provider = steps[1]!;
|
|
171
175
|
if (!provider.complete) {
|
|
172
|
-
const
|
|
173
|
-
if (
|
|
176
|
+
const approved = opts.yes || (await prompt('Set llm.provider to openai-codex now? [y/N] '))?.trim().toLowerCase() === 'y';
|
|
177
|
+
if (approved) {
|
|
174
178
|
const config = (deps.getUserConfig ?? getUserConfig)();
|
|
175
179
|
(deps.saveUserConfig ?? saveUserConfig)({ ...config, llm: { ...config.llm, provider: 'openai-codex' } });
|
|
176
180
|
steps = setupSteps(deps);
|
|
@@ -179,6 +183,23 @@ export function registerSetupCommand(program: Command, deps: SetupCliDeps = {}):
|
|
|
179
183
|
}
|
|
180
184
|
}
|
|
181
185
|
out.log(formatSetupReport(doctor, steps, false));
|
|
186
|
+
for (const line of recommendedSetup((deps.getUserConfig ?? getUserConfig)())) out.log(line);
|
|
187
|
+
const accepted = opts.yes || (isStdinTty() && /^(?:y|yes)?$/i.test((await prompt('추천대로 켤까요? [Y/n] '))?.trim() ?? 'n'));
|
|
188
|
+
if (accepted) {
|
|
189
|
+
const runDoctorFix = deps.runDoctorFix ?? (async (args: readonly string[]) => {
|
|
190
|
+
const doctorProgram = new Command();
|
|
191
|
+
let exitCode = 0;
|
|
192
|
+
registerDoctorCommand(doctorProgram, {
|
|
193
|
+
out,
|
|
194
|
+
err: { error: (message) => (out.error ?? out.log)(message) },
|
|
195
|
+
setExitCode: (code) => { exitCode = code; },
|
|
196
|
+
});
|
|
197
|
+
await doctorProgram.parseAsync([...args], { from: 'user' });
|
|
198
|
+
return exitCode;
|
|
199
|
+
});
|
|
200
|
+
setExitCode(await runDoctorFix(['doctor', '--fix', '--yes']));
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
182
203
|
setExitCode(0);
|
|
183
204
|
});
|
|
184
205
|
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export function recommendedSetup(state: { chat: { fastPath: boolean } }): [string, string] {
|
|
2
|
+
return [
|
|
3
|
+
'고칠 것 고치기 — 권한·PATH·서비스 파일·파이썬 환경 등 고칠 수 있는 항목을 고칩니다(로그인·서비스 재시작은 별도): `elanous doctor --fix --yes`',
|
|
4
|
+
state.chat.fastPath
|
|
5
|
+
? '짧은 물음은 빠르게 — 켜져 있음(chat.fastPath=true): 짧은 물음에 도구 없이 먼저 답합니다. 끄기: `elanous config set chat.fastPath false`'
|
|
6
|
+
: '짧은 물음은 빠르게 — 꺼져 있음(chat.fastPath=false): 켜면 짧은 물음에 도구 없이 먼저 답합니다. 켜기: `elanous config set chat.fastPath true`',
|
|
7
|
+
];
|
|
8
|
+
}
|