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
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { dirname, join } from 'node:path';
|
|
3
|
+
|
|
4
|
+
type Data = Record<string, unknown>;
|
|
5
|
+
const object = (v: unknown): Data => v !== null && typeof v === 'object' && !Array.isArray(v) ? v as Data : {};
|
|
6
|
+
const text = (v: unknown): string => typeof v === 'string' ? v.trim() : '';
|
|
7
|
+
const strings = (v: unknown): string[] => Array.isArray(v) ? v.filter((x): x is string => typeof x === 'string' && !!x.trim()) : [];
|
|
8
|
+
const rows = (v: unknown): Data[] => Array.isArray(v) ? v.map(object) : [];
|
|
9
|
+
const line = (v: unknown): string => text(v).replace(/[\r\n|]+/g, ' ');
|
|
10
|
+
const errorLine = (e: unknown): string => line(e instanceof Error ? e.message : String(e)).slice(0, 240) || 'unknown error';
|
|
11
|
+
try {
|
|
12
|
+
const contextPath = process.env.ELANOUS_GRAPH_CONTEXT ?? '';
|
|
13
|
+
const context = object(JSON.parse(readFileSync(contextPath, 'utf8')));
|
|
14
|
+
const input = object(context.input);
|
|
15
|
+
const outputs = object(context.outputs);
|
|
16
|
+
const stateDirectory = dirname(contextPath);
|
|
17
|
+
if (!stateDirectory.endsWith('.json.contexts')) throw new Error('invalid graph run context');
|
|
18
|
+
const defaultOutDir = stateDirectory.slice(0, -'.json.contexts'.length);
|
|
19
|
+
const brand = text(input.brand);
|
|
20
|
+
if (!brand) throw new Error('brand is required');
|
|
21
|
+
const engines = input.engines === undefined ? ['openai-codex', 'grok'] : strings(input.engines);
|
|
22
|
+
if (!engines.length || engines.length > 3 || engines.length !== new Set(engines).size || (input.engines !== undefined && (!Array.isArray(input.engines) || strings(input.engines).length !== input.engines.length))) throw new Error('engines must contain 1 to 3 distinct names');
|
|
23
|
+
const cli = process.env.GEO_CHECK_ELANOUS_BIN || 'elanous';
|
|
24
|
+
|
|
25
|
+
async function invoke(args: string[], engine?: string): Promise<unknown> {
|
|
26
|
+
const proc = Bun.spawn([cli, ...args], {
|
|
27
|
+
env: { ...process.env, ...(engine ? { ELANOUS_LLM_PROVIDER: engine } : {}) },
|
|
28
|
+
stdout: 'pipe', stderr: 'pipe',
|
|
29
|
+
});
|
|
30
|
+
const [stdout, stderr, exit] = await Promise.all([new Response(proc.stdout).text(), new Response(proc.stderr).text(), proc.exited]);
|
|
31
|
+
if (exit !== 0) throw new Error(line(stderr || stdout) || `CLI exited ${exit}`);
|
|
32
|
+
const last = stdout.trim().split('\n').at(-1);
|
|
33
|
+
if (!last) throw new Error('CLI returned no JSON');
|
|
34
|
+
return JSON.parse(last) as unknown;
|
|
35
|
+
}
|
|
36
|
+
function reply(raw: unknown): string {
|
|
37
|
+
const result = object(raw);
|
|
38
|
+
if (typeof result.reply !== 'string') throw new Error('ask returned no reply');
|
|
39
|
+
return result.reply;
|
|
40
|
+
}
|
|
41
|
+
function parseArray(raw: string): unknown {
|
|
42
|
+
const cleaned = raw.trim().replace(/^```(?:json)?\s*/i, '').replace(/\s*```$/, '');
|
|
43
|
+
return JSON.parse(cleaned) as unknown;
|
|
44
|
+
}
|
|
45
|
+
function domains(v: string): string {
|
|
46
|
+
const raw = v.trim().replace(/^https?:\/\//i, '').replace(/^www\./i, '').replace(/\/+$/, '');
|
|
47
|
+
return raw.split('/')[0]?.toLowerCase().replace(/:\d+$/, '') ?? '';
|
|
48
|
+
}
|
|
49
|
+
function urls(v: unknown): string[] {
|
|
50
|
+
if (Array.isArray(v)) return v.flatMap(urls);
|
|
51
|
+
if (typeof v === 'string') return [...v.matchAll(/https?:\/\/[^\s<>"')\]]+/gi)].map(m => m[0]!.replace(/[.,;!?]+$/, ''));
|
|
52
|
+
if (v && typeof v === 'object') return Object.entries(v).flatMap(([key, item]) => key === 'url' && typeof item === 'string' ? [item] : urls(item));
|
|
53
|
+
return [];
|
|
54
|
+
}
|
|
55
|
+
// `elanous research --json` = { query, output, metadata } — result links are `- [title](url)` lines in `output`.
|
|
56
|
+
// Only those links count; the query string or other fields may name the brand domain without it being a result.
|
|
57
|
+
function resultUrls(raw: unknown): string[] {
|
|
58
|
+
const out = object(raw).output;
|
|
59
|
+
if (typeof out !== 'string') return [];
|
|
60
|
+
return [...out.matchAll(/^\s*-\s*\[[^\]]*\]\((https?:\/\/[^)\s]+)\)/gm)].map(m => m[1]!);
|
|
61
|
+
}
|
|
62
|
+
// A bare domain in the answer text («see elanous.ai») is a citation too, not only an https:// link.
|
|
63
|
+
function mentionsHost(answer: string, host: string): boolean {
|
|
64
|
+
if (!host) return false;
|
|
65
|
+
const escaped = host.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
66
|
+
return new RegExp(`(^|[^a-z0-9.-])(?:www\\.)?${escaped}(?![a-z0-9-])`, 'i').test(answer);
|
|
67
|
+
}
|
|
68
|
+
function cited(url: string, host: string): boolean {
|
|
69
|
+
try {
|
|
70
|
+
const name = new URL(url).hostname.toLowerCase().replace(/^www\./, '');
|
|
71
|
+
return name === host || name.endsWith(`.${host}`);
|
|
72
|
+
} catch { return false; }
|
|
73
|
+
}
|
|
74
|
+
function firstMention(answer: string, name: string): number | null {
|
|
75
|
+
const lower = answer.toLocaleLowerCase();
|
|
76
|
+
const needle = name.toLocaleLowerCase();
|
|
77
|
+
let at = lower.indexOf(needle);
|
|
78
|
+
while (at >= 0) {
|
|
79
|
+
const before = at ? lower[at - 1]! : '';
|
|
80
|
+
const after = lower[at + needle.length] ?? '';
|
|
81
|
+
if (!/[\p{L}\p{N}_]/u.test(before) && !/[\p{L}\p{N}_]/u.test(after)) return at;
|
|
82
|
+
at = lower.indexOf(needle, at + 1);
|
|
83
|
+
}
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
function output(result: Data, calls = 0, errors = 0): void {
|
|
87
|
+
console.log(JSON.stringify({ ...result, calls, errors }));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async function run(step: string): Promise<void> {
|
|
91
|
+
if (step === 'questions') {
|
|
92
|
+
if (input.questions !== undefined && !Array.isArray(input.questions)) throw new Error('questions must be an array');
|
|
93
|
+
const provided = strings(input.questions);
|
|
94
|
+
if (provided.length > 8 || (Array.isArray(input.questions) && provided.length !== input.questions.length)) throw new Error('questions must contain at most 8 nonempty strings');
|
|
95
|
+
if (provided.length) { output({ questions: provided }); return; }
|
|
96
|
+
try {
|
|
97
|
+
const response = reply(await invoke(['ask', '--json', `Generate exactly 5 potential customer questions about the category of ${brand}. Respond ONLY with a JSON array of 5 question strings. Do not include the brand name in any question.`]));
|
|
98
|
+
const generated = parseArray(response);
|
|
99
|
+
if (!Array.isArray(generated) || generated.length !== 5 || strings(generated).length !== 5 || strings(generated).some(q => q.toLocaleLowerCase().includes(brand.toLocaleLowerCase()))) throw new Error('ask returned invalid or branded questions');
|
|
100
|
+
output({ questions: generated }, 1);
|
|
101
|
+
} catch (e) { output({ outcome: 'fail', questions: [], error: errorLine(e) }, 1, 1); }
|
|
102
|
+
} else if (step === 'answers') {
|
|
103
|
+
const questions = strings(object(outputs.questions).questions);
|
|
104
|
+
if (!questions.length || questions.length > 8) throw new Error('missing questions');
|
|
105
|
+
const answers: Data[] = [];
|
|
106
|
+
let failures = 0;
|
|
107
|
+
for (const question of questions) for (const engine of engines) {
|
|
108
|
+
try { answers.push({ question, engine, answer: reply(await invoke(['ask', '--json', question], engine)) }); }
|
|
109
|
+
catch (e) { failures++; answers.push({ question, engine, error: errorLine(e) }); }
|
|
110
|
+
}
|
|
111
|
+
output({ answers, outcome: failures === answers.length ? 'fail' : 'ok' }, answers.length, failures);
|
|
112
|
+
} else if (step === 'web') {
|
|
113
|
+
const questions = strings(object(outputs.questions).questions);
|
|
114
|
+
const web: Data[] = [];
|
|
115
|
+
let failures = 0;
|
|
116
|
+
for (const question of questions) {
|
|
117
|
+
try { web.push({ question, urls: resultUrls(await invoke(['research', '--json', '--limit', '5', question])).slice(0, 5) }); }
|
|
118
|
+
catch (e) { failures++; web.push({ question, urls: [], error: errorLine(e) }); }
|
|
119
|
+
}
|
|
120
|
+
output({ web }, questions.length, failures);
|
|
121
|
+
} else if (step === 'score') {
|
|
122
|
+
const answers = rows(object(outputs.answers).answers);
|
|
123
|
+
const web = rows(object(outputs.web).web);
|
|
124
|
+
if (!answers.length) throw new Error('missing answers');
|
|
125
|
+
const domain = domains(text(input.domain));
|
|
126
|
+
const table = answers.map(cell => {
|
|
127
|
+
const answer = text(cell.answer);
|
|
128
|
+
if (cell.error) return { question: cell.question, engine: cell.engine, error: cell.error, mention: false, citation: domain ? false : null, rank: null };
|
|
129
|
+
const position = firstMention(answer, brand);
|
|
130
|
+
const candidates = [...urls(answer), ...strings(web.find(row => row.question === cell.question)?.urls)];
|
|
131
|
+
return { question: cell.question, engine: cell.engine, mention: position !== null,
|
|
132
|
+
citation: domain ? candidates.some(url => cited(url, domain)) || mentionsHost(answer, domain) : null, rank: position === null ? null : answer.slice(0, position).trim().split(/\s+/).filter(Boolean).length + 1 };
|
|
133
|
+
});
|
|
134
|
+
output({ table }, 0, table.filter(cell => cell.error).length);
|
|
135
|
+
} else if (step === 'suggest') {
|
|
136
|
+
const table = rows(object(outputs.score).table);
|
|
137
|
+
if (!table.length) throw new Error('missing score table');
|
|
138
|
+
const gaps = table.filter(cell => cell.error || !cell.mention || cell.citation === false).map(cell => ({ question: cell.question, engine: cell.engine, error: cell.error ?? null, mention: cell.mention, citation: cell.citation }));
|
|
139
|
+
if (!gaps.length) { output({ suggestions: [] }); return; }
|
|
140
|
+
try {
|
|
141
|
+
const raw = parseArray(reply(await invoke(['ask', '--json', `Using ONLY this score table, return a JSON array of 3 to 5 improvement suggestions. Each must have "suggestion" (FAQ question, FAQPage/Organization structured data, comparison document, etc.) and "evidence" containing a question and engine from the missing cells. Evidence cells: ${JSON.stringify(gaps)}. Score table: ${JSON.stringify(table)}`])));
|
|
142
|
+
if (!Array.isArray(raw) || raw.length < 3 || raw.length > 5) throw new Error('invalid suggestion count');
|
|
143
|
+
const suggestions = raw.map(item => {
|
|
144
|
+
const candidate = object(item);
|
|
145
|
+
const evidence = object(candidate.evidence);
|
|
146
|
+
const match = gaps.find(cell => cell.question === evidence.question && cell.engine === evidence.engine);
|
|
147
|
+
if (!text(candidate.suggestion) || !match) throw new Error('suggestion missing valid score evidence');
|
|
148
|
+
return { suggestion: text(candidate.suggestion), evidence: { question: match.question, engine: match.engine } };
|
|
149
|
+
});
|
|
150
|
+
output({ suggestions }, 1);
|
|
151
|
+
} catch (e) { output({ outcome: 'fail', suggestions: [], error: errorLine(e) }, 1, 1); }
|
|
152
|
+
} else if (step === 'report') {
|
|
153
|
+
const table = rows(object(outputs.score).table);
|
|
154
|
+
const answers = rows(object(outputs.answers).answers);
|
|
155
|
+
const suggestions = rows(object(outputs.suggest).suggestions);
|
|
156
|
+
if (!table.length) throw new Error('missing score table');
|
|
157
|
+
const outDir = text(input.outDir) || defaultOutDir;
|
|
158
|
+
const success = table.filter(cell => !cell.error);
|
|
159
|
+
const citationSummary = text(input.domain) ? `${success.filter(cell => cell.citation).length}/${success.length} cite ${text(input.domain)}.` : 'Citation unknown (no domain provided).';
|
|
160
|
+
const summary = `${brand}: ${success.filter(cell => cell.mention).length}/${success.length} available answers mention the brand; ${citationSummary}`;
|
|
161
|
+
const markdown = [`# GEO check — ${line(brand)}`, '', summary, '', '| Question | Engine | Mention | Citation | Rank | Error |', '| --- | --- | --- | --- | --- | --- |',
|
|
162
|
+
...table.map(cell => `| ${line(cell.question)} | ${line(cell.engine)} | ${cell.mention ? 'yes' : 'no'} | ${cell.citation === null ? 'unknown' : cell.citation ? 'yes' : 'no'} | ${cell.rank ?? '—'} | ${line(cell.error)} |`),
|
|
163
|
+
'', '## Suggestions', ...suggestions.map(item => `- ${line(item.suggestion)} — ${line(object(item.evidence).question)} / ${line(object(item.evidence).engine)}`),
|
|
164
|
+
'', '## Answers', ...answers.map(cell => `### ${line(cell.question)} — ${line(cell.engine)}\n\n${line(cell.error) || text(cell.answer).slice(0, 400)}\n`), ''].join('\n');
|
|
165
|
+
mkdirSync(outDir, { recursive: true });
|
|
166
|
+
const md = join(outDir, 'report.md');
|
|
167
|
+
const json = join(outDir, 'report.json');
|
|
168
|
+
writeFileSync(md, markdown, { mode: 0o600 });
|
|
169
|
+
writeFileSync(json, JSON.stringify({ brand, domain: text(input.domain), summary, table, suggestions, answers }, null, 2) + '\n', { mode: 0o600 });
|
|
170
|
+
output({ report: md, reportJson: json, summary }, 0, table.filter(cell => cell.error).length);
|
|
171
|
+
} else throw new Error(`unknown geo-check step: ${step}`);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
await run(process.argv[2] ?? '');
|
|
175
|
+
} catch (e) {
|
|
176
|
+
console.log(JSON.stringify({ outcome: 'fail', error: errorLine(e), calls: 0, errors: 1 }));
|
|
177
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://antigravity.google/schemas/v1/plugin.json",
|
|
3
|
+
"name": "geo-check",
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"description": "Measure brand mentions and citations in AI answers and web results.",
|
|
6
|
+
"extensions": {
|
|
7
|
+
"ai.elanous": {
|
|
8
|
+
"graphs": [
|
|
9
|
+
"./graphs/geo-check.yaml"
|
|
10
|
+
],
|
|
11
|
+
"capabilities": [
|
|
12
|
+
"fs:workdir",
|
|
13
|
+
"proc:bun",
|
|
14
|
+
"proc:elanous"
|
|
15
|
+
],
|
|
16
|
+
"connectors": [],
|
|
17
|
+
"pricing": {
|
|
18
|
+
"model": "free"
|
|
19
|
+
},
|
|
20
|
+
"trust": "official",
|
|
21
|
+
"category": "Marketing"
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -2,5 +2,22 @@
|
|
|
2
2
|
"name": "job-coach",
|
|
3
3
|
"version": "0.1.0",
|
|
4
4
|
"description": "Interview-to-NCS competency gap and cited course report.",
|
|
5
|
-
"skills": "./skills/"
|
|
5
|
+
"skills": "./skills/",
|
|
6
|
+
"contributes": {
|
|
7
|
+
"skills": [
|
|
8
|
+
{ "name": "interview-to-profile" },
|
|
9
|
+
{ "name": "career-report" },
|
|
10
|
+
{ "name": "run-report" },
|
|
11
|
+
{ "name": "enterprise-needs" },
|
|
12
|
+
{ "name": "enterprise-task-analysis" },
|
|
13
|
+
{ "name": "enterprise-ai-fit" },
|
|
14
|
+
{ "name": "enterprise-competency" },
|
|
15
|
+
{ "name": "enterprise-roadmap" }
|
|
16
|
+
]
|
|
17
|
+
},
|
|
18
|
+
"extensions": {
|
|
19
|
+
"ai.elanous": {
|
|
20
|
+
"graphs": ["./graphs/report.yaml", "./graphs/report-enterprise.yaml"]
|
|
21
|
+
}
|
|
22
|
+
}
|
|
6
23
|
}
|
|
@@ -2,6 +2,48 @@
|
|
|
2
2
|
|
|
3
3
|
**S** 인터뷰에서 직무 후보를 추출해 공공데이터포털의 NCS 능력단위와 비교하고, 외부 코스 조사로 보고서를 작성한다. **C** 실제 NCS 키와 코스 출처가 없으면 매칭·추천을 지어낼 수 없다. **Q** 이 패키지를 받은 사용자가 어떻게 실행하고 결과를 확인하는가? **A** 아래 실행 방법과 검증 방법을 따른다.
|
|
4
4
|
|
|
5
|
+
## 개인 모드 · 기업 HRD 모드
|
|
6
|
+
|
|
7
|
+
개인 모드(`report.yaml`)는 `## 희망 직무`, `## 경험`, `## 보유 역량` 인터뷰를 읽고 직무 후보 → NCS 매칭 → 검증된 과정 조사 → 자기보고 역량 갭 → 개인 보고서로 진행한다. NCS 키가 없으면 실패하며 확인된 과정 URL이 없으면 추천을 `미확인`으로 표시한다. 개인 보고서의 다섯 섹션과 기존 `research` 재시도 계약은 유지한다.
|
|
8
|
+
|
|
9
|
+
기업 HRD 모드(`report-enterprise.yaml`)는 `## 기업 개요`, `## 교육 대상`, `## 현업 과제`, `## 교육 목표`, `## 현재 역량`, `## 교육 제약`의 인터뷰를 받는다. 가상 회사·가상 직원으로 입력을 시험하고 실제 직원 정보나 내부 정책을 예시 파일에 싣지 않는다. 같은 NCS 조회를 재사용해 니즈 → 현업 과제 → NCS → AI 적합성(비AI 대안 포함) → 역량 → 로드맵 → 교육 명세와 운영 경로를 만든 뒤 일곱 섹션을 판정한다. 섹션 또는 근거가 누락되면 보고 단계로 한 번 되돌리고 다시 판정한다. 인터뷰 진술은 자기보고이고, NCS 코드·과정/URL·사내 정책 및 AI 도입 승인은 조회·확인 전에는 `미확인`이다.
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
elanous graph run /absolute/path/to/installed/job-coach/graphs/report-enterprise.yaml --input '{"interview":"/private/path/to/fictional-enterprise.md"}' --json
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
### 기업 모드 샘플 입력 (가상 정보만)
|
|
16
|
+
|
|
17
|
+
다음은 **가상 인물** 민서 하와 **가상 회사** 푸른달 스튜디오의 예시다. 이미지 경로는 실제 사진이 아닌 플레이스홀더이며, 인터뷰 안의 참고용 표기일 뿐이다. 그래프 입력 필드는 `interview` 하나로, 이미지 파일을 읽거나 전송하는 입력은 없다. 비공개 작업 디렉터리에 `fictional-enterprise.md`로 저장한 뒤 위 명령의 `interview` 경로를 그 파일의 절대 경로로 바꾼다.
|
|
18
|
+
|
|
19
|
+
```markdown
|
|
20
|
+
## 기업 개요
|
|
21
|
+
푸른달 스튜디오 (가상 회사). 이미지 참고 경로: /placeholder/images/fictional-company.png
|
|
22
|
+
|
|
23
|
+
## 교육 대상
|
|
24
|
+
민서 하 (가상 인물), 문서 기획 담당자
|
|
25
|
+
|
|
26
|
+
## 현업 과제
|
|
27
|
+
가상 행사 안내 문서의 기획
|
|
28
|
+
|
|
29
|
+
## 교육 목표
|
|
30
|
+
행사 안내 문서를 요구사항에 맞게 기획한다
|
|
31
|
+
|
|
32
|
+
## 현재 역량
|
|
33
|
+
문서 작성 경험 (자기보고)
|
|
34
|
+
|
|
35
|
+
## 교육 제약
|
|
36
|
+
예산과 승인 여부 미확인
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### 입력·출력과 프라이버시 경계
|
|
40
|
+
|
|
41
|
+
입력은 기업 모드 인터뷰 Markdown **파일의 비공개 절대 경로**를 담은 `--input '{"interview":"/private/path/to/fictional-enterprise.md"}'`이다. 위 여섯 `##` 섹션이 필요하다. 실제 직원 이름·사진·인사정보·사내 기밀을 샘플, 명령 인자 또는 공개 저장소에 넣지 않는다. 실제 인터뷰를 사용할 경우에도 민감 정보를 제거하고 접근 제한된 저장소에서만 실행한다. 그래프 러너는 입력·노드 stdout을 런 상태와 컨텍스트 JSON에 저장하므로 **파일 경로만 인자로 넘겨도 보고서와 런 상태에는 인터뷰에서 파생된 직무·역량이 남는다**. 파일 경로가 개인정보 비노출이나 무저장을 보장하는 것은 아니다.
|
|
42
|
+
|
|
43
|
+
출력은 CLI의 상태 JSON(`statePath`와 `done`/`failed`) 및 그 상태 경로와 같은 디렉터리의 `<run-id>/report.md`다. 보고서는 아래 일곱 섹션으로 구성된다. NCS 조회가 실패하면 `failed`로 끝나며 확인되지 않은 과정·URL 및 내부 승인 여부는 `미확인`으로 남긴다. **No-send boundary:** 이 그래프 실행은 보고서를 로컬에 작성하는 경로이며 메신저·이메일로 보고서를 보내거나 마켓에 게시하는 단계가 없다. `--no-send` 플래그를 지원한다고 가정하지 않는다. 다만 NCS API 조회 및 외부 과정 조사는 네트워크 요청이므로 오프라인·무외부전송 실행이 아니다. 보고서 배포나 마켓 게시/시장 출시는 이 플러그인 README의 범위 밖이다.
|
|
44
|
+
|
|
45
|
+
기업 보고서는 `기업 개요 및 교육 대상`, `교육 니즈`, `직무·과제 및 NCS`, `AI 적합성`, `역량 진단`, `교육 로드맵`, `교육 명세·운영 경로` 순서다. NCS 조회에 실패하면 매칭을 발명하지 않고 런을 실패로 종료한다. `run-report` 스킬은 개인 보고서 진입이며 기업 모드는 다섯 개 기업 스킬(`enterprise-needs`, `enterprise-task-analysis`, `enterprise-ai-fit`, `enterprise-competency`, `enterprise-roadmap`)과 기업 그래프를 이용한다.
|
|
46
|
+
|
|
5
47
|
## 실행
|
|
6
48
|
|
|
7
49
|
공공데이터포털 [한국산업인력공단 NCS 기준정보 조회](https://www.data.go.kr/data/15128213/openapi.do) 활용신청 후 키를 **환경변수 `NCS_SERVICE_KEY`** 로 설정한다. 설치 시 `credentials` 이벤트의 `ncs.serviceKey` 에 입력하는 경로는 플러그인 설치기가 담당한다. 저장소에는 키를 넣지 않는다. `.mcp.json`은 MCP stdio 서버를 번들로 제공한다. 레시피는 러너가 제공하는 `ELANOUS_GRAPH_DIR`을 기준으로 번들 스크립트를 찾고, MCP의 `${CODEX_PLUGIN_ROOT}`는 Codex가 설치한 플러그인의 절대 경로로 치환한다. 런타임에는 `bun`과 PATH의 `elanous research --json`가 필요하다. 중첩 elanous 호출에는 `--test`를 붙여 격리한다. 인터뷰 파일은 `## 희망 직무`, `## 경험`, `## 보유 역량`을 포함해야 한다.
|
|
@@ -8,7 +8,7 @@ const schema = (properties: Record<string, unknown>, required: string[]) => ({ t
|
|
|
8
8
|
const text = { type: 'string' };
|
|
9
9
|
export const tools = [
|
|
10
10
|
{ name: 'ncs_search_units', description: 'Search NCS competency units by keyword (NCS007).', inputSchema: schema({ keyword: text }, ['keyword']) },
|
|
11
|
-
{ name: 'ncs_unit', description: 'Look up a competency unit by
|
|
11
|
+
{ name: 'ncs_unit', description: 'Look up a competency unit by its NCS_COMPE_UNIT_CD (NCS005).', inputSchema: schema({ code: text }, ['code']) },
|
|
12
12
|
{ name: 'ncs_classification', description: 'List NCS classifications at major, middle, minor or detailed level (NCS001–004). Parent codes are required below major.', inputSchema: schema({ level: { type: 'string', enum: ['major', 'middle', 'minor', 'detailed'] }, code: text }, ['level']) },
|
|
13
13
|
];
|
|
14
14
|
|
|
@@ -31,9 +31,10 @@ export async function callNcsTool(name: string, args: Arguments, options: { key?
|
|
|
31
31
|
} else if (name === 'ncs_unit') {
|
|
32
32
|
operation = 'NCS005';
|
|
33
33
|
const code = required(args.code, 'code');
|
|
34
|
-
if (!/^\d{8,}$/.test(code)) throw new Error('code must contain the complete numeric NCS
|
|
34
|
+
if (!/^\d{8,}$/.test(code)) throw new Error('code must contain the complete numeric NCS competency unit code');
|
|
35
35
|
for (const [field, start, end] of [['NCS_LCLAS_CD', 0, 2], ['NCS_MCLAS_CD', 2, 4], ['NCS_SCLAS_CD', 4, 6], ['NCS_SUBD_CD', 6, 8]] as const) params.set(field, code.slice(start, end));
|
|
36
|
-
params.set('NCS_CL_CD', code);
|
|
36
|
+
params.set('NCS_CL_CD', code.slice(0, 8));
|
|
37
|
+
params.set('NCS_COMPE_UNIT_CD', code);
|
|
37
38
|
} else if (name === 'ncs_classification') {
|
|
38
39
|
const levels = { major: 1, middle: 2, minor: 3, detailed: 4 } as const;
|
|
39
40
|
if (typeof args.level !== 'string' || !Object.hasOwn(levels, args.level)) throw new Error('level must be major, middle, minor or detailed');
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export type GapUnit = { code: string; name: string; definition: string; level: string; elements: { name: string; criteria: string }[] };
|
|
2
|
+
export type GapVerdict = { code: string; name: string; status: '보유' | '부분' | '갭'; quote: string };
|
|
3
|
+
|
|
4
|
+
type Judge = (prompt: string) => Promise<string>;
|
|
5
|
+
export const MAX_GAP_PROMPT_CHARS = 12000;
|
|
6
|
+
|
|
7
|
+
export function gapPrompt(units: GapUnit[], interview: string): string {
|
|
8
|
+
return [
|
|
9
|
+
'인터뷰 원문과 공식 NCS 능력단위의 능력단위요소·수행준거·수준을 대조한다.',
|
|
10
|
+
'각 단위별 보유(수행준거를 충족하는 구체적 경험), 부분(일부만 입증), 갭(근거 없음) 중 하나를 판정한다.',
|
|
11
|
+
'단위 이름·보유 역량 목록만으로 보유를 추정하지 말고 원문에서 정확한 한 문장을 quote로 그대로 인용한다.',
|
|
12
|
+
'반드시 JSON 객체 {"gaps":[{"code":"...","status":"보유|부분|갭","quote":"..."}]}만 출력한다. 모든 code를 각각 한 번씩 포함한다.',
|
|
13
|
+
'NCS 단위:', JSON.stringify(units), '인터뷰 원문:', interview,
|
|
14
|
+
].join('\n');
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function completeSentence(interview: string, quote: string): boolean {
|
|
18
|
+
if (!quote || /[\r\n]/.test(quote) || !/[.!?。!?]$/.test(quote) || /[.!?。!?]\s*\S/.test(quote.slice(0, -1))) return false;
|
|
19
|
+
let from = 0;
|
|
20
|
+
while (true) {
|
|
21
|
+
const index = interview.indexOf(quote, from);
|
|
22
|
+
if (index < 0) return false;
|
|
23
|
+
const before = interview.slice(0, index);
|
|
24
|
+
if (/(?:^|[.!?。!?]|\n)[ \t]*(?:[-*][ \t]+)?$/.test(before) && !/[\p{L}\p{N}]/u.test(interview[index + quote.length] ?? '')) return true;
|
|
25
|
+
from = index + 1;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export async function judgeGaps(units: GapUnit[], interview: string, judge: Judge): Promise<GapVerdict[]> {
|
|
30
|
+
const prompt = gapPrompt(units, interview);
|
|
31
|
+
if (prompt.length > MAX_GAP_PROMPT_CHARS) throw new Error('gap prompt exceeds input limit; interview or NCS detail is too long');
|
|
32
|
+
const answer = await judge(prompt);
|
|
33
|
+
let parsed: unknown;
|
|
34
|
+
try { parsed = JSON.parse(answer.replace(/^```(?:json)?\s*|\s*```$/g, '').trim()); } catch { parsed = {}; }
|
|
35
|
+
const rows = (parsed && typeof parsed === 'object' && 'gaps' in parsed && Array.isArray(parsed.gaps)) ? parsed.gaps : [];
|
|
36
|
+
return units.map(unit => {
|
|
37
|
+
const matches = rows.filter(row => row && typeof row === 'object' && row.code === unit.code);
|
|
38
|
+
const row = matches.length === 1 ? matches[0] as { status?: unknown; quote?: unknown } : {};
|
|
39
|
+
const quote = typeof row.quote === 'string' ? row.quote.trim() : '';
|
|
40
|
+
const isSentence = completeSentence(interview, quote);
|
|
41
|
+
const status = isSentence && (row.status === '보유' || row.status === '부분') ? row.status : '갭';
|
|
42
|
+
return { code: unit.code, name: unit.name, status, quote: isSentence ? quote : '' };
|
|
43
|
+
});
|
|
44
|
+
}
|
|
@@ -16,3 +16,30 @@ report:
|
|
|
16
16
|
judge:
|
|
17
17
|
command: 'bun "$ELANOUS_GRAPH_DIR/run-step.ts" judge'
|
|
18
18
|
timeout_ms: 30000
|
|
19
|
+
intake:
|
|
20
|
+
command: 'bun "$ELANOUS_GRAPH_DIR/run-step.ts" intake'
|
|
21
|
+
timeout_ms: 30000
|
|
22
|
+
needs:
|
|
23
|
+
command: 'bun "$ELANOUS_GRAPH_DIR/run-step.ts" needs'
|
|
24
|
+
timeout_ms: 30000
|
|
25
|
+
task:
|
|
26
|
+
command: 'bun "$ELANOUS_GRAPH_DIR/run-step.ts" task'
|
|
27
|
+
timeout_ms: 30000
|
|
28
|
+
ai-fit:
|
|
29
|
+
command: 'bun "$ELANOUS_GRAPH_DIR/run-step.ts" ai-fit'
|
|
30
|
+
timeout_ms: 30000
|
|
31
|
+
competency:
|
|
32
|
+
command: 'bun "$ELANOUS_GRAPH_DIR/run-step.ts" competency'
|
|
33
|
+
timeout_ms: 30000
|
|
34
|
+
roadmap:
|
|
35
|
+
command: 'bun "$ELANOUS_GRAPH_DIR/run-step.ts" roadmap'
|
|
36
|
+
timeout_ms: 30000
|
|
37
|
+
spec:
|
|
38
|
+
command: 'bun "$ELANOUS_GRAPH_DIR/run-step.ts" spec'
|
|
39
|
+
timeout_ms: 30000
|
|
40
|
+
routing:
|
|
41
|
+
command: 'bun "$ELANOUS_GRAPH_DIR/run-step.ts" routing'
|
|
42
|
+
timeout_ms: 30000
|
|
43
|
+
judge-enterprise:
|
|
44
|
+
command: 'bun "$ELANOUS_GRAPH_DIR/run-step.ts" judge-enterprise'
|
|
45
|
+
timeout_ms: 30000
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
graph_id: job-coach-report-enterprise
|
|
2
|
+
version: 1
|
|
3
|
+
entry_node: intake
|
|
4
|
+
terminal_nodes: [done, failed]
|
|
5
|
+
nodes:
|
|
6
|
+
- { node_id: intake, kind: agent, recipe: 'cmd:intake', max_visits: 1 }
|
|
7
|
+
- { node_id: needs, kind: agent, recipe: 'cmd:needs', max_visits: 1 }
|
|
8
|
+
- { node_id: task, kind: agent, recipe: 'cmd:task', max_visits: 1 }
|
|
9
|
+
- { node_id: ncs-match, kind: agent, recipe: 'cmd:ncs-match', max_visits: 1 }
|
|
10
|
+
- { node_id: ai-fit, kind: agent, recipe: 'cmd:ai-fit', max_visits: 2 }
|
|
11
|
+
- { node_id: competency, kind: agent, recipe: 'cmd:competency', max_visits: 2 }
|
|
12
|
+
- { node_id: roadmap, kind: agent, recipe: 'cmd:roadmap', max_visits: 2 }
|
|
13
|
+
- { node_id: spec, kind: agent, recipe: 'cmd:spec', max_visits: 2 }
|
|
14
|
+
- { node_id: routing, kind: agent, recipe: 'cmd:routing', max_visits: 2 }
|
|
15
|
+
- { node_id: judge, kind: judge, recipe: 'cmd:judge-enterprise', max_visits: 2 }
|
|
16
|
+
- { node_id: done, kind: gate, max_visits: 1 }
|
|
17
|
+
- { node_id: failed, kind: gate, max_visits: 1 }
|
|
18
|
+
edges:
|
|
19
|
+
- { from: intake, on: outcome, map: { ok: needs, fail: failed } }
|
|
20
|
+
- { from: needs, on: outcome, map: { ok: task, fail: failed } }
|
|
21
|
+
- { from: task, on: outcome, map: { ok: ncs-match, fail: failed } }
|
|
22
|
+
- { from: ncs-match, on: outcome, map: { ok: ai-fit, fail: failed } }
|
|
23
|
+
- { from: ai-fit, on: outcome, map: { ok: competency, fail: failed } }
|
|
24
|
+
- { from: competency, on: outcome, map: { ok: roadmap, fail: failed } }
|
|
25
|
+
- { from: roadmap, on: outcome, map: { ok: spec, fail: failed } }
|
|
26
|
+
- { from: spec, on: outcome, map: { ok: routing, fail: failed } }
|
|
27
|
+
- { from: routing, on: outcome, map: { ok: judge, fail: failed } }
|
|
28
|
+
- { from: judge, on: outcome, map: { ok: done, retry: ai-fit, fail: failed } }
|