@crouton-kit/crouter 0.3.31 → 0.3.33
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 +8 -0
- package/dist/build-root.js +1 -0
- package/dist/builtin-memory/crouter-development/marketplaces.md +2 -2
- package/dist/builtin-memory/crouter-development/personas/base-prompt.md +2 -2
- package/dist/builtin-memory/crouter-development/personas/orchestrator-prompt.md +5 -5
- package/dist/builtin-memory/crouter-development/personas.md +3 -4
- package/dist/builtin-memory/crouter-development/plugins.md +18 -18
- package/dist/builtin-memory/design.md +4 -1
- package/dist/builtin-memory/development.md +4 -1
- package/dist/builtin-memory/internal/INDEX.md +1 -1
- package/dist/builtin-memory/internal/nodes-and-canvas.md +6 -6
- package/dist/builtin-memory/internal/storage-tiers.md +5 -5
- package/dist/builtin-memory/planning.md +4 -1
- package/dist/builtin-memory/product.md +80 -0
- package/dist/builtin-memory/spec.md +4 -1
- package/dist/builtin-personas/advisor/PERSONA.md +10 -0
- package/dist/builtin-personas/design/PERSONA.md +2 -2
- package/dist/builtin-personas/design/orchestrator.md +3 -3
- package/dist/builtin-personas/developer/PERSONA.md +1 -1
- package/dist/builtin-personas/developer/orchestrator.md +2 -2
- package/dist/builtin-personas/explore/PERSONA.md +3 -3
- package/dist/builtin-personas/explore/orchestrator.md +4 -2
- package/dist/builtin-personas/general/PERSONA.md +1 -1
- package/dist/builtin-personas/general/orchestrator.md +1 -1
- package/dist/builtin-personas/orchestration-kernel.md +7 -14
- package/dist/builtin-personas/plan/PERSONA.md +1 -1
- package/dist/builtin-personas/plan/orchestrator.md +2 -2
- package/dist/builtin-personas/plan/reviewers/architecture-fit/PERSONA.md +1 -1
- package/dist/builtin-personas/plan/reviewers/code-smells/PERSONA.md +1 -1
- package/dist/builtin-personas/plan/reviewers/pattern-consistency/PERSONA.md +1 -1
- package/dist/builtin-personas/plan/reviewers/requirements-coverage/PERSONA.md +1 -1
- package/dist/builtin-personas/plan/reviewers/security/PERSONA.md +1 -1
- package/dist/builtin-personas/product/PERSONA.md +18 -0
- package/dist/builtin-personas/product/orchestrator.md +14 -0
- package/dist/builtin-personas/product/teardown/PERSONA.md +13 -0
- package/dist/builtin-personas/review/PERSONA.md +1 -1
- package/dist/builtin-personas/review/orchestrator.md +1 -1
- package/dist/builtin-personas/runtime-base.md +5 -0
- package/dist/builtin-personas/spec/PERSONA.md +1 -1
- package/dist/builtin-personas/spec/orchestrator.md +2 -2
- package/dist/builtin-personas/spec/requirements/PERSONA.md +1 -1
- package/dist/builtin-views/canvas/core.mjs +82 -1
- package/dist/builtin-views/canvas/tui.mjs +9 -5
- package/dist/builtin-views/canvas/web.jsx +3 -2
- package/dist/builtin-views/chat/core.mjs +725 -0
- package/dist/builtin-views/chat/text.mjs +101 -0
- package/dist/builtin-views/chat/tui.mjs +368 -0
- package/dist/builtin-views/chat/web.jsx +367 -0
- package/dist/builtin-views/prompt-review/core.mjs +863 -0
- package/dist/builtin-views/prompt-review/text.mjs +15 -0
- package/dist/builtin-views/prompt-review/tui.mjs +196 -0
- package/dist/builtin-views/prompt-review/web.jsx +484 -0
- package/dist/builtin-views/settings/core.mjs +397 -0
- package/dist/builtin-views/settings/text.mjs +40 -0
- package/dist/builtin-views/settings/tui.mjs +95 -0
- package/dist/builtin-views/settings/web.jsx +167 -0
- package/dist/cli.js +16 -3
- package/dist/clients/attach/__tests__/autocomplete-and-bash-mode.test.js +96 -0
- package/dist/clients/attach/__tests__/bash-bang-routing.test.js +58 -0
- package/dist/clients/attach/__tests__/bundle-pi-tui-dedup.test.d.ts +1 -0
- package/dist/clients/attach/__tests__/bundle-pi-tui-dedup.test.js +56 -0
- package/dist/clients/attach/__tests__/titled-editor.test.js +22 -1
- package/dist/clients/attach/attach-cmd.d.ts +18 -0
- package/dist/clients/attach/attach-cmd.js +1723 -795
- package/dist/clients/attach/canvas-panels.js +2 -2
- package/dist/clients/attach/chat-view.d.ts +10 -0
- package/dist/clients/attach/chat-view.js +23 -0
- package/dist/clients/attach/config-load.d.ts +8 -4
- package/dist/clients/attach/config-load.js +2 -0
- package/dist/clients/attach/extension-dialogs.d.ts +2 -3
- package/dist/clients/attach/extension-dialogs.js +7 -9
- package/dist/clients/attach/graph-overlay.js +3 -2
- package/dist/clients/attach/input-controller.d.ts +4 -3
- package/dist/clients/attach/input-controller.js +24 -3
- package/dist/clients/attach/slash-commands.d.ts +7 -7
- package/dist/clients/attach/slash-commands.js +38 -14
- package/dist/clients/attach/titled-editor.js +18 -14
- package/dist/clients/attach/view-socket.d.ts +2 -1
- package/dist/clients/attach/view-socket.js +27 -8
- package/dist/clients/web/__tests__/source-cache.test.d.ts +1 -0
- package/dist/clients/web/__tests__/source-cache.test.js +32 -0
- package/dist/clients/web/dev-server.js +1 -0
- package/dist/clients/web/events.js +9 -11
- package/dist/clients/web/server.d.ts +4 -3
- package/dist/clients/web/server.js +138 -31
- package/dist/clients/web/source-cache.d.ts +10 -0
- package/dist/clients/web/source-cache.js +57 -0
- package/dist/clients/web/web-cmd.js +28 -9
- package/dist/commands/__tests__/human.test.js +30 -28
- package/dist/commands/canvas-history/search.js +1 -1
- package/dist/commands/canvas-history.js +5 -8
- package/dist/commands/canvas-issue.d.ts +2 -0
- package/dist/commands/canvas-issue.js +148 -0
- package/dist/commands/canvas-prune.js +19 -0
- package/dist/commands/canvas-rebuild-index.d.ts +2 -0
- package/dist/commands/canvas-rebuild-index.js +57 -0
- package/dist/commands/canvas-snapshot.d.ts +2 -0
- package/dist/commands/canvas-snapshot.js +48 -0
- package/dist/commands/canvas-tmux-spread.d.ts +2 -0
- package/dist/commands/canvas-tmux-spread.js +188 -0
- package/dist/commands/canvas.d.ts +1 -0
- package/dist/commands/canvas.js +16 -1
- package/dist/commands/chord.js +143 -48
- package/dist/commands/daemon.js +3 -3
- package/dist/commands/human/prompts.d.ts +0 -1
- package/dist/commands/human/prompts.js +39 -54
- package/dist/commands/human/queue.d.ts +12 -1
- package/dist/commands/human/queue.js +468 -73
- package/dist/commands/human/shared.d.ts +3 -4
- package/dist/commands/human/shared.js +3 -3
- package/dist/commands/human.js +11 -10
- package/dist/commands/memory/find.js +3 -2
- package/dist/commands/memory/list.js +4 -3
- package/dist/commands/memory/read.js +3 -3
- package/dist/commands/memory/shared.d.ts +1 -4
- package/dist/commands/memory/shared.js +6 -9
- package/dist/commands/memory/write.js +3 -3
- package/dist/commands/memory.js +4 -7
- package/dist/commands/node-context.d.ts +2 -0
- package/dist/commands/node-context.js +172 -0
- package/dist/commands/node-snapshot.d.ts +2 -0
- package/dist/commands/node-snapshot.js +123 -0
- package/dist/commands/node.js +228 -52
- package/dist/commands/pkg/plugin-inspect.js +6 -6
- package/dist/commands/pkg/plugin-manage.js +1 -1
- package/dist/commands/pkg/plugin.js +2 -2
- package/dist/commands/pkg.js +2 -2
- package/dist/commands/push.d.ts +2 -0
- package/dist/commands/push.js +79 -11
- package/dist/commands/revive.js +74 -1
- package/dist/commands/search/answer.d.ts +1 -0
- package/dist/commands/search/answer.js +50 -0
- package/dist/commands/search/contents.d.ts +1 -0
- package/dist/commands/search/contents.js +96 -0
- package/dist/commands/search/exa.d.ts +53 -0
- package/dist/commands/search/exa.js +155 -0
- package/dist/commands/search/puremd.d.ts +11 -0
- package/dist/commands/search/puremd.js +53 -0
- package/dist/commands/search/web.d.ts +1 -0
- package/dist/commands/search/web.js +65 -0
- package/dist/commands/search.d.ts +2 -0
- package/dist/commands/search.js +24 -0
- package/dist/commands/sys/__tests__/sync-import.test.d.ts +1 -0
- package/dist/commands/sys/__tests__/sync-import.test.js +72 -0
- package/dist/commands/sys/config.js +59 -10
- package/dist/commands/sys/doctor.js +56 -4
- package/dist/commands/sys/prompt-review.d.ts +1 -0
- package/dist/commands/sys/prompt-review.js +178 -0
- package/dist/commands/sys/promptstudio.d.ts +2 -0
- package/dist/commands/sys/promptstudio.js +65 -0
- package/dist/commands/sys/settings.d.ts +2 -0
- package/dist/commands/sys/settings.js +16 -0
- package/dist/commands/sys/sync.js +311 -159
- package/dist/commands/sys/sysprompt.d.ts +1 -0
- package/dist/commands/sys/sysprompt.js +86 -0
- package/dist/commands/sys.js +9 -5
- package/dist/commands/view-new.js +2 -2
- package/dist/core/__tests__/artifact-paths.test.d.ts +1 -0
- package/dist/core/__tests__/artifact-paths.test.js +44 -0
- package/dist/core/__tests__/broker-preflight.test.d.ts +1 -0
- package/dist/core/__tests__/broker-preflight.test.js +85 -0
- package/dist/core/__tests__/broker-sdk-wiring.test.js +15 -0
- package/dist/core/__tests__/canvas.test.js +37 -1
- package/dist/core/__tests__/chat-view-reconnect.test.d.ts +1 -0
- package/dist/core/__tests__/chat-view-reconnect.test.js +70 -0
- package/dist/core/__tests__/child-death-wake.test.js +11 -2
- package/dist/core/__tests__/close.test.js +39 -1
- package/dist/core/__tests__/connection-reconnect-resume.test.d.ts +1 -0
- package/dist/core/__tests__/connection-reconnect-resume.test.js +37 -0
- package/dist/core/__tests__/context-intro.test.js +48 -14
- package/dist/core/__tests__/daemon-boot.test.js +182 -9
- package/dist/core/__tests__/editor-label.test.d.ts +1 -0
- package/dist/core/__tests__/editor-label.test.js +26 -0
- package/dist/core/__tests__/fault-marker.test.d.ts +1 -0
- package/dist/core/__tests__/fault-marker.test.js +112 -0
- package/dist/core/__tests__/fault-retry-rewind.test.d.ts +1 -0
- package/dist/core/__tests__/fault-retry-rewind.test.js +123 -0
- package/dist/core/__tests__/fixtures/fake-engine.d.ts +30 -4
- package/dist/core/__tests__/fixtures/fake-engine.js +156 -17
- package/dist/core/__tests__/fixtures/fake-pi-host.js +10 -1
- package/dist/core/__tests__/full/human-new-window-regression.test.js +1 -1
- package/dist/core/__tests__/helpers/harness.d.ts +1 -1
- package/dist/core/__tests__/helpers/harness.js +9 -3
- package/dist/core/__tests__/human-stranded-deliver.test.js +38 -1
- package/dist/core/__tests__/human-surface-target.test.js +1 -1
- package/dist/core/__tests__/kickoff.test.js +18 -19
- package/dist/core/__tests__/memory-resolver.test.js +12 -1
- package/dist/core/__tests__/migration.test.js +49 -14
- package/dist/core/__tests__/model-ladders.test.d.ts +1 -0
- package/dist/core/__tests__/model-ladders.test.js +160 -0
- package/dist/core/__tests__/node-env.test.d.ts +1 -0
- package/dist/core/__tests__/node-env.test.js +26 -0
- package/dist/core/__tests__/push-final-guard.test.js +7 -1
- package/dist/core/__tests__/relaunch-root.test.js +9 -9
- package/dist/core/__tests__/revive.test.js +36 -6
- package/dist/core/__tests__/spawn-root.test.js +24 -1
- package/dist/core/__tests__/stop-guard.test.js +10 -0
- package/dist/core/__tests__/stranded-relaunch.test.d.ts +1 -0
- package/dist/core/__tests__/stranded-relaunch.test.js +72 -0
- package/dist/core/__tests__/tmux-surface.test.js +5 -1
- package/dist/core/__tests__/unknown-path.test.js +19 -19
- package/dist/core/__tests__/yield-ensures-daemon.test.d.ts +1 -0
- package/dist/core/__tests__/yield-ensures-daemon.test.js +54 -0
- package/dist/core/artifact.d.ts +2 -33
- package/dist/core/artifact.js +27 -87
- package/dist/core/bootstrap.js +2 -0
- package/dist/core/canvas/browse/__tests__/model.test.js +35 -1
- package/dist/core/canvas/browse/app.js +87 -17
- package/dist/core/canvas/browse/model.d.ts +10 -1
- package/dist/core/canvas/browse/model.js +37 -1
- package/dist/core/canvas/browse/render.js +35 -33
- package/dist/core/canvas/canvas.d.ts +7 -0
- package/dist/core/canvas/canvas.js +54 -5
- package/dist/core/canvas/db.js +35 -8
- package/dist/core/canvas/labels.d.ts +6 -8
- package/dist/core/canvas/labels.js +8 -11
- package/dist/core/canvas/nav-model.d.ts +5 -4
- package/dist/core/canvas/nav-model.js +31 -17
- package/dist/core/canvas/paths.d.ts +18 -1
- package/dist/core/canvas/paths.js +37 -2
- package/dist/core/canvas/render.d.ts +25 -0
- package/dist/core/canvas/render.js +96 -13
- package/dist/core/canvas/status-glyph.d.ts +35 -0
- package/dist/core/canvas/status-glyph.js +104 -0
- package/dist/core/canvas/types.d.ts +2 -2
- package/dist/core/config.js +114 -16
- package/dist/core/fault-classifier.d.ts +41 -0
- package/dist/core/fault-classifier.js +110 -0
- package/dist/core/feed/__tests__/empty-node-guard.test.d.ts +1 -0
- package/dist/core/feed/__tests__/empty-node-guard.test.js +45 -0
- package/dist/core/feed/feed.js +8 -0
- package/dist/core/feed/inbox.d.ts +15 -0
- package/dist/core/feed/inbox.js +40 -14
- package/dist/core/frontmatter.d.ts +1 -11
- package/dist/core/frontmatter.js +1 -55
- package/dist/core/hearth/config.d.ts +2 -0
- package/dist/core/hearth/config.js +75 -0
- package/dist/core/hearth/guest-env.d.ts +2 -0
- package/dist/core/hearth/guest-env.js +11 -0
- package/dist/core/hearth/index.d.ts +5 -0
- package/dist/core/hearth/index.js +5 -0
- package/dist/core/hearth/provider.d.ts +21 -0
- package/dist/core/hearth/provider.js +10 -0
- package/dist/core/hearth/providers/blaxel-bootstrap.d.ts +10 -0
- package/dist/core/hearth/providers/blaxel-bootstrap.js +39 -0
- package/dist/core/hearth/providers/blaxel-home.d.ts +11 -0
- package/dist/core/hearth/providers/blaxel-home.js +126 -0
- package/dist/core/hearth/providers/blaxel.d.ts +26 -0
- package/dist/core/hearth/providers/blaxel.js +208 -0
- package/dist/core/hearth/providers/types.d.ts +72 -0
- package/dist/core/hearth/providers/types.js +1 -0
- package/dist/core/hearth/registry.d.ts +15 -0
- package/dist/core/hearth/registry.js +179 -0
- package/dist/core/hearth/types.d.ts +121 -0
- package/dist/core/hearth/types.js +1 -0
- package/dist/core/host-exports/__tests__/export-prunes-boot-skill.test.d.ts +1 -0
- package/dist/core/host-exports/__tests__/export-prunes-boot-skill.test.js +79 -0
- package/dist/core/{skill-sync → host-exports}/builtins.d.ts +21 -13
- package/dist/core/host-exports/builtins.js +68 -0
- package/dist/core/{skill-sync → host-exports}/export.d.ts +9 -6
- package/dist/core/{skill-sync → host-exports}/export.js +37 -14
- package/dist/core/io.js +1 -1
- package/dist/core/log.d.ts +9 -0
- package/dist/core/log.js +113 -0
- package/dist/core/memory-resolver.d.ts +8 -8
- package/dist/core/memory-resolver.js +15 -28
- package/dist/core/personas/index.d.ts +4 -4
- package/dist/core/personas/index.js +2 -2
- package/dist/core/personas/loader.d.ts +51 -0
- package/dist/core/personas/loader.js +54 -50
- package/dist/core/personas/resolve.d.ts +43 -27
- package/dist/core/personas/resolve.js +336 -94
- package/dist/core/runtime/auth-reload.d.ts +7 -0
- package/dist/core/runtime/auth-reload.js +69 -0
- package/dist/core/runtime/bearings.d.ts +35 -15
- package/dist/core/runtime/bearings.js +305 -44
- package/dist/core/runtime/branded-host.d.ts +4 -2
- package/dist/core/runtime/branded-host.js +66 -4
- package/dist/core/runtime/broker-protocol.d.ts +38 -3
- package/dist/core/runtime/broker.js +343 -18
- package/dist/core/runtime/close.js +37 -29
- package/dist/core/runtime/connectivity.d.ts +12 -0
- package/dist/core/runtime/connectivity.js +73 -0
- package/dist/core/runtime/fault-recovery-nudge.d.ts +2 -0
- package/dist/core/runtime/fault-recovery-nudge.js +2 -0
- package/dist/core/runtime/fault-recovery.d.ts +23 -0
- package/dist/core/runtime/fault-recovery.js +74 -0
- package/dist/core/runtime/fault.d.ts +25 -0
- package/dist/core/runtime/fault.js +176 -0
- package/dist/core/runtime/front-door.d.ts +1 -1
- package/dist/core/runtime/front-door.js +2 -2
- package/dist/core/runtime/host.d.ts +10 -0
- package/dist/core/runtime/host.js +71 -9
- package/dist/core/runtime/kickoff.js +31 -45
- package/dist/core/runtime/launch.d.ts +32 -18
- package/dist/core/runtime/launch.js +148 -43
- package/dist/core/runtime/model-swap.d.ts +18 -0
- package/dist/core/runtime/model-swap.js +95 -0
- package/dist/core/runtime/naming.d.ts +7 -0
- package/dist/core/runtime/naming.js +61 -9
- package/dist/core/runtime/nodes.js +6 -1
- package/dist/core/runtime/persona.js +16 -17
- package/dist/core/runtime/placement.d.ts +36 -31
- package/dist/core/runtime/placement.js +149 -64
- package/dist/core/runtime/promote.d.ts +2 -0
- package/dist/core/runtime/promote.js +23 -3
- package/dist/core/runtime/recycle.js +6 -4
- package/dist/core/runtime/reset.d.ts +5 -2
- package/dist/core/runtime/reset.js +2 -2
- package/dist/core/runtime/revive.d.ts +4 -0
- package/dist/core/runtime/revive.js +27 -9
- package/dist/core/runtime/roadmap.d.ts +1 -1
- package/dist/core/runtime/roadmap.js +1 -1
- package/dist/core/runtime/spawn.d.ts +2 -2
- package/dist/core/runtime/spawn.js +30 -6
- package/dist/core/runtime/stop-guard.js +6 -1
- package/dist/core/runtime/tmux-chrome.d.ts +1 -1
- package/dist/core/runtime/tmux-chrome.js +1 -1
- package/dist/core/runtime/tmux.d.ts +29 -6
- package/dist/core/runtime/tmux.js +134 -80
- package/dist/core/runtime/view-socket-client.d.ts +46 -0
- package/dist/core/runtime/view-socket-client.js +203 -0
- package/dist/core/substrate/index.d.ts +1 -1
- package/dist/core/substrate/index.js +1 -1
- package/dist/core/substrate/on-read.js +16 -4
- package/dist/core/substrate/render.d.ts +10 -13
- package/dist/core/substrate/render.js +56 -51
- package/dist/core/tui/host.js +83 -18
- package/dist/core/view/__tests__/transport-cache.test.d.ts +1 -0
- package/dist/core/view/__tests__/transport-cache.test.js +62 -0
- package/dist/core/view/contract.d.ts +20 -0
- package/dist/core/view/stream-local.d.ts +3 -0
- package/dist/core/view/stream-local.js +184 -0
- package/dist/core/view/transport-cache.d.ts +8 -0
- package/dist/core/view/transport-cache.js +38 -0
- package/dist/core/view/transport-local.js +4 -0
- package/dist/core/view/transport.d.ts +7 -1
- package/dist/daemon/crtrd.d.ts +3 -19
- package/dist/daemon/crtrd.js +139 -178
- package/dist/daemon/manage.d.ts +18 -1
- package/dist/daemon/manage.js +54 -9
- package/dist/hearth/wake-proxy/__tests__/anthropic-oauth.test.d.ts +1 -0
- package/dist/hearth/wake-proxy/__tests__/anthropic-oauth.test.js +289 -0
- package/dist/hearth/wake-proxy/__tests__/config-timeout.test.d.ts +1 -0
- package/dist/hearth/wake-proxy/__tests__/config-timeout.test.js +34 -0
- package/dist/hearth/wake-proxy/__tests__/guest-source.test.d.ts +1 -0
- package/dist/hearth/wake-proxy/__tests__/guest-source.test.js +203 -0
- package/dist/hearth/wake-proxy/__tests__/hardening.test.d.ts +1 -0
- package/dist/hearth/wake-proxy/__tests__/hardening.test.js +59 -0
- package/dist/hearth/wake-proxy/__tests__/hearth-status-route.test.d.ts +1 -0
- package/dist/hearth/wake-proxy/__tests__/hearth-status-route.test.js +372 -0
- package/dist/hearth/wake-proxy/__tests__/http-running-route-fast-path.test.d.ts +1 -0
- package/dist/hearth/wake-proxy/__tests__/http-running-route-fast-path.test.js +258 -0
- package/dist/hearth/wake-proxy/__tests__/model-auth-routes.test.d.ts +1 -0
- package/dist/hearth/wake-proxy/__tests__/model-auth-routes.test.js +437 -0
- package/dist/hearth/wake-proxy/__tests__/source-bridge-gate.test.d.ts +1 -0
- package/dist/hearth/wake-proxy/__tests__/source-bridge-gate.test.js +455 -0
- package/dist/hearth/wake-proxy/__tests__/static-asset-allowlist.test.d.ts +1 -0
- package/dist/hearth/wake-proxy/__tests__/static-asset-allowlist.test.js +15 -0
- package/dist/hearth/wake-proxy/__tests__/warm-path-concurrency.test.d.ts +1 -0
- package/dist/hearth/wake-proxy/__tests__/warm-path-concurrency.test.js +141 -0
- package/dist/hearth/wake-proxy/__tests__/ws-teardown-accounting.test.d.ts +1 -0
- package/dist/hearth/wake-proxy/__tests__/ws-teardown-accounting.test.js +143 -0
- package/dist/hearth/wake-proxy/anthropic-oauth.d.ts +58 -0
- package/dist/hearth/wake-proxy/anthropic-oauth.js +189 -0
- package/dist/hearth/wake-proxy/auth.d.ts +22 -0
- package/dist/hearth/wake-proxy/auth.js +128 -0
- package/dist/hearth/wake-proxy/config.d.ts +2 -0
- package/dist/hearth/wake-proxy/config.js +151 -0
- package/dist/hearth/wake-proxy/guest-source.d.ts +14 -0
- package/dist/hearth/wake-proxy/guest-source.js +130 -0
- package/dist/hearth/wake-proxy/hearth-status.d.ts +44 -0
- package/dist/hearth/wake-proxy/hearth-status.js +267 -0
- package/dist/hearth/wake-proxy/home.d.ts +67 -0
- package/dist/hearth/wake-proxy/home.js +297 -0
- package/dist/hearth/wake-proxy/main.d.ts +1 -0
- package/dist/hearth/wake-proxy/main.js +134 -0
- package/dist/hearth/wake-proxy/model-auth.d.ts +13 -0
- package/dist/hearth/wake-proxy/model-auth.js +345 -0
- package/dist/hearth/wake-proxy/proxy.d.ts +35 -0
- package/dist/hearth/wake-proxy/proxy.js +716 -0
- package/dist/hearth/wake-proxy/public-source-gate.d.ts +9 -0
- package/dist/hearth/wake-proxy/public-source-gate.js +409 -0
- package/dist/hearth/wake-proxy/redact.d.ts +1 -0
- package/dist/hearth/wake-proxy/redact.js +17 -0
- package/dist/hearth/wake-proxy/server.d.ts +11 -0
- package/dist/hearth/wake-proxy/server.js +142 -0
- package/dist/hearth/wake-proxy/state.d.ts +18 -0
- package/dist/hearth/wake-proxy/state.js +342 -0
- package/dist/hearth/wake-proxy/types.d.ts +76 -0
- package/dist/hearth/wake-proxy/types.js +1 -0
- package/dist/index.js +3 -2
- package/dist/pi-extensions/__tests__/canvas-context-intro.test.js +6 -5
- package/dist/pi-extensions/canvas-context-intro.js +7 -7
- package/dist/pi-extensions/canvas-doc-substrate.js +19 -22
- package/dist/pi-extensions/canvas-nav.js +15 -8
- package/dist/pi-extensions/canvas-stophook.d.ts +1 -1
- package/dist/pi-extensions/canvas-stophook.js +78 -11
- package/dist/types.d.ts +39 -20
- package/dist/types.js +38 -13
- package/dist/web/runtime.js +141 -42
- package/dist/web/transport-http.js +7 -5
- package/dist/web/transport-stream.d.ts +3 -0
- package/dist/web/transport-stream.js +204 -0
- package/dist/web-client/assets/fragment-mono-latin-400-normal-BYwT3kSJ.woff +0 -0
- package/dist/web-client/assets/fragment-mono-latin-400-normal-yxdJ5AmL.woff2 +0 -0
- package/dist/web-client/assets/fragment-mono-latin-ext-400-normal-BbKYyvR9.woff2 +0 -0
- package/dist/web-client/assets/fragment-mono-latin-ext-400-normal-CT4YFKeK.woff +0 -0
- package/dist/web-client/assets/fraunces-latin-ext-wght-normal-Ca2vKHc0.woff2 +0 -0
- package/dist/web-client/assets/fraunces-latin-wght-normal-ukD16Tqj.woff2 +0 -0
- package/dist/web-client/assets/fraunces-vietnamese-wght-normal-CnvboYUG.woff2 +0 -0
- package/dist/web-client/assets/index-BZUxTkv5.css +2 -0
- package/dist/web-client/assets/index-D36PNBj4.js +80 -0
- package/dist/web-client/assets/instrument-sans-latin-ext-wght-normal-B5bTHO_g.woff2 +0 -0
- package/dist/web-client/assets/instrument-sans-latin-wght-normal-BbzFLZTg.woff2 +0 -0
- package/dist/web-client/assets/martian-mono-cyrillic-wght-normal-B84CD5C_.woff2 +0 -0
- package/dist/web-client/assets/martian-mono-latin-ext-wght-normal-DlL6xMw5.woff2 +0 -0
- package/dist/web-client/assets/martian-mono-latin-wght-normal-5W32yIyr.woff2 +0 -0
- package/dist/web-client/index.html +3 -2
- package/package.json +32 -10
- package/dist/core/__tests__/error-stall-recycle.test.js +0 -141
- package/dist/core/skill-sync/__tests__/dry-run-wrote-count.test.js +0 -57
- package/dist/core/skill-sync/builtins.js +0 -112
- package/dist/core/skill-sync/claude-plugins.d.ts +0 -23
- package/dist/core/skill-sync/claude-plugins.js +0 -71
- package/dist/core/skill-sync/engine.d.ts +0 -42
- package/dist/core/skill-sync/engine.js +0 -633
- package/dist/core/skill-sync/manifest.d.ts +0 -64
- package/dist/core/skill-sync/manifest.js +0 -181
- package/dist/core/skill-sync/profile.d.ts +0 -76
- package/dist/core/skill-sync/profile.js +0 -173
- package/dist/core/skill-sync/snapshot.d.ts +0 -57
- package/dist/core/skill-sync/snapshot.js +0 -120
- package/dist/pi-extensions/canvas-commands.d.ts +0 -37
- package/dist/pi-extensions/canvas-commands.js +0 -113
- package/dist/pi-extensions/canvas-resume.d.ts +0 -21
- package/dist/pi-extensions/canvas-resume.js +0 -83
- package/dist/pi-extensions/canvas-view.d.ts +0 -21
- package/dist/pi-extensions/canvas-view.js +0 -76
- package/dist/web-client/assets/index-BUvQb4hR.css +0 -2
- package/dist/web-client/assets/index-ClLQXYAE.js +0 -10
- /package/dist/{core/__tests__/error-stall-recycle.test.d.ts → clients/attach/__tests__/autocomplete-and-bash-mode.test.d.ts} +0 -0
- /package/dist/{core/skill-sync/__tests__/dry-run-wrote-count.test.d.ts → clients/attach/__tests__/bash-bang-routing.test.d.ts} +0 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// Fallback content fetcher: when Exa cannot fetch a URL, we re-fetch it through
|
|
2
|
+
// pure.md (https://pure.md), which renders any page to clean markdown. This is
|
|
3
|
+
// the ONLY module that talks to pure.md. API-key resolution is optional — the
|
|
4
|
+
// service works keyless (rate-limited); a key raises limits.
|
|
5
|
+
import { join } from 'node:path';
|
|
6
|
+
import { readFileSync, existsSync } from 'node:fs';
|
|
7
|
+
import { userScopeRoot } from '../../core/scope.js';
|
|
8
|
+
const PUREMD_BASE = 'https://pure.md';
|
|
9
|
+
/** Resolve an optional pure.md API token: PUREMD_API_KEY env first, then
|
|
10
|
+
* ~/.crouter/puremd.key. Returns undefined when neither is set — pure.md is
|
|
11
|
+
* usable keyless, so a missing token is not an error. */
|
|
12
|
+
function getApiKey() {
|
|
13
|
+
const env = process.env['PUREMD_API_KEY'];
|
|
14
|
+
if (env !== undefined && env.trim() !== '')
|
|
15
|
+
return env.trim();
|
|
16
|
+
const keyFile = join(userScopeRoot(), 'puremd.key');
|
|
17
|
+
if (existsSync(keyFile)) {
|
|
18
|
+
const fromFile = readFileSync(keyFile, 'utf8').trim();
|
|
19
|
+
if (fromFile !== '')
|
|
20
|
+
return fromFile;
|
|
21
|
+
}
|
|
22
|
+
return undefined;
|
|
23
|
+
}
|
|
24
|
+
/** Fetch a single URL's content as markdown via pure.md, capped at maxChars.
|
|
25
|
+
* Never throws — any transport or HTTP failure becomes { ok: false, reason }
|
|
26
|
+
* so the caller can keep the URL in its failures list. */
|
|
27
|
+
export async function puremdFetch(url, maxChars) {
|
|
28
|
+
const headers = {};
|
|
29
|
+
const key = getApiKey();
|
|
30
|
+
if (key !== undefined)
|
|
31
|
+
headers['x-puremd-api-token'] = key;
|
|
32
|
+
let res;
|
|
33
|
+
try {
|
|
34
|
+
res = await fetch(`${PUREMD_BASE}/${url}`, { headers });
|
|
35
|
+
}
|
|
36
|
+
catch (err) {
|
|
37
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
38
|
+
return { ok: false, reason: `pure.md request failed: ${msg}` };
|
|
39
|
+
}
|
|
40
|
+
if (!res.ok) {
|
|
41
|
+
return { ok: false, reason: `pure.md returned ${res.status} ${res.statusText}` };
|
|
42
|
+
}
|
|
43
|
+
let text;
|
|
44
|
+
try {
|
|
45
|
+
text = (await res.text()).trim();
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return { ok: false, reason: 'pure.md response body unreadable' };
|
|
49
|
+
}
|
|
50
|
+
if (text === '')
|
|
51
|
+
return { ok: false, reason: 'pure.md returned empty content' };
|
|
52
|
+
return { ok: true, text: text.slice(0, maxChars) };
|
|
53
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const webLeaf: import("../../core/command.js").LeafDef;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { defineLeaf } from '../../core/command.js';
|
|
2
|
+
import { exaSearch, parseDomains, renderResult, TEXT_MAX_CHARACTERS, } from './exa.js';
|
|
3
|
+
const SEARCH_TYPES = ['auto', 'fast', 'instant', 'deep-lite', 'deep', 'deep-reasoning'];
|
|
4
|
+
export const webLeaf = defineLeaf({
|
|
5
|
+
name: 'web',
|
|
6
|
+
description: 'find web pages relevant to a query, with highlight excerpts',
|
|
7
|
+
whenToUse: 'you need to discover web pages relevant to a query and read query-matched excerpts from them — the default web search. Use this for open-ended research, finding sources, or gathering current information. Reach for `answer` instead when you want one synthesized, cited answer to a specific question rather than a ranked list of pages; reach for `contents` when you already hold the URLs and only need their content.',
|
|
8
|
+
help: {
|
|
9
|
+
name: 'search web',
|
|
10
|
+
summary: 'web search via Exa — ranked results with query-relevant highlight excerpts (or full text)',
|
|
11
|
+
params: [
|
|
12
|
+
{ kind: 'positional', name: 'query', required: true, constraint: 'The search query. Natural language; be specific.' },
|
|
13
|
+
{ kind: 'flag', name: 'type', type: 'enum', choices: [...SEARCH_TYPES], required: false, default: 'auto', constraint: 'Search depth. auto balances relevance and speed; fast/instant trade depth for latency; deep-lite/deep/deep-reasoning run multi-query expansion and rank the combined set for harder synthesis.' },
|
|
14
|
+
{ kind: 'flag', name: 'num', type: 'int', required: false, default: 10, constraint: 'Number of results to return.' },
|
|
15
|
+
{ kind: 'flag', name: 'text', type: 'bool', required: false, constraint: `Return cleaned full page text (capped at ${TEXT_MAX_CHARACTERS} characters per result) instead of highlight excerpts. Off by default; highlights keep token usage predictable.` },
|
|
16
|
+
{ kind: 'flag', name: 'include-domains', type: 'string', required: false, constraint: 'Comma-separated domain allowlist — restrict results to these domains.' },
|
|
17
|
+
{ kind: 'flag', name: 'exclude-domains', type: 'string', required: false, constraint: 'Comma-separated domain blocklist — drop results from these domains.' },
|
|
18
|
+
],
|
|
19
|
+
output: [
|
|
20
|
+
{ name: 'query', type: 'string', required: true, constraint: 'Echo of the query searched.' },
|
|
21
|
+
{ name: 'results', type: 'object[]', required: true, constraint: 'Ranked best-first, each: title, url, published date and author when present, and either highlight excerpts (default) or capped full text (--text).' },
|
|
22
|
+
{ name: 'follow_up', type: 'string', required: true, constraint: 'Concrete next command — fetch full content of a result with `crtr search contents`, or refine the query.' },
|
|
23
|
+
],
|
|
24
|
+
outputKind: 'object',
|
|
25
|
+
effects: ['Sends one search request to the Exa API (network). No local state changes.'],
|
|
26
|
+
},
|
|
27
|
+
run: async (input) => {
|
|
28
|
+
const query = input['query'];
|
|
29
|
+
const type = input['type'];
|
|
30
|
+
const num = input['num'];
|
|
31
|
+
const wantText = input['text'];
|
|
32
|
+
const contents = wantText
|
|
33
|
+
? { text: { maxCharacters: TEXT_MAX_CHARACTERS } }
|
|
34
|
+
: { highlights: true };
|
|
35
|
+
const body = { query, type, numResults: num, contents };
|
|
36
|
+
const include = parseDomains(input['includeDomains']);
|
|
37
|
+
const exclude = parseDomains(input['excludeDomains']);
|
|
38
|
+
if (include !== undefined)
|
|
39
|
+
body['includeDomains'] = include;
|
|
40
|
+
if (exclude !== undefined)
|
|
41
|
+
body['excludeDomains'] = exclude;
|
|
42
|
+
const res = await exaSearch(body);
|
|
43
|
+
const results = res.results ?? [];
|
|
44
|
+
return {
|
|
45
|
+
query,
|
|
46
|
+
results,
|
|
47
|
+
follow_up: 'Fetch the full content of any result with `crtr search contents <url>`. No good hits? Broaden the query, drop domain filters, or try --type deep.',
|
|
48
|
+
};
|
|
49
|
+
},
|
|
50
|
+
render: (result) => {
|
|
51
|
+
const query = result['query'];
|
|
52
|
+
const results = result['results'];
|
|
53
|
+
const followUp = result['follow_up'];
|
|
54
|
+
if (results.length === 0) {
|
|
55
|
+
return `No results for "${query}".\n\n${followUp}`;
|
|
56
|
+
}
|
|
57
|
+
const lines = [`${results.length} results for "${query}":`, ''];
|
|
58
|
+
results.forEach((r, i) => {
|
|
59
|
+
lines.push(renderResult(r, i + 1));
|
|
60
|
+
lines.push('');
|
|
61
|
+
});
|
|
62
|
+
lines.push(followUp);
|
|
63
|
+
return lines.join('\n');
|
|
64
|
+
},
|
|
65
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// `crtr search` subtree — web search for agents, backed by the Exa API. Three
|
|
2
|
+
// leaves: web (find pages), answer (one grounded cited answer), contents
|
|
3
|
+
// (extract from URLs you already have). API key resolves from EXA_API_KEY or
|
|
4
|
+
// ~/.crouter/exa.key (see ./search/exa.ts).
|
|
5
|
+
import { defineBranch } from '../core/command.js';
|
|
6
|
+
import { webLeaf } from './search/web.js';
|
|
7
|
+
import { answerLeaf } from './search/answer.js';
|
|
8
|
+
import { contentsLeaf } from './search/contents.js';
|
|
9
|
+
export function registerSearch() {
|
|
10
|
+
return defineBranch({
|
|
11
|
+
name: 'search',
|
|
12
|
+
rootEntry: {
|
|
13
|
+
concept: 'web search for agents — find pages, get grounded answers, extract page content (Exa)',
|
|
14
|
+
desc: 'search the web, answer a question with citations, or extract content from known URLs',
|
|
15
|
+
useWhen: 'you need information from the live web — current events, documentation, sources, facts beyond your training. `crtr search web "<query>"` finds relevant pages with excerpts; `crtr search answer "<question>"` returns one synthesized, cited answer; `crtr search contents <url>` extracts clean content from URLs you already hold. Needs an Exa API key (EXA_API_KEY or ~/.crouter/exa.key).',
|
|
16
|
+
},
|
|
17
|
+
help: {
|
|
18
|
+
name: 'search',
|
|
19
|
+
summary: 'web search via the Exa API — find pages, answer questions with citations, extract page content',
|
|
20
|
+
model: 'Three leaves split by what you have and what you want. `web` is the default: a query in, ranked pages out with query-relevant highlight excerpts (or full text with --text) — use it for discovery and research. `answer` collapses a question into one synthesized, source-cited answer — use it when you want the conclusion, not a reading list. `contents` does no searching; it extracts cleaned content from URLs you already hold. Highlights are the default content mode everywhere (token-predictable); full text is opt-in and length-capped. Every leaf needs an Exa API key from EXA_API_KEY or ~/.crouter/exa.key.',
|
|
21
|
+
},
|
|
22
|
+
children: [webLeaf, answerLeaf, contentsLeaf],
|
|
23
|
+
});
|
|
24
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// Regression: `crtr sys sync` is the one-shot migration path from legacy
|
|
2
|
+
// Agent Skill bundles into crouter memory docs. The hard cut removes SKILL.md as
|
|
3
|
+
// an active guidance surface, but must keep the converter.
|
|
4
|
+
//
|
|
5
|
+
// Run: node --import tsx/esm --test src/commands/sys/__tests__/sync-import.test.ts
|
|
6
|
+
import { afterEach, beforeEach, test } from 'node:test';
|
|
7
|
+
import assert from 'node:assert/strict';
|
|
8
|
+
import { mkdtempSync, mkdirSync, rmSync, writeFileSync, readFileSync, existsSync } from 'node:fs';
|
|
9
|
+
import { tmpdir } from 'node:os';
|
|
10
|
+
import { join } from 'node:path';
|
|
11
|
+
import { sysSyncLeaf } from '../sync.js';
|
|
12
|
+
import { parseFrontmatterGeneric } from '../../../core/frontmatter.js';
|
|
13
|
+
let home;
|
|
14
|
+
let prevHome;
|
|
15
|
+
beforeEach(() => {
|
|
16
|
+
prevHome = process.env.HOME;
|
|
17
|
+
home = mkdtempSync(join(tmpdir(), 'crtr-sys-sync-'));
|
|
18
|
+
process.env.HOME = home;
|
|
19
|
+
});
|
|
20
|
+
afterEach(() => {
|
|
21
|
+
if (prevHome === undefined)
|
|
22
|
+
delete process.env.HOME;
|
|
23
|
+
else
|
|
24
|
+
process.env.HOME = prevHome;
|
|
25
|
+
rmSync(home, { recursive: true, force: true });
|
|
26
|
+
});
|
|
27
|
+
test('imports a SKILL.md bundle as a plain crouter memory doc', async () => {
|
|
28
|
+
const sourceRoot = join(home, '.claude', 'skills');
|
|
29
|
+
const bundle = join(sourceRoot, 'demo-skill');
|
|
30
|
+
mkdirSync(bundle, { recursive: true });
|
|
31
|
+
writeFileSync(join(bundle, 'SKILL.md'), '---\nname: Demo Skill\ndescription: Handles demos. Use when demo work appears.\n---\n\n# Demo\n\nDo the demo.\n', 'utf8');
|
|
32
|
+
const result = await sysSyncLeaf.run({ source: sourceRoot, scope: 'user' });
|
|
33
|
+
assert.equal(result?.imported, 1);
|
|
34
|
+
const target = join(home, '.crouter', 'memory', 'demo-skill.md');
|
|
35
|
+
assert.equal(existsSync(target), true);
|
|
36
|
+
const parsed = parseFrontmatterGeneric(readFileSync(target, 'utf8'));
|
|
37
|
+
assert.equal(parsed.data?.kind, 'knowledge');
|
|
38
|
+
assert.equal(parsed.data?.['system-prompt-visibility'], 'preview');
|
|
39
|
+
assert.equal(parsed.data?.['file-read-visibility'], 'none');
|
|
40
|
+
assert.equal(parsed.data?.['when-and-why-to-read'], 'When demo work appears, this knowledge should be read because handles demos.');
|
|
41
|
+
assert.match(parsed.body, /Do the demo\./);
|
|
42
|
+
});
|
|
43
|
+
test('skips generated crtr boot skills instead of importing them', async () => {
|
|
44
|
+
const bundle = join(home, '.pi', 'agent', 'skills', 'crtr-skills');
|
|
45
|
+
mkdirSync(bundle, { recursive: true });
|
|
46
|
+
writeFileSync(join(bundle, 'SKILL.md'), '---\nname: crtr-skills\n---\n\n<!-- crtr-boot-skill v2 -->\n', 'utf8');
|
|
47
|
+
const result = await sysSyncLeaf.run({ source: bundle, scope: 'user' });
|
|
48
|
+
assert.equal(result?.imported, 0);
|
|
49
|
+
assert.equal(result?.skipped, 1);
|
|
50
|
+
assert.equal(existsSync(join(home, '.crouter', 'memory', 'crtr-skills.md')), false);
|
|
51
|
+
});
|
|
52
|
+
test('permanently ignores selected SKILL.md bundles', async () => {
|
|
53
|
+
const sourceRoot = join(home, '.claude', 'skills');
|
|
54
|
+
const bundle = join(sourceRoot, 'ignored-skill');
|
|
55
|
+
mkdirSync(bundle, { recursive: true });
|
|
56
|
+
writeFileSync(join(bundle, 'SKILL.md'), '---\nname: Ignored Skill\ndescription: Ignore me. Use when testing ignores.\n---\n\n# Ignore\n', 'utf8');
|
|
57
|
+
const ignored = await sysSyncLeaf.run({ source: sourceRoot, scope: 'user', ignore: true });
|
|
58
|
+
assert.equal(ignored?.ignored, 1);
|
|
59
|
+
assert.equal(existsSync(join(home, '.crouter', 'skill-import-ignore.json')), true);
|
|
60
|
+
const dry = await sysSyncLeaf.run({ source: sourceRoot, scope: 'user', dryRun: true });
|
|
61
|
+
assert.equal(dry?.wouldImport, 0);
|
|
62
|
+
assert.equal((dry?.results).length, 0);
|
|
63
|
+
const shown = await sysSyncLeaf.run({ source: sourceRoot, scope: 'user', dryRun: true, includeIgnored: true });
|
|
64
|
+
assert.equal(shown?.ignored, 1);
|
|
65
|
+
assert.equal((shown?.results)[0].status, 'ignored');
|
|
66
|
+
const wouldIgnore = await sysSyncLeaf.run({ source: sourceRoot, scope: 'user', dryRun: true, ignore: true });
|
|
67
|
+
assert.equal(wouldIgnore?.wouldIgnore, 1);
|
|
68
|
+
assert.equal((wouldIgnore?.results)[0].status, 'would-ignore');
|
|
69
|
+
const rendered = sysSyncLeaf.render?.(wouldIgnore);
|
|
70
|
+
assert.notEqual(rendered, undefined);
|
|
71
|
+
assert.match(rendered, /would ignore/);
|
|
72
|
+
});
|
|
@@ -12,7 +12,17 @@ const TOP_LEVEL_KEYS = new Set([
|
|
|
12
12
|
'plugins',
|
|
13
13
|
'max_panes_per_window',
|
|
14
14
|
'canvasNav',
|
|
15
|
+
'modelLadders',
|
|
16
|
+
'personaStrengths',
|
|
15
17
|
]);
|
|
18
|
+
const MODEL_PROVIDERS = new Set(['anthropic', 'openai']);
|
|
19
|
+
const MODEL_STRENGTHS = new Set(['ultra', 'strong', 'medium', 'light']);
|
|
20
|
+
function isModelProvider(value) {
|
|
21
|
+
return MODEL_PROVIDERS.has(value);
|
|
22
|
+
}
|
|
23
|
+
function isModelStrength(value) {
|
|
24
|
+
return MODEL_STRENGTHS.has(value);
|
|
25
|
+
}
|
|
16
26
|
function getNestedValue(obj, key) {
|
|
17
27
|
const parts = key.split('.');
|
|
18
28
|
let current = obj;
|
|
@@ -32,7 +42,7 @@ function parseConfigValue(raw) {
|
|
|
32
42
|
return parseInt(raw, 10);
|
|
33
43
|
return raw;
|
|
34
44
|
}
|
|
35
|
-
function setNestedValue(cfg, key, value) {
|
|
45
|
+
function setNestedValue(cfg, key, rawValue, value) {
|
|
36
46
|
const parts = key.split('.');
|
|
37
47
|
const topKey = parts[0];
|
|
38
48
|
if (!TOP_LEVEL_KEYS.has(topKey)) {
|
|
@@ -65,11 +75,50 @@ function setNestedValue(cfg, key, value) {
|
|
|
65
75
|
// record tables are edited directly in config.json (run `crtr sys config
|
|
66
76
|
// path` to locate it).
|
|
67
77
|
if (key === 'canvasNav.prefixKey') {
|
|
68
|
-
cfg.canvasNav.prefixKey =
|
|
78
|
+
cfg.canvasNav.prefixKey = rawValue;
|
|
69
79
|
return;
|
|
70
80
|
}
|
|
71
81
|
throw usage(`canvasNav.${parts.slice(1).join('.') || '*'} is a record table — edit config.json directly (run \`crtr sys config path\`). Only canvasNav.prefixKey is settable via this command.`);
|
|
72
82
|
}
|
|
83
|
+
if (topKey === 'modelLadders') {
|
|
84
|
+
if (parts.length === 2 && parts[1] === 'defaultProvider') {
|
|
85
|
+
if (!isModelProvider(rawValue)) {
|
|
86
|
+
throw usage(`modelLadders.defaultProvider must be anthropic or openai`);
|
|
87
|
+
}
|
|
88
|
+
cfg.modelLadders.defaultProvider = rawValue;
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
if (parts.length === 3) {
|
|
92
|
+
const provider = parts[1];
|
|
93
|
+
const strength = parts[2];
|
|
94
|
+
if (!isModelProvider(provider)) {
|
|
95
|
+
throw usage(`modelLadders provider must be anthropic or openai`);
|
|
96
|
+
}
|
|
97
|
+
if (!isModelStrength(strength)) {
|
|
98
|
+
throw usage(`modelLadders strength must be ultra, strong, medium, or light`);
|
|
99
|
+
}
|
|
100
|
+
if (rawValue.trim() === '') {
|
|
101
|
+
throw usage(`modelLadders.${provider}.${strength} cannot be empty`);
|
|
102
|
+
}
|
|
103
|
+
cfg.modelLadders[provider][strength] = rawValue;
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
throw usage(`unsupported key path for set: ${key}`);
|
|
107
|
+
}
|
|
108
|
+
if (topKey === 'personaStrengths') {
|
|
109
|
+
if (parts.length !== 2) {
|
|
110
|
+
throw usage(`unsupported key path for set: ${key}`);
|
|
111
|
+
}
|
|
112
|
+
const persona = parts[1];
|
|
113
|
+
if (persona.trim() === '' || persona !== persona.trim()) {
|
|
114
|
+
throw usage(`personaStrengths.<persona-kind> must be a non-empty persona kind without leading or trailing whitespace`);
|
|
115
|
+
}
|
|
116
|
+
if (!isModelStrength(rawValue)) {
|
|
117
|
+
throw usage(`personaStrengths.<persona-kind> must be ultra, strong, medium, or light`);
|
|
118
|
+
}
|
|
119
|
+
cfg.personaStrengths[persona] = rawValue;
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
73
122
|
if (parts.length === 1) {
|
|
74
123
|
cfg[topKey] = value;
|
|
75
124
|
return;
|
|
@@ -87,13 +136,13 @@ function setNestedValue(cfg, key, value) {
|
|
|
87
136
|
const configGet = defineLeaf({
|
|
88
137
|
name: 'get',
|
|
89
138
|
description: 'read a config value by key',
|
|
90
|
-
whenToUse: 'you want to read a single config value by its dotted key — the current auto_update policy, max_panes_per_window, the canvasNav prefix key — optionally from a specific scope. Use sys config set instead to change a value, sys config path to locate the file for hand-editing.',
|
|
139
|
+
whenToUse: 'you want to read a single config value by its dotted key — the current auto_update policy, max_panes_per_window, the canvasNav prefix key, model ladders, or persona strengths — optionally from a specific scope. Use sys config set instead to change a value, sys config path to locate the file for hand-editing.',
|
|
91
140
|
help: {
|
|
92
141
|
name: 'sys config get',
|
|
93
142
|
summary: 'read a config value by dotted key',
|
|
94
143
|
params: [
|
|
95
|
-
{ kind: 'positional', name: 'key', type: 'string', required: true, constraint: 'Dotted key path. Top-level keys: auto_update, marketplaces, plugins, max_panes_per_window, canvasNav (read whole; edit canvasNav.prefixBinds/graphBinds in config.json directly).' },
|
|
96
|
-
{ kind: 'flag', name: 'scope', type: 'enum', choices: ['user', 'project'
|
|
144
|
+
{ kind: 'positional', name: 'key', type: 'string', required: true, constraint: 'Dotted key path. Top-level keys: auto_update, marketplaces, plugins, max_panes_per_window, canvasNav, modelLadders, personaStrengths (read whole; edit canvasNav.prefixBinds/graphBinds in config.json directly).' },
|
|
145
|
+
{ kind: 'flag', name: 'scope', type: 'enum', choices: ['user', 'project'], required: false, constraint: 'Scope to read from. Default: user.' },
|
|
97
146
|
],
|
|
98
147
|
output: [
|
|
99
148
|
{ name: 'key', type: 'string', required: true, constraint: 'Echo of input key.' },
|
|
@@ -117,12 +166,12 @@ const configGet = defineLeaf({
|
|
|
117
166
|
const configSet = defineLeaf({
|
|
118
167
|
name: 'set',
|
|
119
168
|
description: 'write a config value by key',
|
|
120
|
-
whenToUse: 'you want to change a crtr setting — flip auto_update.crtr or auto_update.content to notify, apply, or off, raise max_panes_per_window, rebind canvasNav.prefixKey — written to the user or project scope. Use sys config get instead to read a value; the canvasNav record tables are not settable here, so edit config.json directly (sys config path) for those.',
|
|
169
|
+
whenToUse: 'you want to change a crtr setting — flip auto_update.crtr or auto_update.content to notify, apply, or off, raise max_panes_per_window, rebind canvasNav.prefixKey, set model ladders, or map persona strengths — written to the user or project scope. Use sys config get instead to read a value; the canvasNav record tables are not settable here, so edit config.json directly (sys config path) for those.',
|
|
121
170
|
help: {
|
|
122
171
|
name: 'sys config set',
|
|
123
172
|
summary: 'write a config value by dotted key',
|
|
124
173
|
params: [
|
|
125
|
-
{ kind: 'positional', name: 'key', type: 'string', required: true, constraint: 'Dotted key path. Supported: auto_update.crtr, auto_update.content, auto_update.interval_hours, max_panes_per_window, canvasNav.prefixKey. The canvasNav.prefixBinds/graphBinds record tables are not settable here — edit config.json directly (`crtr sys config path`).' },
|
|
174
|
+
{ kind: 'positional', name: 'key', type: 'string', required: true, constraint: 'Dotted key path. Supported: auto_update.crtr, auto_update.content, auto_update.interval_hours, max_panes_per_window, canvasNav.prefixKey, modelLadders.defaultProvider, modelLadders.<anthropic|openai>.<ultra|strong|medium|light>, personaStrengths.<persona-kind>. The canvasNav.prefixBinds/graphBinds record tables are not settable here — edit config.json directly (`crtr sys config path`).' },
|
|
126
175
|
{ kind: 'flag', name: 'value', type: 'string', required: true, constraint: 'value VALUE — string, required. Stored as-is if quoted; coerced to number or boolean when unambiguous.' },
|
|
127
176
|
{ kind: 'flag', name: 'scope', type: 'enum', choices: ['user', 'project'], required: false, constraint: 'Scope to write to. Default: user.' },
|
|
128
177
|
],
|
|
@@ -141,7 +190,7 @@ const configSet = defineLeaf({
|
|
|
141
190
|
// Flags are stringly-typed; coerce to number or boolean when unambiguous
|
|
142
191
|
const parsed = parseConfigValue(rawValue);
|
|
143
192
|
const cfg = readConfig(scope);
|
|
144
|
-
setNestedValue(cfg, key, parsed);
|
|
193
|
+
setNestedValue(cfg, key, rawValue, parsed);
|
|
145
194
|
writeConfig(scope, cfg);
|
|
146
195
|
// Read back the written value for echo
|
|
147
196
|
const written = getNestedValue(cfg, key);
|
|
@@ -191,10 +240,10 @@ const configPath = defineLeaf({
|
|
|
191
240
|
export const configBranch = defineBranch({
|
|
192
241
|
name: 'config',
|
|
193
242
|
description: 'read and write configuration',
|
|
194
|
-
whenToUse: 'inspecting or changing crtr settings — read a value with sys config get, change one with sys config set (auto_update policy, max_panes_per_window, canvasNav.prefixKey), or locate config.json with sys config path to hand-edit the record tables set cannot reach.',
|
|
243
|
+
whenToUse: 'inspecting or changing crtr settings — read a value with sys config get, change one with sys config set (auto_update policy, max_panes_per_window, canvasNav.prefixKey, model ladders, persona strengths), or locate config.json with sys config path to hand-edit the record tables set cannot reach.',
|
|
195
244
|
help: {
|
|
196
245
|
name: 'sys config',
|
|
197
|
-
summary: 'read and write crtr configuration',
|
|
246
|
+
summary: 'read and write crtr configuration, including model ladders and persona strengths',
|
|
198
247
|
},
|
|
199
248
|
children: [configGet, configSet, configPath],
|
|
200
249
|
});
|
|
@@ -5,6 +5,9 @@ import { scopeRoot, listScopes, marketplacesDir, pluginsDir } from '../../core/s
|
|
|
5
5
|
import { readMarketplaceManifest, readPluginManifest } from '../../core/manifest.js';
|
|
6
6
|
import { pathExists, listDirs, removePath } from '../../core/fs-utils.js';
|
|
7
7
|
import { lsRemote } from '../../core/git.js';
|
|
8
|
+
import { listNodes } from '../../core/canvas/index.js';
|
|
9
|
+
import { activeFaultForDisplay } from '../../core/canvas/render.js';
|
|
10
|
+
import { readFault } from '../../core/runtime/fault.js';
|
|
8
11
|
function pass(scope, name, message) {
|
|
9
12
|
return { scope, name, status: 'pass', message };
|
|
10
13
|
}
|
|
@@ -41,6 +44,8 @@ function applyRemediation(rem) {
|
|
|
41
44
|
removePath(rem.path);
|
|
42
45
|
return true;
|
|
43
46
|
}
|
|
47
|
+
case 'human_action':
|
|
48
|
+
return false;
|
|
44
49
|
default:
|
|
45
50
|
return false;
|
|
46
51
|
}
|
|
@@ -49,6 +54,51 @@ function applyRemediation(rem) {
|
|
|
49
54
|
return false;
|
|
50
55
|
}
|
|
51
56
|
}
|
|
57
|
+
function faultMessage(fault) {
|
|
58
|
+
const parts = [
|
|
59
|
+
`${fault.link} ${fault.kind}`,
|
|
60
|
+
`disposition=${fault.retry.disposition}`,
|
|
61
|
+
typeof fault.retry.attempt === 'number' && typeof fault.retry.max === 'number' ? `attempt=${fault.retry.attempt}/${fault.retry.max}` : undefined,
|
|
62
|
+
typeof fault.retry.nextAt === 'string' ? `nextAt=${fault.retry.nextAt}` : undefined,
|
|
63
|
+
].filter((part) => part !== undefined);
|
|
64
|
+
return parts.join(' · ');
|
|
65
|
+
}
|
|
66
|
+
function faultRemediation(fault) {
|
|
67
|
+
const actionByKind = {
|
|
68
|
+
connection: 'restore connectivity',
|
|
69
|
+
'rate-limit': 'wait for the rate limit to clear',
|
|
70
|
+
overloaded: 'wait for load to drop',
|
|
71
|
+
auth: 're-authenticate',
|
|
72
|
+
protocol: 'fix the protocol mismatch',
|
|
73
|
+
other: 'fix the underlying issue',
|
|
74
|
+
};
|
|
75
|
+
const progress = fault.retry.disposition === 'auto'
|
|
76
|
+
? fault.retry.by === 'client'
|
|
77
|
+
? 'client redialing'
|
|
78
|
+
: typeof fault.retry.attempt === 'number' && typeof fault.retry.max === 'number'
|
|
79
|
+
? `auto-retrying, attempt ${fault.retry.attempt}/${fault.retry.max}`
|
|
80
|
+
: 'auto-retrying'
|
|
81
|
+
: fault.retry.disposition === 'fatal'
|
|
82
|
+
? 'parked fatal'
|
|
83
|
+
: 'manual';
|
|
84
|
+
return {
|
|
85
|
+
kind: 'human_action',
|
|
86
|
+
description: `${actionByKind[fault.kind]}; ${progress}; inspect ${fault.logPath}`,
|
|
87
|
+
logPath: fault.logPath,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
function runFaultChecks() {
|
|
91
|
+
const results = [];
|
|
92
|
+
for (const node of listNodes()) {
|
|
93
|
+
const fault = activeFaultForDisplay(node, readFault(node.node_id));
|
|
94
|
+
if (fault === null)
|
|
95
|
+
continue;
|
|
96
|
+
const name = `fault:${node.node_id}:${fault.link}`;
|
|
97
|
+
const remediation = faultRemediation(fault);
|
|
98
|
+
results.push(failCheck('canvas', name, faultMessage(fault), remediation));
|
|
99
|
+
}
|
|
100
|
+
return results;
|
|
101
|
+
}
|
|
52
102
|
function runChecksForScope(scope, opts) {
|
|
53
103
|
const results = [];
|
|
54
104
|
const root = scopeRoot(scope);
|
|
@@ -198,14 +248,14 @@ export const sysDoctorLeaf = defineLeaf({
|
|
|
198
248
|
{ kind: 'flag', name: 'remote', type: 'bool', required: false, constraint: 'Check git remotes with ls-remote (slow — makes network calls).' },
|
|
199
249
|
],
|
|
200
250
|
output: [
|
|
201
|
-
{ name: 'checks', type: 'object[]', required: true, constraint: 'Each: {scope, name, status, message, fixed?, remediation?}. status: pass | fail. remediation (when present) is {kind, description, ...payload} where kind is remove_config_key | rm_path. Sorted by scope then name.' },
|
|
251
|
+
{ name: 'checks', type: 'object[]', required: true, constraint: 'Each: {scope, name, status, message, fixed?, remediation?}. status: pass | fail. scope may be user | project | canvas. remediation (when present) is {kind, description, ...payload} where kind is remove_config_key | rm_path | human_action. Sorted by scope then name.' },
|
|
202
252
|
{ name: 'ok', type: 'boolean', required: true, constraint: 'True when no unresolved fail checks remain.' },
|
|
203
253
|
],
|
|
204
254
|
outputKind: 'object',
|
|
205
255
|
effects: [
|
|
206
256
|
'Read-only unless --fix is passed.',
|
|
207
|
-
'With --fix: applies each non-pass check\'s `remediation` — removes stale config entries and deletes dangling plugin/marketplace directories.',
|
|
208
|
-
'Each non-pass result carries a structured `remediation` describing the fix action (absolute paths, exact config keys) so callers can apply it directly without --fix.',
|
|
257
|
+
'With --fix: applies each non-pass check\'s `remediation` — removes stale config entries and deletes dangling plugin/marketplace directories; canvas fault findings stay read-only.',
|
|
258
|
+
'Each non-pass result carries a structured `remediation` describing the fix action (absolute paths, exact config keys, or a human_action with logPath) so callers can apply it directly without --fix.',
|
|
209
259
|
],
|
|
210
260
|
},
|
|
211
261
|
run: async (input) => {
|
|
@@ -218,10 +268,12 @@ export const sysDoctorLeaf = defineLeaf({
|
|
|
218
268
|
const results = runChecksForScope(scope, { fix, remote });
|
|
219
269
|
allResults.push(...results);
|
|
220
270
|
}
|
|
271
|
+
allResults.push(...runFaultChecks());
|
|
221
272
|
// Sort by scope then name (mirrors old printResults grouping)
|
|
273
|
+
const scopeOrder = { user: 0, project: 1, builtin: 2, canvas: 3 };
|
|
222
274
|
allResults.sort((a, b) => {
|
|
223
275
|
if (a.scope !== b.scope)
|
|
224
|
-
return a.scope
|
|
276
|
+
return scopeOrder[a.scope] - scopeOrder[b.scope];
|
|
225
277
|
return a.name.localeCompare(b.name);
|
|
226
278
|
});
|
|
227
279
|
const ok = !allResults.some((r) => r.status === 'fail' && r.fixed !== true);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const promptReviewLeaf: import("../../core/command.js").LeafDef;
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { mkdirSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { defineLeaf } from '../../core/command.js';
|
|
4
|
+
import { InputError, readStdinRaw } from '../../core/io.js';
|
|
5
|
+
import { workspaceRoot } from '../../core/artifact.js';
|
|
6
|
+
import { availableKinds, kindWhenToUse, subPersonasFor } from '../../core/personas/index.js';
|
|
7
|
+
import { resolvePromptReview } from '../../core/personas/resolve.js';
|
|
8
|
+
function resolveLifecycle(mode, lifecycle) {
|
|
9
|
+
if (lifecycle !== undefined)
|
|
10
|
+
return lifecycle;
|
|
11
|
+
return mode === 'orchestrator' ? 'resident' : 'terminal';
|
|
12
|
+
}
|
|
13
|
+
function readJson(raw) {
|
|
14
|
+
try {
|
|
15
|
+
return JSON.parse(raw);
|
|
16
|
+
}
|
|
17
|
+
catch (err) {
|
|
18
|
+
throw new InputError({
|
|
19
|
+
error: 'invalid_json',
|
|
20
|
+
message: `stdin is not valid JSON: ${String(err)}`,
|
|
21
|
+
field: 'stdin',
|
|
22
|
+
next: 'Pass the comments deck JSON on stdin.',
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function parseBool(value, field, defaultValue) {
|
|
27
|
+
if (value === undefined)
|
|
28
|
+
return defaultValue;
|
|
29
|
+
if (value === true || value === false)
|
|
30
|
+
return value;
|
|
31
|
+
if (typeof value !== 'string') {
|
|
32
|
+
throw new InputError({ error: 'invalid_field', message: `${field} must be true or false`, field, next: `Use true or false for ${field}.` });
|
|
33
|
+
}
|
|
34
|
+
const normalized = value.trim().toLowerCase();
|
|
35
|
+
if (normalized === 'true')
|
|
36
|
+
return true;
|
|
37
|
+
if (normalized === 'false')
|
|
38
|
+
return false;
|
|
39
|
+
throw new InputError({ error: 'invalid_field', message: `${field} must be true or false`, field, next: `Use true or false for ${field}.` });
|
|
40
|
+
}
|
|
41
|
+
function optionalString(value) {
|
|
42
|
+
return typeof value === 'string' && value.trim() !== '' ? value : null;
|
|
43
|
+
}
|
|
44
|
+
function normaliseKindList() {
|
|
45
|
+
return availableKinds().map((kind) => ({ kind, whenToUse: kindWhenToUse(kind) }));
|
|
46
|
+
}
|
|
47
|
+
function normaliseSubPersonaList(kind) {
|
|
48
|
+
return subPersonasFor(kind).map((sub) => ({ kind: sub.kind, whenToUse: sub.whenToUse }));
|
|
49
|
+
}
|
|
50
|
+
function reviewList() {
|
|
51
|
+
const kinds = normaliseKindList();
|
|
52
|
+
const subPersonas = {};
|
|
53
|
+
for (const { kind } of kinds)
|
|
54
|
+
subPersonas[kind] = normaliseSubPersonaList(kind);
|
|
55
|
+
return {
|
|
56
|
+
kinds,
|
|
57
|
+
modes: ['base', 'orchestrator'],
|
|
58
|
+
lifecycles: ['terminal', 'resident'],
|
|
59
|
+
subPersonas,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
function promptReviewPath() {
|
|
63
|
+
const dir = workspaceRoot();
|
|
64
|
+
const reviewsDir = join(dir, 'prompt-reviews');
|
|
65
|
+
mkdirSync(reviewsDir, { recursive: true });
|
|
66
|
+
return join(reviewsDir, `${new Date().toISOString()}.md`);
|
|
67
|
+
}
|
|
68
|
+
function renderExportDeck(deck) {
|
|
69
|
+
const lines = [];
|
|
70
|
+
lines.push(`# Prompt review — ${deck.config.kind}/${deck.config.mode}`);
|
|
71
|
+
lines.push('');
|
|
72
|
+
lines.push('## Config');
|
|
73
|
+
lines.push(`- timestamp: ${new Date().toISOString()}`);
|
|
74
|
+
lines.push(`- kind: ${deck.config.kind}`);
|
|
75
|
+
lines.push(`- mode: ${deck.config.mode}`);
|
|
76
|
+
lines.push(`- lifecycle: ${deck.config.lifecycle}`);
|
|
77
|
+
lines.push(`- hasManager: ${String(deck.config.hasManager)}`);
|
|
78
|
+
lines.push(`- node: ${deck.config.node ?? '(none)'}`);
|
|
79
|
+
lines.push('');
|
|
80
|
+
lines.push('## Comments');
|
|
81
|
+
if (deck.comments.length === 0) {
|
|
82
|
+
lines.push('- none');
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
for (const comment of deck.comments) {
|
|
86
|
+
lines.push(`### ${comment.label} (${comment.layerId})`);
|
|
87
|
+
lines.push(`- source: \`${comment.sourcePath ?? '(generated)'}\` (${comment.scope})`);
|
|
88
|
+
if (comment.anchor !== null) {
|
|
89
|
+
lines.push('> ' + comment.anchor.split('\n').join('\n> '));
|
|
90
|
+
}
|
|
91
|
+
lines.push(comment.body);
|
|
92
|
+
lines.push('');
|
|
93
|
+
}
|
|
94
|
+
if (lines[lines.length - 1] === '')
|
|
95
|
+
lines.pop();
|
|
96
|
+
}
|
|
97
|
+
lines.push('');
|
|
98
|
+
lines.push('## Prompt sources (edit these)');
|
|
99
|
+
const seen = new Set();
|
|
100
|
+
for (const source of deck.sources) {
|
|
101
|
+
const key = `${source.scope}:${source.path}`;
|
|
102
|
+
if (seen.has(key))
|
|
103
|
+
continue;
|
|
104
|
+
seen.add(key);
|
|
105
|
+
lines.push(`- ${source.label} — \`${source.path}\` (${source.scope})`);
|
|
106
|
+
}
|
|
107
|
+
lines.push('');
|
|
108
|
+
lines.push('These are review comments on an assembled system prompt; edit the listed source files to address them.');
|
|
109
|
+
return lines.join('\n') + '\n';
|
|
110
|
+
}
|
|
111
|
+
function exportReview(deck) {
|
|
112
|
+
const path = promptReviewPath();
|
|
113
|
+
writeFileSync(path, renderExportDeck(deck), 'utf8');
|
|
114
|
+
return { path };
|
|
115
|
+
}
|
|
116
|
+
function resolveDeck(input) {
|
|
117
|
+
const kind = input['kind'];
|
|
118
|
+
const mode = input['mode'] ?? 'base';
|
|
119
|
+
const lifecycle = resolveLifecycle(mode, input['lifecycle']);
|
|
120
|
+
const hasManager = parseBool(input['hasManager'], 'hasManager', true);
|
|
121
|
+
const node = optionalString(input['node']);
|
|
122
|
+
if (kind === undefined || kind.trim() === '') {
|
|
123
|
+
throw new InputError({ error: 'missing_parameter', message: 'kind is required', field: 'kind', next: 'Pass --kind <kind> or use --list.' });
|
|
124
|
+
}
|
|
125
|
+
return resolvePromptReview(kind, mode, {
|
|
126
|
+
lifecycle,
|
|
127
|
+
hasManager,
|
|
128
|
+
}, node ?? undefined);
|
|
129
|
+
}
|
|
130
|
+
export const promptReviewLeaf = defineLeaf({
|
|
131
|
+
name: 'prompt-review',
|
|
132
|
+
description: 'inspect an assembled prompt and export review notes',
|
|
133
|
+
whenToUse: 'you want to inspect a composed persona prompt for a kind/mode combo, compare the space around it, or export review notes back to source files',
|
|
134
|
+
help: {
|
|
135
|
+
name: 'sys prompt-review',
|
|
136
|
+
summary: 'inspect an assembled prompt and export review notes',
|
|
137
|
+
params: [
|
|
138
|
+
{ kind: 'positional', name: 'action', type: 'string', required: false, constraint: 'Optional subcommand: export. Omit for the assembled prompt contract.' },
|
|
139
|
+
{ kind: 'flag', name: 'kind', type: 'string', required: false, constraint: 'Persona kind to inspect. Required unless --list or export.' },
|
|
140
|
+
{ kind: 'flag', name: 'mode', type: 'enum', choices: ['base', 'orchestrator'], required: false, default: 'base', constraint: 'Persona mode. Default: base.' },
|
|
141
|
+
{ kind: 'flag', name: 'lifecycle', type: 'enum', choices: ['terminal', 'resident'], required: false, constraint: 'Lifecycle axis. Default: base -> terminal, orchestrator -> resident.' },
|
|
142
|
+
{ kind: 'flag', name: 'has-manager', type: 'string', required: false, default: 'true', constraint: 'Whether the node reports to a manager. Use true or false. Default: true.' },
|
|
143
|
+
{ kind: 'flag', name: 'node', type: 'string', required: false, constraint: 'Optional node id. When the node exists, runtime substrate layers are appended.' },
|
|
144
|
+
{ kind: 'flag', name: 'list', type: 'bool', required: false, constraint: 'List the available kinds, modes, lifecycles, and sub-personas.' },
|
|
145
|
+
],
|
|
146
|
+
output: [
|
|
147
|
+
{ name: 'config', type: 'object', required: false, constraint: 'Resolved config: {kind, mode, lifecycle, hasManager, node}.' },
|
|
148
|
+
{ name: 'layers', type: 'object[]', required: false, constraint: 'Ordered prompt layers, including excluded alternatives and provenance.' },
|
|
149
|
+
{ name: 'assembled', type: 'string', required: false, constraint: 'The assembled prompt string.' },
|
|
150
|
+
{ name: 'total', type: 'object', required: false, constraint: 'Approximate token/char totals for included layers.' },
|
|
151
|
+
{ name: 'sources', type: 'object[]', required: false, constraint: 'De-duplicated source files referenced by the prompt.' },
|
|
152
|
+
{ name: 'kinds', type: 'object[]', required: false, constraint: 'Available kinds for --list.' },
|
|
153
|
+
{ name: 'modes', type: 'string[]', required: false, constraint: 'Mode choices for --list.' },
|
|
154
|
+
{ name: 'lifecycles', type: 'string[]', required: false, constraint: 'Lifecycle choices for --list.' },
|
|
155
|
+
{ name: 'subPersonas', type: 'object', required: false, constraint: 'Available sub-personas keyed by kind for --list.' },
|
|
156
|
+
{ name: 'path', type: 'string', required: false, constraint: 'Written markdown path for export.' },
|
|
157
|
+
],
|
|
158
|
+
outputKind: 'object',
|
|
159
|
+
effects: ['Read-only inspection, or writes a review markdown file under the per-cwd crouter root.'],
|
|
160
|
+
},
|
|
161
|
+
run: async (input) => {
|
|
162
|
+
const action = input['action'];
|
|
163
|
+
const isList = input['list'] === true;
|
|
164
|
+
const hasManager = parseBool(input['hasManager'], 'hasManager', true);
|
|
165
|
+
if (action !== undefined && action !== 'export') {
|
|
166
|
+
throw new InputError({ error: 'unknown_action', message: `unknown action: ${action}`, field: 'action', next: 'Use `export` or omit the positional action.' });
|
|
167
|
+
}
|
|
168
|
+
if (action === 'export') {
|
|
169
|
+
const raw = await readStdinRaw();
|
|
170
|
+
const deck = readJson(raw);
|
|
171
|
+
return exportReview(deck);
|
|
172
|
+
}
|
|
173
|
+
if (isList)
|
|
174
|
+
return reviewList();
|
|
175
|
+
const deck = resolveDeck({ ...input, hasManager });
|
|
176
|
+
return deck;
|
|
177
|
+
},
|
|
178
|
+
});
|