elanous 0.2.4 → 0.2.6
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/README.md +7 -6
- package/apps/pwa/out/404/index.html +1 -1
- package/apps/pwa/out/404.html +1 -1
- package/apps/pwa/out/_next/static/chunks/1169-696263ac7af8c4d4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1883.c91a2648af4842fd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1947-9d64872eeb9cfdec.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2808-dc06482994b40f70.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{5080-1613752891fcadb4.js → 2984-977cfa339fcd65a5.js} +3 -3
- package/apps/pwa/out/_next/static/chunks/3165-95515ca5f299a120.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{8142-011e1f474efb8b33.js → 3190-e9282753e7ebde85.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/3995-bee512a7063036ce.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{6062-73e68343a9814f6a.js → 4240-7153881b0b377306.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/4259-99614149577e77cb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4283-329eabeaf233e6cd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4328-cb5058b1acc2de5e.js +25 -0
- package/apps/pwa/out/_next/static/chunks/4801-ecee6ef62b349b18.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5110-8058791dd25a2e14.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{3403.4bc97b4066945a94.js → 5329.8b7e4bca684b6eec.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/5330-c41e9670f594c38f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5423-e95fd125d1794349.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5478-b5eae07dc79ea619.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5787-96ee6642b9a661e5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6448-79d72368589f98bc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6522-9866c002b5531836.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6729-b4a558b7aa891e0c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6969-cb03f9410b7d2e03.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7007-3b9861091bb8dd21.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{5791-bb82743fa6299562.js → 763-7d0e1913b5074e98.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{4323.c5dfb6f503304775.js → 7633.0654fe6ea259c1ee.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/7930-8dc7e20fc6fc19ca.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8071-187edbc22a86741f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8198-d4101ad70cb9a579.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8250.4a2c5553a07cdf47.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8293-123b91e7b0330dc0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8296-d6d5d87dfa911d41.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8305-603963c33ee8eec3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8612-4a0066f20951b8fd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{2ea96958-e8c7e112de50b067.js → 8d0d9faf-97887ff99713b84e.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/9181-cf4e1b61c4783f92.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{8046cadf-211ca3c0e00f2b72.js → 91ff6dda-3b33d42d20657429.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/9262-26c2ecbd5a67e4a2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9328-45abdebce4fbecb7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9355-dbc67205adf8745f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9763.255b90dd69875689.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9933-66d31ec25c0231af.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-bd06fb55f4da0381.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-4e586142da455a25.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-889ad5cddcf45b0d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/board/page-47ba401d56572c27.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-0b54f9c83458e038.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/bots/page-4fcc296ef6c8ed90.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/page-fe9fda494a87f4e5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/control/page-f9642e25ce129590.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-e1063e8ba5f3e2e7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/editor/page-9d0df30027d8aa9d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-bd06fb55f4da0381.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/page-d97adf2c9455b8d0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/layout-236b80d202110362.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live/page-21a30f35f1a276ad.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/page-b1a5be357c4fa25d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-1705a60303f7f573.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/morning/page-76b337c11ad0e237.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/not-found-7c0f85fbb56f64c6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-65ca16b20617b44d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/page-e7f9b8fc29411c1d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-92ea6213d1aeec04.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-a913a005aacb5476.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-2d78804a40dfdef6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-08a99148e780ac00.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/page-deb674e7ed36f815.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-49f8de0534d192d7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-bd06fb55f4da0381.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/page-ba162a28d2176e18.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/share/page-1ff1a37b550ab41a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-ccaa698f56c0fbdb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-53b944082de1d7db.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/term/page-9b70adf1552789bf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/trace/page-30d482aa63241fe8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/vault/page-aecf786f54651d8a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-6ab9b62c7b6e08f2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-08949735455e0c9a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-a1479005234a46c2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-c3890e90bd232732.js +1 -0
- package/apps/pwa/out/_next/static/chunks/beb5cf10-9adcc10b83930090.js +1 -0
- package/apps/pwa/out/_next/static/chunks/framework-d68d41c88e05c801.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-app-11c63971b7a9593b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-d09452353bd0d9cc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_app-184d727cebeca767.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_error-21bbbbac99b720b1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{webpack-e5bea36f183b25e6.js → webpack-37b8bf78a3c6eec5.js} +1 -1
- package/apps/pwa/out/_next/static/css/eaa74e8bb2f62269.css +1 -0
- package/apps/pwa/out/_next/static/{zoQX5iTvXOsIvCjxCe1Ji → qMI0jnlZaBjjm4aIDYNGM}/_buildManifest.js +1 -1
- package/apps/pwa/out/approvals/index.html +1 -1
- package/apps/pwa/out/approvals/index.txt +29 -27
- package/apps/pwa/out/autopilot/index.html +1 -1
- package/apps/pwa/out/autopilot/index.txt +29 -27
- package/apps/pwa/out/board/index.html +1 -1
- package/apps/pwa/out/board/index.txt +29 -27
- package/apps/pwa/out/botlab/index.html +1 -1
- package/apps/pwa/out/botlab/index.txt +26 -24
- package/apps/pwa/out/bots/index.html +1 -1
- package/apps/pwa/out/bots/index.txt +26 -24
- package/apps/pwa/out/chat/index.html +1 -1
- package/apps/pwa/out/chat/index.txt +26 -24
- package/apps/pwa/out/control/index.html +1 -1
- package/apps/pwa/out/control/index.txt +29 -27
- package/apps/pwa/out/design-check/index.html +1 -1
- package/apps/pwa/out/design-check/index.txt +29 -27
- package/apps/pwa/out/editor/index.html +1 -1
- package/apps/pwa/out/editor/index.txt +29 -27
- package/apps/pwa/out/healthz +1 -1
- package/apps/pwa/out/icons/elanous-16.png +0 -0
- package/apps/pwa/out/icons/elanous-180.png +0 -0
- package/apps/pwa/out/icons/elanous-192.png +0 -0
- package/apps/pwa/out/icons/elanous-32.png +0 -0
- package/apps/pwa/out/icons/elanous-512.png +0 -0
- package/apps/pwa/out/index.html +1 -1
- package/apps/pwa/out/index.txt +26 -24
- package/apps/pwa/out/intake/index.html +1 -1
- package/apps/pwa/out/intake/index.txt +29 -27
- package/apps/pwa/out/live/index.html +1 -1
- package/apps/pwa/out/live/index.txt +29 -27
- package/apps/pwa/out/manifest.webmanifest +5 -2
- package/apps/pwa/out/market/index.html +1 -1
- package/apps/pwa/out/market/index.txt +26 -24
- package/apps/pwa/out/missions/placeholder/index.html +1 -1
- package/apps/pwa/out/missions/placeholder/index.txt +26 -24
- package/apps/pwa/out/morning/index.html +1 -1
- package/apps/pwa/out/morning/index.txt +29 -27
- package/apps/pwa/out/observatory/index.html +1 -1
- package/apps/pwa/out/observatory/index.txt +29 -27
- package/apps/pwa/out/reflection/index.html +1 -1
- package/apps/pwa/out/reflection/index.txt +27 -25
- package/apps/pwa/out/scheduler/index.html +1 -1
- package/apps/pwa/out/scheduler/index.txt +29 -27
- package/apps/pwa/out/sessions/index.html +1 -1
- package/apps/pwa/out/sessions/index.txt +26 -24
- package/apps/pwa/out/settings/devices/index.html +1 -1
- package/apps/pwa/out/settings/devices/index.txt +29 -27
- package/apps/pwa/out/settings/index.html +1 -1
- package/apps/pwa/out/settings/index.txt +29 -27
- package/apps/pwa/out/setup/done/index.html +1 -1
- package/apps/pwa/out/setup/done/index.txt +29 -27
- package/apps/pwa/out/setup/index.html +1 -1
- package/apps/pwa/out/setup/index.txt +29 -27
- package/apps/pwa/out/share/index.html +1 -1
- package/apps/pwa/out/share/index.txt +29 -27
- package/apps/pwa/out/showroom/index.html +1 -1
- package/apps/pwa/out/showroom/index.txt +26 -24
- package/apps/pwa/out/sw-strategies.js +44 -0
- package/apps/pwa/out/sw.js +6 -44
- package/apps/pwa/out/tasks/index.html +1 -1
- package/apps/pwa/out/tasks/index.txt +29 -27
- package/apps/pwa/out/term/index.html +1 -1
- package/apps/pwa/out/term/index.txt +29 -27
- package/apps/pwa/out/trace/index.html +1 -1
- package/apps/pwa/out/trace/index.txt +29 -27
- package/apps/pwa/out/vault/index.html +1 -1
- package/apps/pwa/out/vault/index.txt +29 -27
- package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
- package/apps/pwa/out/workflows/chat-ui/index.txt +29 -27
- package/apps/pwa/out/workflows/index.html +1 -1
- package/apps/pwa/out/workflows/index.txt +29 -27
- package/apps/pwa/out/workspace/index.html +1 -1
- package/apps/pwa/out/workspace/index.txt +29 -27
- package/apps/pwa/out/worktrees/index.html +1 -1
- package/apps/pwa/out/worktrees/index.txt +29 -27
- package/catalog/resources.yaml +6 -6
- package/docs/goal-context/steward-directives-2026-09-28.md +35 -37
- package/graphs/landing/landing-heal.yaml +1 -1
- package/graphs/release/recipes.yaml +11 -1
- package/graphs/release/release-loop.yaml +9 -2
- package/graphs/video/README.md +1 -0
- package/graphs/video/explainer-line.yaml +58 -0
- package/package.json +3 -1
- package/plugins/card-followup/README.md +33 -0
- package/plugins/card-followup/examples/card-sample.json +7 -0
- package/plugins/card-followup/graphs/card-followup.yaml +16 -0
- package/plugins/card-followup/graphs/recipes.yaml +12 -0
- package/plugins/card-followup/graphs/run-step.ts +172 -0
- package/plugins/card-followup/plugin.json +25 -0
- package/plugins/geo-check/README.md +13 -0
- package/plugins/geo-check/examples/brand-sample.json +8 -0
- package/plugins/geo-check/graphs/geo-check.yaml +20 -0
- package/plugins/geo-check/graphs/recipes.yaml +18 -0
- package/plugins/geo-check/graphs/run-step.ts +177 -0
- package/plugins/geo-check/plugin.json +24 -0
- package/plugins/job-coach/.codex-plugin/plugin.json +18 -1
- package/plugins/job-coach/README.md +42 -0
- package/plugins/job-coach/connectors/ncs/server.ts +4 -3
- package/plugins/job-coach/graphs/gap-judge.ts +44 -0
- package/plugins/job-coach/graphs/recipes.yaml +27 -0
- package/plugins/job-coach/graphs/report-enterprise.yaml +28 -0
- package/plugins/job-coach/graphs/run-step.ts +211 -17
- package/plugins/job-coach/plugin.json +16 -3
- package/plugins/job-coach/skills/enterprise-ai-fit/SKILL.md +8 -0
- package/plugins/job-coach/skills/enterprise-competency/SKILL.md +8 -0
- package/plugins/job-coach/skills/enterprise-needs/SKILL.md +8 -0
- package/plugins/job-coach/skills/enterprise-roadmap/SKILL.md +8 -0
- package/plugins/job-coach/skills/enterprise-task-analysis/SKILL.md +8 -0
- package/plugins/job-coach/skills/interview-to-profile/SKILL.md +18 -1
- package/scripts/backup/elanous-backup.sh +4 -1
- package/scripts/build-docs-site.ts +4 -3
- package/scripts/coord-channel-watch.sh +23 -4
- package/scripts/coord-post.sh +49 -12
- package/scripts/coord-tracks.json +5 -5
- package/scripts/daemon-port-baseline.txt +1 -2
- package/scripts/eval-bot-vs-grok.ts +79 -0
- package/scripts/eval-grok-live-baseline.ts +36 -0
- package/scripts/fixtures/front-route-corpus.sample.json +17 -0
- package/scripts/fleet/node-setup.sh +46 -7
- package/scripts/landing-heal/must-fix-match.ts +26 -0
- package/scripts/landing-heal/run-mission.ts +114 -20
- package/scripts/lib/android-test-device.ts +32 -0
- package/scripts/lib/front-route-measurement.ts +131 -0
- package/scripts/lib/tui-sim-keys.ts +42 -0
- package/scripts/measure-front-route-classifier.ts +56 -0
- package/scripts/media/ocr-leak-scan.sh +21 -0
- package/scripts/public-export.ts +2 -3
- package/scripts/public-import-pr.ts +80 -6
- package/scripts/publish-internal-dist.ts +2 -3
- package/scripts/red-sweep-diff.ts +1 -1
- package/scripts/release-loop/auto-approve-node.ts +72 -0
- package/scripts/release-loop/checklist-gate-node.ts +35 -0
- package/scripts/release-loop/gate-diff.ts +29 -0
- package/scripts/release-loop/gate-node.ts +366 -45
- package/scripts/release-loop/publish-node.ts +7 -3
- package/scripts/release-loop/release-docs.ts +20 -13
- package/scripts/release-loop/shard-plan.ts +47 -0
- package/scripts/release-loop/test-durations.ts +63 -0
- package/scripts/release-loop/upgrade-node.ts +3 -2
- package/scripts/test-audit-coverage-per-file.sh +16 -0
- package/scripts/test-audit-meaningless.ts +88 -0
- package/scripts/test-audit-overlap-and-weak.ts +73 -0
- package/scripts/test-deterministic.ts +27 -5
- package/scripts/tui-sim.ts +59 -14
- package/scripts/ux/bump-installed.sh +20 -0
- package/scripts/ux/lib/cdp.ts +72 -0
- package/scripts/ux/record-page.ts +53 -0
- package/skills/explainer-video/SKILL.md +117 -0
- package/skills/explainer-video/engine/brand/elanous-mark-on-dark.svg +1 -0
- package/skills/explainer-video/engine/build.mjs +431 -0
- package/skills/explainer-video/engine/reel.mjs +128 -0
- package/skills/explainer-video/engine/reel.sh +8 -0
- package/skills/explainer-video/engine/vo.ts +79 -0
- package/skills/explainer-video/examples/elanous-0.2.5/script.json +155 -0
- package/src/agent/nested-agent-env.ts +1 -0
- package/src/agent-mission/driver.ts +95 -16
- package/src/agent-mission/mission-cli.ts +6 -0
- package/src/agent-mission/resource-ladder.ts +73 -25
- package/src/auth/acp-token.ts +16 -0
- package/src/auth/github-app-token.ts +112 -0
- package/src/autopilot/gh-rest-fallback.ts +177 -0
- package/src/autopilot/mission-origin.ts +20 -4
- package/src/autopilot/mission-phase-track.ts +11 -1
- package/src/autopilot/pr-manager.ts +51 -4
- package/src/autopilot/track-agent.ts +3 -3
- package/src/autopilot/track-courier.ts +2 -2
- package/src/boot/acp-server.ts +2 -16
- package/src/boot/daemon-tools/web-search.ts +2 -1
- package/src/boot/ws-bridge.ts +11 -0
- package/src/chat/index.ts +37 -8
- package/src/chat/log-scroll-key.ts +12 -0
- package/src/chat/submit-trace.ts +30 -0
- package/src/cli/agent-cli.ts +7 -2
- package/src/cli/decisions-cli.ts +94 -0
- package/src/cli/doctor-cli.ts +40 -54
- package/src/cli/doctor-readiness.ts +16 -24
- package/src/cli/intake-cli.ts +1 -1
- package/src/cli/mcp-call.ts +9 -0
- package/src/cli/mcp-gateway-cli.ts +7 -3
- package/src/cli/nexus-ios-bind.ts +25 -8
- package/src/cli/phone-link.ts +117 -0
- package/src/cli/plugin-cli.ts +136 -11
- package/src/cli/pr-lineage.ts +2 -1
- package/src/cli/private-ledger-commands.ts +31 -0
- package/src/cli/pwa-start.ts +3 -0
- package/src/cli/release-cli.ts +97 -7
- package/src/cli/self-update.ts +7 -4
- package/src/cli/start-cli.ts +33 -14
- package/src/dashboard/acp-boot.ts +3 -0
- package/src/dashboard/index.ts +62 -13
- package/src/dashboard/input/no-provider-reply.ts +13 -0
- package/src/dashboard/slash-runtime/unknown-slash-reply.ts +78 -0
- package/src/dashboard/stale-install-check.ts +77 -0
- package/src/decisions/decision-ledger.ts +404 -0
- package/src/domains/outbound-alert.ts +13 -7
- package/src/domains/telegram-kind-route.ts +21 -0
- package/src/execution-loop/dispatch-task.ts +139 -0
- package/src/execution-loop/gate-decision-summary.ts +29 -0
- package/src/execution-loop/gates.ts +197 -0
- package/src/field/field-media.ts +245 -0
- package/src/graph-kinds/installed-plugin-nodes.ts +95 -0
- package/src/graph-kinds/plugin-nodes.ts +10 -2
- package/src/graph-runner/runner.ts +35 -5
- package/src/grounding/token.ts +12 -6
- package/src/harness/harness-cli-command.ts +68 -9
- package/src/harness/harness-pod-dispatch.ts +8 -2
- package/src/harness/harness-stop.ts +147 -18
- package/src/harness/self-send-decide.ts +26 -17
- package/src/harness/self-send-target.ts +22 -1
- package/src/harness/worktree-branch-prefix.ts +4 -3
- package/src/index.ts +50 -13
- package/src/instance/resolve.ts +6 -0
- package/src/intake-plane/collect-telegram-saved.ts +5 -0
- package/src/intake-plane/digest.ts +11 -2
- package/src/live/detail-switch.ts +8 -3
- package/src/llm/usable-llm.ts +32 -0
- package/src/loops/registry.ts +27 -5
- package/src/market/publish.ts +59 -3
- package/src/native-tool-catalog.ts +2 -2
- package/src/nexus/api/field-uploads.ts +143 -0
- package/src/nexus/api/graph-approvals.ts +174 -0
- package/src/nexus/api/harness-api.ts +219 -5
- package/src/nexus/api/http-server.ts +69 -6
- package/src/nexus/api/log-fabric.ts +10 -1
- package/src/nexus/api/mcp-oauth-hint.ts +10 -9
- package/src/nexus/api/merge-approvals.ts +2 -1
- package/src/nexus/api/meta-api.ts +14 -0
- package/src/nexus/api/plugins-credentials-api.ts +47 -0
- package/src/nexus/api/plugins-market.ts +87 -3
- package/src/nexus/api/pod-credential-api.ts +68 -14
- package/src/nexus/api/public-routes.ts +7 -0
- package/src/nexus/api/trace.ts +57 -6
- package/src/nexus/boot/register-mcp-clients.ts +99 -14
- package/src/nexus/index.ts +33 -32
- package/src/nexus/outbound/router.ts +1 -1
- package/src/notify/llm-budget-alert.ts +119 -0
- package/src/onboarding.ts +25 -2
- package/src/plugins/core/host.ts +14 -0
- package/src/plugins/install/market-fetch.ts +4 -3
- package/src/plugins/install/plugin-credentials.ts +106 -0
- package/src/plugins/install/plugin-install.ts +163 -32
- package/src/plugins/maker/plugin-maker.ts +208 -0
- package/src/prompt-library/universal-preamble.ts +8 -2
- package/src/relay/channel.ts +184 -0
- package/src/relay/mux.ts +182 -0
- package/src/relay/test-vectors.json +29 -0
- package/src/release-loop/checklist.ts +217 -0
- package/src/release-loop/release-note.ts +6 -3
- package/src/seat-address/seat-address.ts +34 -0
- package/src/self-dev/dev-cli.ts +40 -20
- package/src/self-dev/dev-pipeline.ts +77 -1
- package/src/self-implement/gate-baseline.ts +3 -1
- package/src/self-implement/goal-author.ts +18 -1
- package/src/self-implement/host-regate.ts +6 -2
- package/src/self-implement/orchestrator.ts +7 -2
- package/src/self-implement/run-ledger-compact.ts +1 -1
- package/src/self-implement/seams.ts +4 -2
- package/src/session/index.ts +23 -10
- package/src/skills/tools/dev-harness.ts +42 -0
- package/src/steward/steward-cards.ts +45 -0
- package/src/steward/triage.ts +50 -14
- package/src/task-orchestrator/surfaces/pod-artifact-return.ts +61 -3
- package/src/task-orchestrator/surfaces/pod-bun-cache.ts +18 -0
- package/src/task-orchestrator/surfaces/pod-command-job.ts +36 -6
- package/src/task-orchestrator/surfaces/pod-ledger-collect.ts +39 -9
- package/src/task-orchestrator/surfaces/pod-source-bundle.ts +16 -1
- package/src/task-orchestrator/surfaces/pod-source-receive.ts +51 -13
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +172 -28
- package/src/telegram-report.ts +12 -6
- package/src/telegram.ts +108 -1
- package/src/tui-client/acp-transport-ws-client.ts +72 -5
- package/src/tui.ts +3 -2
- package/src/user-config.ts +32 -2
- package/src/util/spawn-sync-output.ts +15 -0
- package/src/version/code-revision.ts +1 -1
- package/src/version/packed-revision.json +1 -1
- package/src/video-pipeline/recipes/explainer.ts +213 -0
- package/src/video-pipeline/walk-line.ts +3 -2
- package/src/web-search/free.ts +77 -0
- package/src/web-search/index.ts +2 -0
- package/src/workflow-runtime/deps-bridge.ts +4 -3
- package/src/workflow-runtime/plugin-kind-node.ts +32 -4
- package/src/workflow-runtime/types.ts +2 -1
- package/apps/pwa/out/_next/static/chunks/1105-c473ed46272fba79.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1191-7b13a24117a689fa.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-3fcae5186c64fe6b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2415.ed6428ff334c7ce2.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/319-d37df1b6e0cbeff4.js +0 -1
- 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/4836-3c93795ab834381e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5609-012bb3557211f07e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6090-1bd6077591603947.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/7457-f5cb1cc8db9f7453.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-f74bf14d67ad98f1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8519-0059d82bdb4812bf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8619-4365e03a2862830f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/920-f7dceb78f1c585ef.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9651-3cd291a6fa1370d2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9740-52467f244755917f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9804-64c02c3c127d5014.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-21c3261af6ab377c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-1991445b8ea86274.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-4ebfaee2369fe248.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-9fa879ebf5959096.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-ca7c3cc305c3285a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/chat/page-9951930add3d2051.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/control/page-4472ddf1e2458e69.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-4de08d4b766f134b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-7a975e2b9db1dfb7.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-49270714ef35bb96.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-33fbdf353e2a5982.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-259f122e2dd64317.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/market/page-65b82726753658e1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-4b8df3748dc06285.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/morning/page-112f7e477bda77fd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-542a5c0006da637c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-63ffb347831262ea.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-98e4e359d97263b4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-97e24ac675ce576e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-ac6391f26391d89d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-9f45c9d45ffc43bd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-36995b9cb5d98218.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/page-0c795b577988d63f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-b4311e2f71c6735a.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-eb2381074df20dba.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/share/page-b01336e64eba01ae.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-7af7f21c1f2858ef.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-a98ecd364586c89b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/term/page-57f0502dfbfa089b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-3467d2e1abd8a14a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-7842ca23d53ba682.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-ae9ac882912b0198.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-3a0d72e3f783bcef.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-f3b39fe8358c2a97.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-5a22af3181ac2afb.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-1457627bb302a22c.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/css/e66b7f5efc8df3f3.css +0 -1
- /package/apps/pwa/out/_next/static/{zoQX5iTvXOsIvCjxCe1Ji → qMI0jnlZaBjjm4aIDYNGM}/_ssgManifest.js +0 -0
package/src/chat/index.ts
CHANGED
|
@@ -107,9 +107,27 @@ import {
|
|
|
107
107
|
import { resolveTextInputControlAction } from './input-control-key.js';
|
|
108
108
|
import { resolveTextInputEditAction } from './input-edit-key.js';
|
|
109
109
|
import { resolveTextInputTextAction } from './input-text-key.js';
|
|
110
|
+
import { classifyLeftoverInput, summarizeSubmitText } from './submit-trace.js';
|
|
110
111
|
|
|
111
112
|
import { perf } from '../perf-counters.js';
|
|
112
113
|
|
|
114
|
+
let lastSubmitAt: number | null = null;
|
|
115
|
+
|
|
116
|
+
function traceAfterSubmitKey(key: Key): void {
|
|
117
|
+
if (lastSubmitAt === null) return;
|
|
118
|
+
const withinMs = Date.now() - lastSubmitAt;
|
|
119
|
+
if (withinMs > 1000) {
|
|
120
|
+
lastSubmitAt = null;
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
if (key.ctrl || key.alt) return;
|
|
124
|
+
const input = key.name === 'paste' ? key.paste ?? '' : key.name;
|
|
125
|
+
if (!input || (key.name !== 'paste' && [...input].length !== 1)) return;
|
|
126
|
+
lastSubmitAt = null;
|
|
127
|
+
const kind = classifyLeftoverInput(input, withinMs);
|
|
128
|
+
if (kind) debug.log('chat.input.leftover', 'after-submit', { withinMs, kind });
|
|
129
|
+
}
|
|
130
|
+
|
|
113
131
|
/** Row where the input prompt starts — set by the textInput paint
|
|
114
132
|
* loop so writeTerminal knows which rows to invalidate in the
|
|
115
133
|
* frame cache. Falls back to "invalidate everything" when unknown
|
|
@@ -419,14 +437,14 @@ export const SLASH_COMMANDS: SlashCommand[] = [
|
|
|
419
437
|
// B4 — craft rulebook verdict, same resolution as `elanous repo design-check`
|
|
420
438
|
// and the PWA `/design-check` panel. Listed (not baselined as hidden) on
|
|
421
439
|
// purpose: a surface nobody can discover is not a surface.
|
|
422
|
-
{ name: 'design', aliases: ['design-check'], description: '
|
|
440
|
+
{ name: 'design', aliases: ['design-check'], description: 'Design rulebooks this repository declares · /design [--declared] · /design pick [number|id] to choose a design direction', subcommands: ['--declared', 'pick'] },
|
|
423
441
|
{ name: 'provider', aliases: ['p'], description: 'LLM providers — /provider (list) · next (cycle, also Alt+M / pill click) · use <name> · pick (visual picker) · reset', subcommands: ['next', 'use', 'pick', 'picker', 'menu', 'reset', 'list'] },
|
|
424
442
|
{ name: 'reasoning', aliases: ['r', 'think'], description: 'Reasoning level (codex effort + summary, anthropic extended-thinking budget) — /reasoning [off|low|medium|high|xhigh] (cycle when no arg · xhigh 는 모델 상한이 xhigh 이상일 때만 wire 에 실린다, 아니면 high 로 깎인다)', subcommands: ['off', 'low', 'medium', 'high', 'xhigh'] },
|
|
425
443
|
{ name: 'model', aliases: ['m'], description: 'Switch active model — /model <codex|terra|sol|luna|opus|sonnet|grok> (list + current when no arg). OpenAI 는 Codex(Responses API)만. effort 는 /reasoning.', subcommands: ['codex', 'terra', 'sol', 'luna', 'opus', 'sonnet', 'grok'] },
|
|
426
444
|
{ name: 'local', aliases: ['ll'], description: 'Local OpenAI-compatible LLM — ping / models / test / use', subcommands: ['ping', 'models', 'test', 'use', 'status'] },
|
|
427
445
|
{ name: 'session', aliases: ['sess'], description: 'Session resume — list / load / sync / new (handoff from mobile)', subcommands: ['list', 'load', 'sync', 'new'] },
|
|
428
|
-
{ name: 'resume', aliases: [], description: '
|
|
429
|
-
{ name: 'fork', aliases: [], description: '
|
|
446
|
+
{ name: 'resume', aliases: [], description: 'Pick a past session, or load one by id prefix · /resume <id-prefix>' },
|
|
447
|
+
{ name: 'fork', aliases: [], description: 'Copy this session into a new one' },
|
|
430
448
|
{ name: 'rewind', aliases: [], description: 'Rewind — 과거 user 턴 픽커로 되감기 (원본 보존 · 새 세션 분기 · /rewind <n> 숏컷)' },
|
|
431
449
|
// TUI 부활 C-a (2026-07-12): autopilot 미션 TUI 표면 — CLI 동등 단일 창구 소비.
|
|
432
450
|
{ name: 'mission', aliases: [], description: 'Autopilot 미션 — list(+헬스) · trace <id> 계보 · arm <id> 승인 (구체화/종료는 CLI)', subcommands: ['list', 'trace', 'arm'] },
|
|
@@ -468,7 +486,7 @@ export const SLASH_COMMANDS: SlashCommand[] = [
|
|
|
468
486
|
{ name: 'acp', aliases: [], description: 'ACP chat — stream claude-code / codex / gemini replies into the chat pane (not a VW spawn). /acp codex points at the canonical codex app-server path; /acp cas is a synonym.', subcommands: ['claude', 'codex', 'gemini', 'cas', 'cancel', 'status', 'drop'] },
|
|
469
487
|
{ name: 'conv', aliases: [], description: 'Conversation widget/popup host — /conv list · /conv open <session-id> · /conv layout <cascade|tile|stack> · /conv focus <next|prev>.', subcommands: ['list', 'ls', 'open', 'layout', 'focus'] },
|
|
470
488
|
{ name: 'handoff', aliases: [], description: 'H5 P3 cross-agent context handoff: /handoff <from_session_id> <to_brand> [--channels r,p,m] [--prompt "prefix"]. Takes source session snapshot (filtered by channels if observer present) and launches target via adapter registry. Brands: codex · claude · claude-code · gemini · elanous.' },
|
|
471
|
-
{ name: 'remaining', aliases: [], description: '
|
|
489
|
+
{ name: 'remaining', aliases: [], description: 'What each account has left' },
|
|
472
490
|
{ name: 'agent-room', aliases: [], description: 'H6 P4 VW agent-room — /agent-room <N> <brands...> · /agent-room list · /agent-room close <id> · N ∈ {2,3,4} · brands: codex/claude/gemini/elanous/auto/lll:<m>', subcommands: ['list', 'close', 'preset', 'help'] },
|
|
473
491
|
{ name: 'showroom', aliases: ['sr'], description: 'Showroom v2 multi-LLM lane composer — /showroom (default 2-pane) · /showroom <lane1> <lane2> [<lane3> [<lane4>]] · lane = role:provider[:transport] · roles plan/build/exec/review/reflect · pair with /lane and /relay for cross-lane handoffs.', subcommands: ['help'] },
|
|
474
492
|
{ name: 'reply', aliases: [], description: 'H6 P5 AgentReply — /reply <target-session-id> <message...> · send message to a live session, capture the response · flags: --from · --channels r,m · --idle-ms · --timeout-ms', subcommands: ['help'] },
|
|
@@ -701,6 +719,8 @@ export async function textInput(opts: {
|
|
|
701
719
|
* typing. The host is expected to mutate its own state; the input
|
|
702
720
|
* redraws the bar + picker on top afterward. */
|
|
703
721
|
onMouse?: (mouse: { row: number; col: number; type: 'click' | 'double-click' | 'right-click' | 'scroll-up' | 'scroll-down' | 'drag' | 'release'; shift?: boolean }) => void | boolean | Promise<void | boolean>;
|
|
722
|
+
/** Scroll the host's chat log from the input without editing its buffer. */
|
|
723
|
+
onScrollKey?: (name: 'pageup' | 'pagedown') => boolean | void;
|
|
704
724
|
/** MX11b — key interceptor called BEFORE textInput's own key
|
|
705
725
|
* handling. Host returns 'consumed' to short-circuit the event
|
|
706
726
|
* (no further input processing), 'passthrough' to let the default
|
|
@@ -1331,13 +1351,15 @@ export async function textInput(opts: {
|
|
|
1331
1351
|
writeTerminal(endTextInputCursor(opts.cursorSink));
|
|
1332
1352
|
return result;
|
|
1333
1353
|
};
|
|
1334
|
-
const submitBufferResult = (text: string): InputResult =>
|
|
1335
|
-
|
|
1354
|
+
const submitBufferResult = (text: string): InputResult => {
|
|
1355
|
+
debug.log('chat.input.submit', 'summary', summarizeSubmitText(text.trim()));
|
|
1356
|
+
lastSubmitAt = Date.now();
|
|
1357
|
+
return finalizeInput({
|
|
1336
1358
|
text: text.trim(),
|
|
1337
1359
|
submitted: true,
|
|
1338
1360
|
...(pendingExternalSubmitSource ? { externalSubmitSource: pendingExternalSubmitSource } : {}),
|
|
1339
|
-
})
|
|
1340
|
-
|
|
1361
|
+
});
|
|
1362
|
+
};
|
|
1341
1363
|
const currentBufferText = (): string => lines.join('\n').trim();
|
|
1342
1364
|
|
|
1343
1365
|
// Expose drawAll so the host can ping us to re-paint after an
|
|
@@ -1401,6 +1423,7 @@ export async function textInput(opts: {
|
|
|
1401
1423
|
}
|
|
1402
1424
|
|
|
1403
1425
|
const key = await (opts.readKey ?? (() => readKey('input')))();
|
|
1426
|
+
traceAfterSubmitKey(key);
|
|
1404
1427
|
|
|
1405
1428
|
// Raw-key trace — diagnoses "modifier bit dropped before global-action
|
|
1406
1429
|
// matcher saw the chord" cases (e.g. tablet terminal apps that
|
|
@@ -1627,6 +1650,11 @@ export async function textInput(opts: {
|
|
|
1627
1650
|
}
|
|
1628
1651
|
}
|
|
1629
1652
|
|
|
1653
|
+
if (!key.ctrl && (key.name === 'pageup' || key.name === 'pagedown') && opts.onScrollKey) {
|
|
1654
|
+
if (opts.onScrollKey(key.name) !== false) drawAll();
|
|
1655
|
+
continue;
|
|
1656
|
+
}
|
|
1657
|
+
|
|
1630
1658
|
const controlAction = resolveTextInputControlAction(key, {
|
|
1631
1659
|
currentLine: lines[lineIdx] ?? '',
|
|
1632
1660
|
cursor: colIdx,
|
|
@@ -1897,6 +1925,7 @@ export function attachStreamingKeys(
|
|
|
1897
1925
|
};
|
|
1898
1926
|
function deliver(keys: readonly Key[]): void {
|
|
1899
1927
|
for (const key of keys) {
|
|
1928
|
+
traceAfterSubmitKey(key);
|
|
1900
1929
|
traceKey(key, 'stream');
|
|
1901
1930
|
try {
|
|
1902
1931
|
const r = handler(key);
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export function nextChatScrollOffset(
|
|
2
|
+
current: number,
|
|
3
|
+
key: 'pageup' | 'pagedown',
|
|
4
|
+
pageSize: number,
|
|
5
|
+
maxOffset: number,
|
|
6
|
+
): number {
|
|
7
|
+
if (maxOffset <= 0) return -1;
|
|
8
|
+
const offset = current < 0 ? maxOffset : Math.max(0, Math.min(current, maxOffset));
|
|
9
|
+
if (key === 'pageup') return Math.max(0, offset - pageSize);
|
|
10
|
+
const next = offset + pageSize;
|
|
11
|
+
return next >= maxOffset ? -1 : next;
|
|
12
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
const LONE_JAMO = /[\u3131-\u318e\u1100-\u11ff]/u;
|
|
2
|
+
const HANGUL_SYLLABLE = /[\uac00-\ud7a3]/u;
|
|
3
|
+
|
|
4
|
+
export function summarizeSubmitText(text: string): {
|
|
5
|
+
chars: number;
|
|
6
|
+
lines: number;
|
|
7
|
+
lastIsLoneJamo: boolean;
|
|
8
|
+
loneJamoCount: number;
|
|
9
|
+
hasHangul: boolean;
|
|
10
|
+
} {
|
|
11
|
+
const characters = [...text];
|
|
12
|
+
const loneJamoCount = characters.filter(char => LONE_JAMO.test(char)).length;
|
|
13
|
+
return {
|
|
14
|
+
chars: characters.length,
|
|
15
|
+
lines: text.split('\n').length,
|
|
16
|
+
lastIsLoneJamo: LONE_JAMO.test(characters.at(-1) ?? ''),
|
|
17
|
+
loneJamoCount,
|
|
18
|
+
hasHangul: loneJamoCount > 0 || HANGUL_SYLLABLE.test(text),
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function classifyLeftoverInput(
|
|
23
|
+
input: string,
|
|
24
|
+
msSinceSubmit: number,
|
|
25
|
+
): 'syllable' | 'jamo' | null {
|
|
26
|
+
if (msSinceSubmit < 0 || msSinceSubmit > 1000 || [...input].length !== 1) return null;
|
|
27
|
+
if (HANGUL_SYLLABLE.test(input)) return 'syllable';
|
|
28
|
+
if (LONE_JAMO.test(input)) return 'jamo';
|
|
29
|
+
return null;
|
|
30
|
+
}
|
package/src/cli/agent-cli.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Command } from 'commander';
|
|
2
2
|
import { resolve } from 'node:path';
|
|
3
3
|
import { getUserConfig, reloadUserConfig } from '../user-config.js';
|
|
4
|
-
import { needsOnboarding, runOnboarding } from '../onboarding.js';
|
|
4
|
+
import { handleOnboardingRefusal, needsOnboarding, runOnboarding } from '../onboarding.js';
|
|
5
5
|
import { runTurn, ensureCliSession, sessionBudget } from '../session/chat.js';
|
|
6
6
|
import { NoLlmProviderAvailableError, noProviderAvailableMessage } from '../llm.js';
|
|
7
7
|
import { resolveSessionId, getActiveSessionId, setActiveSessionId } from '../session/index.js';
|
|
@@ -93,7 +93,12 @@ export function registerAgentCommands(program: Command): void {
|
|
|
93
93
|
const cfg = getUserConfig();
|
|
94
94
|
if (needsOnboarding(cfg)) {
|
|
95
95
|
ui.info('No config yet — launching setup wizard first.');
|
|
96
|
-
|
|
96
|
+
try {
|
|
97
|
+
await runOnboarding();
|
|
98
|
+
} catch (error) {
|
|
99
|
+
if (handleOnboardingRefusal(error, 'agent')) return;
|
|
100
|
+
throw error;
|
|
101
|
+
}
|
|
97
102
|
}
|
|
98
103
|
const refreshed = reloadUserConfig();
|
|
99
104
|
await runChatTurnCli({
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import type { Command } from 'commander';
|
|
2
|
+
import { DecisionLedger, importDecisionMarkdown, type DecisionCategory, type DecisionLedgerOptions, type DecisionOption, type DecisionTrack, type DecisionEntry } from '../decisions/decision-ledger.js';
|
|
3
|
+
|
|
4
|
+
const repeat = (value: string, values: string[]) => [...values, value];
|
|
5
|
+
function date(raw?: string): string | undefined {
|
|
6
|
+
if (!raw) return undefined;
|
|
7
|
+
const days = /^(\d+)d$/.exec(raw);
|
|
8
|
+
const parsed = days ? new Date(Date.now() - Number(days[1]) * 86400000) : new Date(raw);
|
|
9
|
+
if (!Number.isFinite(parsed.getTime())) throw new Error(`invalid since: ${raw}`);
|
|
10
|
+
return parsed.toISOString();
|
|
11
|
+
}
|
|
12
|
+
const kst = (at: string) => new Intl.DateTimeFormat('sv-SE', { timeZone: 'Asia/Seoul', dateStyle: 'short', timeStyle: 'medium' }).format(new Date(at)) + ' KST';
|
|
13
|
+
const version = (e: DecisionEntry['version']) => e ? `released=${e.released ?? '-'} · dev=${e.dev ?? '-'}${e.codename ? ` «${e.codename}»` : ''}` : '판 미상';
|
|
14
|
+
function agent(o?: string): string {
|
|
15
|
+
const value = o?.trim() || process.env.AI_AGENT?.trim();
|
|
16
|
+
if (!value) throw new Error('agent unknown: provide --agent or AI_AGENT');
|
|
17
|
+
return value;
|
|
18
|
+
}
|
|
19
|
+
function parseOption(raw: string): DecisionOption {
|
|
20
|
+
const m = /^([a-z])=(.+?):(.+)$/.exec(raw);
|
|
21
|
+
if (!m) throw new Error(`invalid --option: ${raw} (expected a=label:consequence)`);
|
|
22
|
+
return { key: m[1]!, label: m[2]!, consequence: m[3]! };
|
|
23
|
+
}
|
|
24
|
+
export function formatDecisionRow(e: DecisionEntry): string {
|
|
25
|
+
const recommendation = 'skipped' in e.recommendation ? '권고 생략' : `${e.recommendation.option} (${e.recommendation.why})`;
|
|
26
|
+
return `${e.id} · ${e.category} · ${e.title} · ${recommendation} · ${e.raisedAt ? kst(e.raisedAt) : '올린 시각 미상'} · ${version(e.version)}`;
|
|
27
|
+
}
|
|
28
|
+
export function formatDecisionDetail(e: DecisionEntry): string {
|
|
29
|
+
return [formatDecisionRow(e), `상태: ${e.status}`, `S: ${e.scqa.s}`, `C: ${e.scqa.c}`, `Q: ${e.scqa.q ?? '(비움)'}`, `A: ${e.scqa.a ?? '(비움)'}`,
|
|
30
|
+
'선택지 | 결과', ...(e.options.length ? e.options.map(o => `${o.key}: ${o.label} | ${o.consequence}`) : ['원문에 선택지 없음 · 확인 필요']),
|
|
31
|
+
'skipped' in e.recommendation ? `권고 생략: ${e.recommendation.reason}` : `권고: ${e.recommendation.option} — ${e.recommendation.why}`,
|
|
32
|
+
`올림: ${e.raisedAt ? kst(e.raisedAt) : '시각 미상'} · ${e.raisedBy.agent} · ${version(e.version)}`,
|
|
33
|
+
...(e.importedAt ? [`가져옴: ${kst(e.importedAt)}`] : []),
|
|
34
|
+
...(e.status === 'decided' ? [`결정: ${e.decidedAt ? kst(e.decidedAt) : '시각 미상'} · ${e.decidedBy?.kind === 'auto' ? `AUTO ${e.decidedBy.agent} (${e.decidedBy.delegation})` : e.decidedBy?.kind === 'human' ? 'human' : '주체 미상'} · ${e.choice ?? '선택 키 미상'} · ${e.versionAtDecision ? version(e.versionAtDecision) : '결정 당시 판 미상'}`,
|
|
35
|
+
...(e.note ? [`메모: ${e.note}`] : [])] : []),
|
|
36
|
+
...(e.withdrawnAt ? [`철회: ${kst(e.withdrawnAt)} · ${e.withdrawReason}`] : []),
|
|
37
|
+
'이력:', ...e.history.map(h => ` ${h.type} · ${h.at ? kst(h.at) : '시각 미상'} · ${h.by}${h.choice ? ` · ${h.choice}` : ''}${h.reason ? ` · ${h.reason}` : ''} · ${h.version ? version(h.version) : '판 미상'}`),
|
|
38
|
+
...(e.refs?.length ? [`참조: ${e.refs.join(' · ')}`] : [])].join('\n');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function registerDecisionsCommands(program: Command, config: DecisionLedgerOptions = {}, out: Pick<Console, 'log'> = console): void {
|
|
42
|
+
const root = program.command('decisions').description('대표 결정 원장 · 로컬 전용');
|
|
43
|
+
const emit = (value: unknown, json?: boolean, text?: string) => out.log(json ? JSON.stringify(value) : text ?? JSON.stringify(value));
|
|
44
|
+
const fail = (action: () => void) => { try { action(); } catch (e) { throw new Error(`decisions: ${e instanceof Error ? e.message : String(e)}`); } };
|
|
45
|
+
root.command('raise').description('SCQA·선택지와 권고로 결정 항목을 올린다')
|
|
46
|
+
.requiredOption('--title <text>').requiredOption('--category <category>')
|
|
47
|
+
.requiredOption('--s <text>').requiredOption('--c <text>').option('--q <text>').option('--a <text>')
|
|
48
|
+
.requiredOption('--option <key=label:consequence>', '선택지 (두 번 이상)', repeat, [] as string[])
|
|
49
|
+
.option('--recommend <key>').option('--why <text>').option('--skip-recommend <reason>')
|
|
50
|
+
.option('--track <track>').option('--agent <agent>').option('--session <id>')
|
|
51
|
+
.option('--ref <url>', '참조 (반복)', repeat, [] as string[]).option('--json')
|
|
52
|
+
.action((o: { title: string; category: DecisionCategory; s: string; c: string; q?: string; a?: string; option: string[]; recommend?: string; why?: string; skipRecommend?: string; track?: DecisionTrack; agent?: string; session?: string; ref: string[]; json?: boolean }) => fail(() => {
|
|
53
|
+
if (o.skipRecommend !== undefined && (o.recommend !== undefined || o.why !== undefined)) throw new Error('choose recommendation or skip, not both');
|
|
54
|
+
if (o.skipRecommend === undefined && (!o.recommend || !o.why)) throw new Error('--recommend and --why required, or --skip-recommend <reason>');
|
|
55
|
+
const options: DecisionOption[] = o.option.map(parseOption);
|
|
56
|
+
const entry = new DecisionLedger(config).raise({ title: o.title, category: o.category, scqa: { s: o.s, c: o.c, ...(o.q ? { q: o.q } : {}), ...(o.a ? { a: o.a } : {}) }, options,
|
|
57
|
+
recommendation: o.skipRecommend !== undefined ? { skipped: true, reason: o.skipRecommend } : { option: o.recommend!, why: o.why! },
|
|
58
|
+
raisedBy: { agent: agent(o.agent), ...(o.track ? { track: o.track } : {}), ...(o.session ? { session: o.session } : {}) }, ...(o.ref.length ? { refs: o.ref } : {}) });
|
|
59
|
+
emit(entry, o.json, `올림: ${formatDecisionRow(entry)}`);
|
|
60
|
+
}));
|
|
61
|
+
root.command('list').description('결정 목록 (기본 열린 것)')
|
|
62
|
+
.option('--status <open|decided|all>', '기본 open', 'open').option('--since <date>').option('--version <version>')
|
|
63
|
+
.option('--decided-by <human|auto>').option('--category <category>').option('--json')
|
|
64
|
+
.action((o: { status: 'open' | 'decided' | 'all'; since?: string; version?: string; decidedBy?: 'human' | 'auto'; category?: DecisionCategory; json?: boolean }) => fail(() => {
|
|
65
|
+
const rows = new DecisionLedger(config).list({ status: o.status, since: date(o.since), version: o.version, decidedBy: o.decidedBy, category: o.category });
|
|
66
|
+
emit(rows, o.json, rows.length ? rows.map(formatDecisionRow).join('\n') : '결정 0건');
|
|
67
|
+
}));
|
|
68
|
+
root.command('show <id>').description('SCQA · 선택지 · 권고 · 이력 · 판').option('--json')
|
|
69
|
+
.action((id: string, o: { json?: boolean }) => fail(() => { const entry = new DecisionLedger(config).show(id); emit(entry, o.json, formatDecisionDetail(entry)); }));
|
|
70
|
+
root.command('decide <id> <option>').description('사람 또는 AUTO 위임 결정 기록')
|
|
71
|
+
.option('--note <text>').option('--auto').option('--delegation <reason>').option('--track <track>').option('--agent <agent>').option('--json')
|
|
72
|
+
.action((id: string, choice: string, o: { note?: string; auto?: boolean; delegation?: string; track?: DecisionTrack; agent?: string; json?: boolean }) => fail(() => {
|
|
73
|
+
if (o.auto && !o.delegation?.trim()) throw new Error('--auto requires --delegation');
|
|
74
|
+
if (!o.auto && (o.delegation || o.track || o.agent)) throw new Error('delegation/track/agent require --auto');
|
|
75
|
+
const by = o.auto ? { kind: 'auto' as const, agent: agent(o.agent), delegation: o.delegation!, ...(o.track ? { track: o.track } : {}) } : { kind: 'human' as const };
|
|
76
|
+
const entry = new DecisionLedger(config).decide(id, choice, by, o.note);
|
|
77
|
+
emit(entry, o.json, `결정: ${formatDecisionDetail(entry)}`);
|
|
78
|
+
}));
|
|
79
|
+
root.command('add-options <id>').description('선택지가 미기재된 과거 항목에 확인된 선택지를 추가')
|
|
80
|
+
.requiredOption('--option <key=label:consequence>', '선택지 (두 번 이상)', repeat, [] as string[])
|
|
81
|
+
.option('--agent <agent>').option('--json')
|
|
82
|
+
.action((id: string, o: { option: string[]; agent?: string; json?: boolean }) => fail(() => {
|
|
83
|
+
const entry = new DecisionLedger(config).addOptions(id, o.option.map(parseOption), agent(o.agent));
|
|
84
|
+
emit(entry, o.json, formatDecisionDetail(entry));
|
|
85
|
+
}));
|
|
86
|
+
root.command('withdraw <id>').description('결정 철회도 원장에 남긴다').requiredOption('--reason <reason>').option('--json')
|
|
87
|
+
.action((id: string, o: { reason: string; json?: boolean }) => fail(() => { const entry = new DecisionLedger(config).withdraw(id, o.reason); emit(entry, o.json, `철회: ${formatDecisionDetail(entry)}`); }));
|
|
88
|
+
root.command('import-markdown <file>').description('기존 대표 결정 문서 씨앗을 가져온다 (재실행 안전)').option('--json')
|
|
89
|
+
.action((file: string, o: { json?: boolean }) => fail(() => {
|
|
90
|
+
const result = importDecisionMarkdown(new DecisionLedger(config), file);
|
|
91
|
+
emit(result, o.json, `가져옴 ${result.imported.length} · 기존 ${result.existing.length} · 불완전 ${result.incomplete.length} · 못 읽음 ${result.unread.length}${result.incomplete.length ? `\n${result.incomplete.join('\n')}` : ''}${result.unread.length ? `\n${result.unread.join('\n')}` : ''}`);
|
|
92
|
+
if (result.unread.length) process.exitCode = 1;
|
|
93
|
+
}));
|
|
94
|
+
}
|
package/src/cli/doctor-cli.ts
CHANGED
|
@@ -8,7 +8,7 @@ import { fileURLToPath } from 'node:url';
|
|
|
8
8
|
import type { Command } from 'commander';
|
|
9
9
|
import { parse as parseYaml, stringify as stringifyYaml } from 'yaml';
|
|
10
10
|
import { discoverChromeBinary } from '../browser-cdp/client.js';
|
|
11
|
-
import {
|
|
11
|
+
import { resolveUsableLlm, type UsableLlm } from '../llm/usable-llm.js';
|
|
12
12
|
import { detectLocale } from '../expression/locale.js';
|
|
13
13
|
import { findRetiredConfigKeysInFile, getUserConfig, type RetiredConfigKey, type UserConfig } from '../user-config.js';
|
|
14
14
|
import { codeRevision } from '../version/code-revision.js';
|
|
@@ -142,7 +142,8 @@ export interface DoctorOptions {
|
|
|
142
142
|
*/
|
|
143
143
|
readiness?: ReadinessDeps;
|
|
144
144
|
checkReadiness?: (deps: ReadinessDeps) => ReadinessReport;
|
|
145
|
-
|
|
145
|
+
resolveLlm?: () => UsableLlm;
|
|
146
|
+
/** Legacy read-only seam; route selection no longer uses provider-name presence. */
|
|
146
147
|
listAuthProviders?: () => readonly string[];
|
|
147
148
|
/** Read-only `codeRevision()`. */
|
|
148
149
|
codeRevision?: () => string | undefined;
|
|
@@ -187,15 +188,6 @@ export interface DoctorCliDeps extends DoctorOptions, Pick<DoctorFixDeps, 'home'
|
|
|
187
188
|
const repositoryRoot = resolve(dirname(fileURLToPath(import.meta.url)), '../..');
|
|
188
189
|
const tavilyNames = new Set(['TAVILY_API_KEY', 'TAVILY_KEY']);
|
|
189
190
|
|
|
190
|
-
function codexLoginPresent(providers: readonly string[]): boolean {
|
|
191
|
-
return providers.some((name) => name === 'openai-codex' || name.startsWith('openai-codex:'));
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
/** Presence only. Token strings from the auth store never leave this function. */
|
|
195
|
-
function defaultListAuthProviders(): readonly string[] {
|
|
196
|
-
return listProviders();
|
|
197
|
-
}
|
|
198
|
-
|
|
199
191
|
function defaultCodeRevision(): string | undefined {
|
|
200
192
|
try {
|
|
201
193
|
return codeRevision();
|
|
@@ -294,7 +286,6 @@ interface ReadinessLookup {
|
|
|
294
286
|
platform: NodeJS.Platform;
|
|
295
287
|
commandExists: (name: string) => boolean;
|
|
296
288
|
getConfig: () => UserConfig;
|
|
297
|
-
listAuthProviders: () => readonly string[];
|
|
298
289
|
codeRevision: () => string | undefined;
|
|
299
290
|
fetchHealth: () => { daemonSha?: string } | null;
|
|
300
291
|
readInstallPrefix: () => string | null | undefined;
|
|
@@ -306,8 +297,6 @@ interface ReadinessLookup {
|
|
|
306
297
|
/** 저장소가 시험한 bun 판(`.bun-version`) · 지금 도는 bun 판(시험 seam). */
|
|
307
298
|
bunPin?: () => string | null;
|
|
308
299
|
bunVersion?: () => string | null;
|
|
309
|
-
/** LLM 키가 하나라도 풀렸나(자격 보고서에서 · 없으면 못 쟀다). */
|
|
310
|
-
llmKeyResolved?: boolean;
|
|
311
300
|
/** `/etc/os-release` 본문(시험 seam) — 못 읽으면 null. */
|
|
312
301
|
readOsRelease?: () => string | null;
|
|
313
302
|
/** 빌드 도구 탐침(시험 seam). */
|
|
@@ -371,19 +360,6 @@ function resolveReadinessDeps(lookup: ReadinessLookup): ReadinessDeps {
|
|
|
371
360
|
if (provider !== null && provider !== 'auto' && !/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.test(provider)) {
|
|
372
361
|
provider = null;
|
|
373
362
|
}
|
|
374
|
-
let codexLogin: boolean | null = null;
|
|
375
|
-
let anyLogin: boolean | null = null;
|
|
376
|
-
try {
|
|
377
|
-
const providers = lookup.listAuthProviders();
|
|
378
|
-
codexLogin = codexLoginPresent(providers);
|
|
379
|
-
anyLogin = providers.length > 0;
|
|
380
|
-
} catch {
|
|
381
|
-
codexLogin = null;
|
|
382
|
-
}
|
|
383
|
-
// 로그인이 있거나 LLM 키가 풀렸으면 true · 둘 다 «아니라고 잰» 경우만 false · 하나라도 못 쟀으면 null.
|
|
384
|
-
const llmCredentialAvailable = anyLogin === true || lookup.llmKeyResolved === true
|
|
385
|
-
? true
|
|
386
|
-
: anyLogin === false && lookup.llmKeyResolved === false ? false : null;
|
|
387
363
|
const pathEntries = (lookup.env.PATH ?? '').split(lookup.pathDelimiter).filter((entry) => entry.length > 0);
|
|
388
364
|
let ghOnPath: boolean | null = null;
|
|
389
365
|
let ghAuthStatus: number | null = null;
|
|
@@ -466,8 +442,6 @@ function resolveReadinessDeps(lookup: ReadinessLookup): ReadinessDeps {
|
|
|
466
442
|
}
|
|
467
443
|
return {
|
|
468
444
|
provider,
|
|
469
|
-
codexLogin,
|
|
470
|
-
llmCredentialAvailable,
|
|
471
445
|
ghOnPath,
|
|
472
446
|
ghAuthStatus,
|
|
473
447
|
ghVersion,
|
|
@@ -1045,8 +1019,7 @@ export function runDoctor(options: DoctorOptions = {}): DoctorReport {
|
|
|
1045
1019
|
pathDelimiter,
|
|
1046
1020
|
platform,
|
|
1047
1021
|
commandExists,
|
|
1048
|
-
getConfig,
|
|
1049
|
-
listAuthProviders: options.listAuthProviders ?? defaultListAuthProviders,
|
|
1022
|
+
getConfig: () => resolutionOptions.userConfig,
|
|
1050
1023
|
codeRevision: options.codeRevision ?? defaultCodeRevision,
|
|
1051
1024
|
fetchHealth: options.fetchHealth ?? (() => defaultFetchHealth(options.resolveDaemonEndpoint ?? resolveDaemonEndpoint)),
|
|
1052
1025
|
readInstallPrefix: options.readInstallPrefix ?? (() => defaultReadInstallPrefix(root, exists)),
|
|
@@ -1054,12 +1027,17 @@ export function runDoctor(options: DoctorOptions = {}): DoctorReport {
|
|
|
1054
1027
|
ghAuthStatus: options.ghAuthStatus ?? (() => defaultGhAuthStatus(commandExists)),
|
|
1055
1028
|
ghVersion: options.ghVersion ?? (() => defaultGhVersion(commandExists)),
|
|
1056
1029
|
tmpdirSameFsAsBunCache: options.tmpdirSameFsAsBunCache ?? (() => defaultTmpdirSameFsAsBunCache(env)),
|
|
1057
|
-
llmKeyResolved: llmKeyResolvedFrom(credentials),
|
|
1058
1030
|
nodePty: nodePtyStatus(externalCommands.commands),
|
|
1059
1031
|
...(options.probeBuildToolchain ? { probeBuildToolchain: options.probeBuildToolchain } : {}),
|
|
1060
1032
|
...(options.checkPythonEnv ? { checkPythonEnv: options.checkPythonEnv } : {}),
|
|
1061
1033
|
...(options.probeHostEnvironment ? { probeHostEnvironment: options.probeHostEnvironment } : {}),
|
|
1062
1034
|
});
|
|
1035
|
+
// An injected readiness snapshot stays as-is. For a real machine, measure the
|
|
1036
|
+
// same runtime route as `start` rather than inferring usability from key names.
|
|
1037
|
+
if (options.readiness === undefined && readiness.provider !== null) {
|
|
1038
|
+
try { readiness.usableLlm = options.resolveLlm ? options.resolveLlm() : resolveUsableLlm({ config: resolutionOptions.userConfig }); }
|
|
1039
|
+
catch { readiness.usableLlm = null; }
|
|
1040
|
+
}
|
|
1063
1041
|
return {
|
|
1064
1042
|
ok: true,
|
|
1065
1043
|
credentials,
|
|
@@ -1083,15 +1061,14 @@ function formatReadiness(readiness: ReadinessReport | undefined): string[] {
|
|
|
1083
1061
|
if (readiness === undefined) return [];
|
|
1084
1062
|
const todo = readiness.items.filter((entry) => entry.status === 'manual' || entry.status === 'fixable');
|
|
1085
1063
|
return [
|
|
1086
|
-
'
|
|
1064
|
+
'Readiness:',
|
|
1087
1065
|
...readiness.items.map((entry) => {
|
|
1088
1066
|
const remedy = entry.remedy === undefined ? '' : ` — ${entry.remedy}`;
|
|
1089
1067
|
return ` ${entry.id}: ${entry.status} — ${entry.evidence}${remedy}`;
|
|
1090
1068
|
}),
|
|
1091
1069
|
todo.length === 0
|
|
1092
|
-
? '
|
|
1093
|
-
|
|
1094
|
-
: `할 일 ${todo.length}개: ${todo.map((entry) => entry.id).join(', ')} — 처방은 위 각 줄 끝`,
|
|
1070
|
+
? 'Nothing to do.'
|
|
1071
|
+
: `To do (${todo.length}): ${todo.map((entry) => entry.id).join(', ')} — each line above ends with its fix`,
|
|
1095
1072
|
];
|
|
1096
1073
|
}
|
|
1097
1074
|
|
|
@@ -1105,14 +1082,21 @@ function humanFreeFallback(value: string): string {
|
|
|
1105
1082
|
.replace(/\s{2,}/g, ' ').trim().replace(/[—–·;,\s]+$/, '');
|
|
1106
1083
|
}
|
|
1107
1084
|
|
|
1085
|
+
export function humanBreaks(value: string): string | undefined {
|
|
1086
|
+
if (value.trim().toLowerCase() === 'unmeasured'
|
|
1087
|
+
|| /\b\d+ test (failures|errors)\b|\bmeasured\b|\bpostmortem\b|\bquota\b/i.test(value)
|
|
1088
|
+
|| /[가-힣ㄱ-ㅎㅏ-ㅣ]/.test(value)) return undefined;
|
|
1089
|
+
return humanFreeFallback(value) || undefined;
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1108
1092
|
function formatCapabilitySummary(summary: DoctorCapabilitySummary): string[] {
|
|
1109
1093
|
const unavailable = summary.unavailable.map((capability) =>
|
|
1110
1094
|
` ${capability.name} — unlock with ${capability.credential}${capability.freeFallback !== undefined && humanFreeFallback(capability.freeFallback) ? `; free alternative: ${humanFreeFallback(capability.freeFallback)}` : ''}`,
|
|
1111
1095
|
);
|
|
1112
1096
|
return [
|
|
1113
|
-
'
|
|
1097
|
+
'Available:',
|
|
1114
1098
|
...(summary.available.length === 0 ? [' None.'] : summary.available.map((capability) => ` ${capability.name} — unlocked by ${capability.credential}`)),
|
|
1115
|
-
'
|
|
1099
|
+
'Unavailable:',
|
|
1116
1100
|
...(unavailable.length === 0 ? [' None.'] : unavailable),
|
|
1117
1101
|
'Unknown by credential:',
|
|
1118
1102
|
...(summary.unknownCredentials.length === 0 ? [' Empty.'] : summary.unknownCredentials.map((credential) => ` ${credential}`)),
|
|
@@ -1129,8 +1113,8 @@ export function formatDoctorReport(report: DoctorReport, options: { credentials?
|
|
|
1129
1113
|
const satisfied = report.credentials.filter((credential) => !credential.resolved && credential.satisfiedBy !== undefined).length;
|
|
1130
1114
|
return [
|
|
1131
1115
|
...formatReadiness(report.readiness),
|
|
1132
|
-
...(requiredMissing.length ? [`⛔
|
|
1133
|
-
|
|
1116
|
+
...(requiredMissing.length ? [`⛔ Required commands missing: ${requiredMissing.join(', ')}`] : []),
|
|
1117
|
+
`Credentials: ${resolved}/${report.credentials.length} resolved${satisfied ? ` · ${satisfied} satisfied by alternative names` : ''} · details: elanous doctor --credentials`,
|
|
1134
1118
|
...(options.credentials ? report.credentials.map((credential) => [
|
|
1135
1119
|
`${credential.name}: ${credential.resolved ? 'resolved' : 'unresolved'} (${credential.source}) — ${credential.note}`,
|
|
1136
1120
|
...(credential.satisfiedBy !== undefined ? [` Satisfied by: ${credential.satisfiedBy.name} (${credential.satisfiedBy.source})`] : []),
|
|
@@ -1138,14 +1122,15 @@ export function formatDoctorReport(report: DoctorReport, options: { credentials?
|
|
|
1138
1122
|
...(credential.freeFallback !== undefined ? [` Free fallback${credential.freeFallbackMode === 'auto' || credential.freeFallbackMode === 'manual' || credential.freeFallbackMode === 'none' ? ` [${credential.freeFallbackMode}]` : ''}: ${humanFreeFallback(credential.freeFallback)}`] : []),
|
|
1139
1123
|
].join('\n')) : []),
|
|
1140
1124
|
...(report.catalogMetadataUnavailable ? ['Catalog metadata unavailable: could not read catalog/resources.yaml.'] : []),
|
|
1141
|
-
...(report.retiredConfigKeys ?? []).map(({ path, reason }) =>
|
|
1125
|
+
...(report.retiredConfigKeys ?? []).map(({ path, reason }) => `Retired config key: ${path} — ${reason}`),
|
|
1142
1126
|
'External commands:',
|
|
1143
1127
|
...report.externalCommands.map((command) => {
|
|
1144
1128
|
const breaks = command.status === 'broken' ? command.breaks_broken ?? command.breaks : command.breaks;
|
|
1129
|
+
const visibleBreaks = breaks === undefined ? undefined : humanBreaks(breaks);
|
|
1145
1130
|
const fix = command.status === 'broken' ? command.fix_broken ?? command.fix : command.fix;
|
|
1146
1131
|
return [
|
|
1147
1132
|
`${command.name}: ${command.status === 'skipped' ? `skipped (${command.platform}-only)` : `${command.status} (${command.tier})`}${command.detail === undefined ? '' : ` — ${command.detail}`}`,
|
|
1148
|
-
...((command.status === 'missing' || command.status === 'broken') &&
|
|
1133
|
+
...((command.status === 'missing' || command.status === 'broken') && visibleBreaks !== undefined ? [` Breaks: ${visibleBreaks}`] : []),
|
|
1149
1134
|
...((command.status === 'missing' || command.status === 'broken') && fix !== undefined ? [` Fix: ${fix}`] : []),
|
|
1150
1135
|
].join('\n');
|
|
1151
1136
|
}),
|
|
@@ -1260,13 +1245,12 @@ export function registerDoctorCommand(program: Command, deps: DoctorCliDeps = {}
|
|
|
1260
1245
|
home: deps.home,
|
|
1261
1246
|
cacheDir: deps.cacheDir,
|
|
1262
1247
|
readdir: deps.readdir,
|
|
1263
|
-
readiness: deps.readiness ?? resolveReadinessDeps({
|
|
1248
|
+
readiness: deps.readiness ?? { ...resolveReadinessDeps({
|
|
1264
1249
|
env: deps.env ?? process.env,
|
|
1265
1250
|
pathDelimiter: deps.pathDelimiter ?? delimiter,
|
|
1266
1251
|
platform: deps.platform ?? process.platform,
|
|
1267
1252
|
commandExists: deps.commandExists ?? ((name) => (deps.env ?? process.env).PATH?.split(deps.pathDelimiter ?? delimiter).some((dir) => existsSync(join(dir, name))) ?? false),
|
|
1268
|
-
getConfig: deps.getUserConfig ?? getUserConfig,
|
|
1269
|
-
listAuthProviders: deps.listAuthProviders ?? defaultListAuthProviders,
|
|
1253
|
+
getConfig: () => deps.userConfig ?? (deps.getUserConfig ?? getUserConfig)(),
|
|
1270
1254
|
codeRevision: deps.codeRevision ?? defaultCodeRevision,
|
|
1271
1255
|
fetchHealth: deps.fetchHealth ?? (() => defaultFetchHealth(deps.resolveDaemonEndpoint ?? resolveDaemonEndpoint)),
|
|
1272
1256
|
readInstallPrefix: deps.readInstallPrefix ?? (() => defaultReadInstallPrefix(deps.repositoryRoot ?? repositoryRoot, deps.exists ?? existsSync)),
|
|
@@ -1274,13 +1258,15 @@ export function registerDoctorCommand(program: Command, deps: DoctorCliDeps = {}
|
|
|
1274
1258
|
// gh 판은 수리 계획에 쓰인다(낡은 gh → 정적 gh) — 🩸 09-25: 여기서 안 재서 계획은 보고서에만 뜨고 적용이 안 됐다.
|
|
1275
1259
|
ghVersion: deps.ghVersion ?? (() => defaultGhVersion((name) => (deps.env ?? process.env).PATH?.split(deps.pathDelimiter ?? delimiter).some((dir) => existsSync(join(dir, name))) ?? false)),
|
|
1276
1260
|
tmpdirSameFsAsBunCache: deps.tmpdirSameFsAsBunCache ?? (() => defaultTmpdirSameFsAsBunCache(deps.env ?? process.env)),
|
|
1277
|
-
|
|
1278
|
-
...(source.ok ? { llmKeyResolved: llmKeyResolvedFrom(source.credentials), nodePty: nodePtyStatus(source.externalCommands) } : {}),
|
|
1261
|
+
...(source.ok ? { nodePty: nodePtyStatus(source.externalCommands) } : {}),
|
|
1279
1262
|
// L0 칸(substrate·docker·kubernetes·memory)은 «정보성·선택 기능»이라 수리 계획에 싣지 않는다 —
|
|
1280
1263
|
// `--sudo` 가 `sudo systemctl start docker` 를 «설치 줄»로 치지 않게 하고, docker·kubectl 탐침(최대 수 초)을 두 번 돌리지 않는다.
|
|
1281
1264
|
// 보고서(`runDoctor`)에는 그대로 나온다.
|
|
1282
1265
|
probeHostEnvironment: () => null,
|
|
1283
|
-
}),
|
|
1266
|
+
}), usableLlm: (() => {
|
|
1267
|
+
try { return deps.resolveLlm ? deps.resolveLlm() : resolveUsableLlm({ config: deps.userConfig ?? (deps.getUserConfig ?? getUserConfig)() }); }
|
|
1268
|
+
catch { return null; }
|
|
1269
|
+
})() },
|
|
1284
1270
|
exists: deps.exists,
|
|
1285
1271
|
readFile: deps.readFile,
|
|
1286
1272
|
writeFile: deps.writeFile,
|
|
@@ -1374,16 +1360,16 @@ export function registerDoctorCommand(program: Command, deps: DoctorCliDeps = {}
|
|
|
1374
1360
|
else out.log([
|
|
1375
1361
|
formatDoctorReport(currentReport, { credentials: opts.credentials }),
|
|
1376
1362
|
...(advice ? [formatDoctorAdvice(advice)] : []),
|
|
1377
|
-
'
|
|
1363
|
+
'Fix plan:',
|
|
1378
1364
|
...plan.items.map((item) => ` ${item.id}: ${item.status} — ${item.path} — ${item.action}${item.reason ? ` — ${item.reason}` : ''}`),
|
|
1379
1365
|
...plan.manual.map((item) => ` ${item.id}: manual${item.remedy ? ` — ${item.remedy}` : ''}`),
|
|
1380
1366
|
...manualCommandFixes(currentReport).map((line) => ` ${line}`),
|
|
1381
|
-
...(gitInstall ? [`
|
|
1367
|
+
...(gitInstall ? [`Git installation: ${gitInstall.detail}${gitPlan?.note ? ` — ${gitPlan.note}` : ''}`] : []),
|
|
1382
1368
|
...(sudoResult ? [sudoResult.sudoAvailable
|
|
1383
|
-
? `
|
|
1384
|
-
: '
|
|
1385
|
-
...(results ? ['
|
|
1386
|
-
...(restartResult ? [
|
|
1369
|
+
? `Sudo installation: ${sudoResult.runs.length ? sudoResult.runs.map((entry) => `${entry.result} — ${entry.command}${entry.detail ? ` — ${entry.detail}` : ''}`).join(' · ') : '칠 줄 없음'}`
|
|
1370
|
+
: 'Sudo installation: 건너뜀 — 이 기계는 sudo 에 암호가 필요하다(sudo -n true 실패). 위 «manual» 줄을 직접 치세요.'] : []),
|
|
1371
|
+
...(results ? ['Applied:', ...results.items.map((item) => ` ${item.id}: ${item.result} — ${item.path}${item.reason ? ` — ${item.reason}` : ''}`)] : ['적용하려면 --fix --yes']),
|
|
1372
|
+
...(restartResult ? [`Service restart: ${restartResult.result} — ${restartResult.reason}`] : []),
|
|
1387
1373
|
].join('\n'));
|
|
1388
1374
|
const exitCode = Math.max(results?.exitCode ?? 0, sudoResult?.exitCode ?? 0, restartResult?.result === 'failed' ? 1 : 0, currentReport.requiredMissing?.length ? 1 : 0, gitInstall?.ran && !gitInstall.ok ? 1 : 0);
|
|
1389
1375
|
if (results || exitCode) setExitCode(exitCode);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { remediesFor, type DistroFamily } from './doctor-distro.js';
|
|
2
|
+
import type { UsableLlm } from '../llm/usable-llm.js';
|
|
2
3
|
import { providerSecretNames } from '../nexus/install/launchd.js';
|
|
3
4
|
import { win32 } from 'node:path';
|
|
4
5
|
/**
|
|
@@ -42,13 +43,8 @@ export interface ReadinessDeps {
|
|
|
42
43
|
* must pass `null` rather than omit it).
|
|
43
44
|
*/
|
|
44
45
|
provider?: string | null;
|
|
45
|
-
/**
|
|
46
|
-
|
|
47
|
-
* The token itself is never passed in.
|
|
48
|
-
*/
|
|
49
|
-
codexLogin?: boolean | null;
|
|
50
|
-
/** LLM 을 부를 자격이 «하나라도» 있나(어떤 로그인이든 · LLM 키가 풀렸든). 없으면(undefined/null) 못 쟀다. */
|
|
51
|
-
llmCredentialAvailable?: boolean | null;
|
|
46
|
+
/** Selected runtime route, already resolved with the runtime auto selector. null = unmeasured. */
|
|
47
|
+
usableLlm?: UsableLlm | null;
|
|
52
48
|
/** `gh` is on PATH. `null` means not measured. */
|
|
53
49
|
ghOnPath?: boolean | null;
|
|
54
50
|
/** `gh --version` 의 판 · null/undefined = 못 쟀다. */
|
|
@@ -210,23 +206,19 @@ function providerDecision(deps: ReadinessDeps): ReadinessItem {
|
|
|
210
206
|
return item('provider-decision', 'unknown', 'llm.provider was not measured');
|
|
211
207
|
}
|
|
212
208
|
const provider = deps.provider.trim() || 'auto';
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
if (deps.llmCredentialAvailable === false) {
|
|
227
|
-
return item('provider-decision', 'manual', 'llm.provider=auto but no LLM login or key was found — LLM commands will fail', 'elanous login openai-codex');
|
|
228
|
-
}
|
|
229
|
-
return item('provider-decision', 'ok', 'llm.provider=auto and no codex login');
|
|
209
|
+
const shown = KNOWN_PROVIDERS.has(provider) ? provider : '(set · value not shown)';
|
|
210
|
+
if (deps.usableLlm == null) return item('provider-decision', 'unknown', `llm.provider=${shown} route was not measured`);
|
|
211
|
+
const { usable, provider: selected, via, why } = deps.usableLlm;
|
|
212
|
+
if (!usable) {
|
|
213
|
+
const reason = why === 'no usable LLM route selected' ? why : 'route unavailable';
|
|
214
|
+
const remedy = provider === 'auto' || provider === 'openai-codex' ? 'elanous login openai-codex'
|
|
215
|
+
: provider === 'openai' ? 'Set OPENAI_API_KEY or llm.apiKey for openai'
|
|
216
|
+
: provider === 'local' ? 'Set llm.baseUrl or LOCAL_LLM_URL for local'
|
|
217
|
+
: KNOWN_PROVIDERS.has(provider) ? `Configure credentials for llm.provider=${provider}` : undefined;
|
|
218
|
+
return item('provider-decision', 'manual', `llm.provider=${shown} selected no usable LLM (via none) — ${reason}`, remedy);
|
|
219
|
+
}
|
|
220
|
+
const selectedName = selected && KNOWN_PROVIDERS.has(selected) ? selected : 'selected provider';
|
|
221
|
+
return item('provider-decision', 'ok', `llm.provider=${shown} → ${via} ${selectedName}`);
|
|
230
222
|
}
|
|
231
223
|
|
|
232
224
|
/** Debian 계열 apt 의 gh 는 GH_MIN_VERSION 미만이다(📏 09-25: Debian 12 = 2.23 · Ubuntu 24.04 = 2.45) — apt 로 깔면 곧바로
|
package/src/cli/intake-cli.ts
CHANGED
|
@@ -282,7 +282,7 @@ export function registerIntakeCommands(program: Command): void {
|
|
|
282
282
|
const { sendTelegramReport } = await import('../telegram-report.js');
|
|
283
283
|
const { getUserConfig } = await import('../user-config.js');
|
|
284
284
|
const text = renderDigestTelegram(d, { ...(opts.vault ? { vaultRoot: opts.vault } : {}), ...(opts.note ? { notePath: opts.note } : {}) });
|
|
285
|
-
const sent = await sendTelegramReport(getUserConfig(), text, { markdown: true });
|
|
285
|
+
const sent = await sendTelegramReport(getUserConfig(), text, { markdown: true, kind: 'intake' });
|
|
286
286
|
debug.log('intake.digest', 'telegram', { day, absorbed: d.absorbed.length, goals: d.goals.length, sent });
|
|
287
287
|
console.log(sent ? `텔레그램 보고 채널로 보냈다 (${day} · 흡수 ${d.absorbed.length} · 골 후보 ${d.goals.length})` : '텔레그램 보고 채널 설정이 없다(telegram.reportChannel) — 보내지 않음');
|
|
288
288
|
if (!sent) process.exitCode = 3;
|
package/src/cli/mcp-call.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { resolveDaemonEndpoint, type ResolveDaemonEndpointOpts } from '../nexus/daemon-endpoint.js';
|
|
2
2
|
import { RemotesStore } from './remotes.js';
|
|
3
3
|
import { bookmarkAttachDefaults } from './remote-resolve.js';
|
|
4
|
+
import { getNodeKind } from '../graph-kinds/registry.js';
|
|
4
5
|
|
|
5
6
|
export type McpCliClassification =
|
|
6
7
|
| 'ok'
|
|
@@ -84,7 +85,15 @@ export async function callDaemonMcpTool(opts: {
|
|
|
84
85
|
arguments: Record<string, unknown>;
|
|
85
86
|
endpoint?: string | ResolvedEndpoint;
|
|
86
87
|
fetchFn?: McpCallOpts['fetchFn'];
|
|
88
|
+
pluginKind?: string;
|
|
87
89
|
}): Promise<unknown> {
|
|
90
|
+
if (opts.pluginKind) {
|
|
91
|
+
const entry = getNodeKind('workflow', opts.pluginKind);
|
|
92
|
+
if (!entry || entry.core || !entry.plugin || !entry.run || !('mcp' in entry.run)
|
|
93
|
+
|| opts.tool !== `${entry.run.mcp.server}.${entry.run.mcp.tool}`) {
|
|
94
|
+
throw new McpCliFailure('mcp-usage-error', 'invalid plugin MCP kind');
|
|
95
|
+
}
|
|
96
|
+
}
|
|
88
97
|
const endpoint = typeof opts.endpoint === 'string' ? { url: opts.endpoint } : opts.endpoint;
|
|
89
98
|
const resolved = endpoint ? { ok: true as const, value: endpoint } : resolveMcpEndpoint({});
|
|
90
99
|
if (!resolved.ok) throw new McpCliFailure(resolved.classification, resolved.message);
|