@crouton-kit/crouter 0.3.32 → 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 +2 -2
- 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 +31 -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/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
package/dist/core/artifact.d.ts
CHANGED
|
@@ -1,37 +1,6 @@
|
|
|
1
|
-
export type ArtifactKind = 'plans' | 'specs';
|
|
2
1
|
export declare function mangleCwd(cwd?: string): string;
|
|
3
|
-
export declare function
|
|
4
|
-
/** Per-cwd interactions root, mirroring artifactsRoot construction.
|
|
5
|
-
* `~/.crouter/<mangled-cwd>/interactions/`. */
|
|
2
|
+
export declare function workspaceRoot(cwd?: string): string;
|
|
6
3
|
export declare function interactionsRoot(cwd?: string): string;
|
|
7
|
-
/** Directory for one interaction. `id` is sanitized like an artifact name. */
|
|
8
4
|
export declare function interactionDir(id: string, cwd?: string): string;
|
|
9
5
|
export declare function sanitizeName(raw: string): string;
|
|
10
|
-
export declare function
|
|
11
|
-
/** Lines above this threshold trigger an oversize advisory in follow_up. */
|
|
12
|
-
export declare const OVERSIZE_WARN_LINES = 200;
|
|
13
|
-
export interface SaveArtifactResult {
|
|
14
|
-
path: string;
|
|
15
|
-
oversize: boolean;
|
|
16
|
-
lineCount: number;
|
|
17
|
-
}
|
|
18
|
-
/**
|
|
19
|
-
* Atomically write an artifact. Prepends a minimal frontmatter block when
|
|
20
|
-
* `meta` is non-empty so readers can extract structured fields without
|
|
21
|
-
* parsing the full body. Returns the written path and oversize status.
|
|
22
|
-
*/
|
|
23
|
-
export declare function saveArtifact(kind: ArtifactKind, name: string, body: string, meta?: Record<string, string>): SaveArtifactResult;
|
|
24
|
-
export interface ArtifactRecord {
|
|
25
|
-
name: string;
|
|
26
|
-
path: string;
|
|
27
|
-
body: string;
|
|
28
|
-
/** Present only in plan artifacts; null if not set. */
|
|
29
|
-
spec: string | null;
|
|
30
|
-
}
|
|
31
|
-
export declare function readArtifact(kind: ArtifactKind, name: string): ArtifactRecord;
|
|
32
|
-
export interface ArtifactListItem {
|
|
33
|
-
name: string;
|
|
34
|
-
path: string;
|
|
35
|
-
updated_at: string;
|
|
36
|
-
}
|
|
37
|
-
export declare function listArtifacts(kind: ArtifactKind): ArtifactListItem[];
|
|
6
|
+
export declare function migrateLegacyWorkspaceDirs(): void;
|
package/dist/core/artifact.js
CHANGED
|
@@ -1,24 +1,20 @@
|
|
|
1
|
-
// Core
|
|
1
|
+
// Core per-cwd workspace primitives for human interaction artifacts.
|
|
2
2
|
// No commander, no spawn, no output helpers — pure data logic only.
|
|
3
|
-
// Old registerArtifactCommand (commander-based) is removed; callers ported to JSON I/O.
|
|
4
3
|
import { homedir } from 'node:os';
|
|
5
|
-
import { join
|
|
6
|
-
import {
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import { renameSync } from 'node:fs';
|
|
7
6
|
import { CRTR_DIR_NAME } from '../types.js';
|
|
8
|
-
import { ensureDir,
|
|
9
|
-
import {
|
|
7
|
+
import { ensureDir, listDirs, pathExists, removePath } from './fs-utils.js';
|
|
8
|
+
import { usage } from './errors.js';
|
|
10
9
|
export function mangleCwd(cwd = process.cwd()) {
|
|
11
10
|
return cwd.replace(/\//g, '-');
|
|
12
11
|
}
|
|
13
|
-
export function
|
|
14
|
-
return join(homedir(), CRTR_DIR_NAME, mangleCwd(cwd)
|
|
12
|
+
export function workspaceRoot(cwd) {
|
|
13
|
+
return join(homedir(), CRTR_DIR_NAME, 'workspaces', mangleCwd(cwd));
|
|
15
14
|
}
|
|
16
|
-
/** Per-cwd interactions root, mirroring artifactsRoot construction.
|
|
17
|
-
* `~/.crouter/<mangled-cwd>/interactions/`. */
|
|
18
15
|
export function interactionsRoot(cwd) {
|
|
19
|
-
return join(
|
|
16
|
+
return join(workspaceRoot(cwd), 'interactions');
|
|
20
17
|
}
|
|
21
|
-
/** Directory for one interaction. `id` is sanitized like an artifact name. */
|
|
22
18
|
export function interactionDir(id, cwd) {
|
|
23
19
|
return join(interactionsRoot(cwd), sanitizeName(id));
|
|
24
20
|
}
|
|
@@ -34,84 +30,28 @@ export function sanitizeName(raw) {
|
|
|
34
30
|
}
|
|
35
31
|
return trimmed;
|
|
36
32
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
*/
|
|
47
|
-
export function saveArtifact(kind, name, body, meta = {}) {
|
|
48
|
-
const filePath = artifactPath(kind, name);
|
|
49
|
-
ensureDir(dirname(filePath));
|
|
50
|
-
const metaKeys = Object.keys(meta);
|
|
51
|
-
let content;
|
|
52
|
-
if (metaKeys.length > 0) {
|
|
53
|
-
const fm = '---\n' + metaKeys.map((k) => `${k}: ${meta[k]}`).join('\n') + '\n---\n';
|
|
54
|
-
content = fm + body;
|
|
55
|
-
}
|
|
56
|
-
else {
|
|
57
|
-
content = body;
|
|
58
|
-
}
|
|
59
|
-
const finalContent = content.endsWith('\n') ? content : content + '\n';
|
|
60
|
-
writeFileSync(filePath, finalContent, 'utf8');
|
|
61
|
-
const lineCount = finalContent.split('\n').length - 1;
|
|
62
|
-
return { path: filePath, oversize: lineCount > OVERSIZE_WARN_LINES, lineCount };
|
|
63
|
-
}
|
|
64
|
-
const FRONTMATTER_BLOCK_RE = /^---\s*\r?\n([\s\S]*?)\r?\n---\s*\r?\n?/;
|
|
65
|
-
function parseArtifactFrontmatter(source) {
|
|
66
|
-
const match = source.match(FRONTMATTER_BLOCK_RE);
|
|
67
|
-
if (!match)
|
|
68
|
-
return { fields: {}, body: source };
|
|
69
|
-
const raw = match[1];
|
|
70
|
-
const body = source.slice(match[0].length);
|
|
71
|
-
const fields = {};
|
|
72
|
-
for (const line of raw.split(/\r?\n/)) {
|
|
73
|
-
const colon = line.indexOf(':');
|
|
74
|
-
if (colon === -1)
|
|
33
|
+
const LEGACY_WORKSPACE_DIR_RE = /^-[A-Za-z0-9].*/;
|
|
34
|
+
export function migrateLegacyWorkspaceDirs() {
|
|
35
|
+
const dataHome = join(homedir(), CRTR_DIR_NAME);
|
|
36
|
+
const workspaceHome = join(dataHome, 'workspaces');
|
|
37
|
+
if (!pathExists(dataHome))
|
|
38
|
+
return;
|
|
39
|
+
ensureDir(workspaceHome);
|
|
40
|
+
for (const entry of listDirs(dataHome)) {
|
|
41
|
+
if (entry === 'workspaces' || !LEGACY_WORKSPACE_DIR_RE.test(entry))
|
|
75
42
|
continue;
|
|
76
|
-
const
|
|
77
|
-
const
|
|
78
|
-
if (
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
}
|
|
83
|
-
export function readArtifact(kind, name) {
|
|
84
|
-
const filePath = artifactPath(kind, name);
|
|
85
|
-
if (!pathExists(filePath)) {
|
|
86
|
-
throw notFound(`${kind.slice(0, -1)} not found: ${name} (looked at ${filePath})`);
|
|
87
|
-
}
|
|
88
|
-
const raw = readText(filePath);
|
|
89
|
-
const { fields, body } = parseArtifactFrontmatter(raw);
|
|
90
|
-
const specField = fields['spec'];
|
|
91
|
-
return {
|
|
92
|
-
name,
|
|
93
|
-
path: filePath,
|
|
94
|
-
body,
|
|
95
|
-
spec: specField !== undefined ? specField : null,
|
|
96
|
-
};
|
|
97
|
-
}
|
|
98
|
-
export function listArtifacts(kind) {
|
|
99
|
-
const root = artifactsRoot(kind);
|
|
100
|
-
if (!pathExists(root))
|
|
101
|
-
return [];
|
|
102
|
-
const files = walkFiles(root, (n) => n.endsWith('.md'));
|
|
103
|
-
const items = files.map((abs) => {
|
|
104
|
-
const name = abs.substring(root.length + 1).replace(/\.md$/, '');
|
|
105
|
-
let updated_at = '';
|
|
43
|
+
const source = join(dataHome, entry);
|
|
44
|
+
const target = join(workspaceHome, entry);
|
|
45
|
+
if (pathExists(target)) {
|
|
46
|
+
removePath(source);
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
106
49
|
try {
|
|
107
|
-
|
|
50
|
+
renameSync(source, target);
|
|
108
51
|
}
|
|
109
52
|
catch {
|
|
110
|
-
|
|
53
|
+
// Leave the source in place only if the rename truly failed.
|
|
54
|
+
// The next startup will retry the same hard-cut move.
|
|
111
55
|
}
|
|
112
|
-
|
|
113
|
-
});
|
|
114
|
-
// Sort ascending by name (stable key for pagination cursor).
|
|
115
|
-
items.sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
|
|
116
|
-
return items;
|
|
56
|
+
}
|
|
117
57
|
}
|
package/dist/core/bootstrap.js
CHANGED
|
@@ -4,6 +4,7 @@ import { findProjectScopeRoot, resetScopeCache, userScopeRoot } from './scope.js
|
|
|
4
4
|
import { ensureDir, pathExists, removePath, nowIso } from './fs-utils.js';
|
|
5
5
|
import { readConfig, readState, updateConfig, updateState, ensureScopeInitialized } from './config.js';
|
|
6
6
|
import { clone } from './git.js';
|
|
7
|
+
import { migrateLegacyWorkspaceDirs } from './artifact.js';
|
|
7
8
|
import { readMarketplaceManifest } from './manifest.js';
|
|
8
9
|
import { CRTR_DIR_NAME } from '../types.js';
|
|
9
10
|
export const OFFICIAL_MARKETPLACE_NAME = 'crouter-official-marketplace';
|
|
@@ -76,6 +77,7 @@ export function ensureProjectScope(argv) {
|
|
|
76
77
|
return;
|
|
77
78
|
if (shouldSkipForArgv(argv))
|
|
78
79
|
return;
|
|
80
|
+
migrateLegacyWorkspaceDirs();
|
|
79
81
|
// Already inside a project scope (here or in an ancestor) — nothing to do.
|
|
80
82
|
if (findProjectScopeRoot() !== null)
|
|
81
83
|
return;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { test } from 'node:test';
|
|
2
2
|
import assert from 'node:assert/strict';
|
|
3
|
-
import { buildTree, flatten, fuzzyMatch, tabPredicate, TABS, attentionTier, attentionMtime } from '../model.js';
|
|
3
|
+
import { buildTree, flatten, fuzzyMatch, pruneNode, tabPredicate, TABS, attentionTier, attentionMtime } from '../model.js';
|
|
4
4
|
// ── Fixture canvas ───────────────────────────────────────────────────────────
|
|
5
5
|
// root1 (active) ← rank 0
|
|
6
6
|
// child-a (idle)
|
|
@@ -73,6 +73,40 @@ test('buildTree: unknown child ids are dropped (missing meta safe)', () => {
|
|
|
73
73
|
assert.deepEqual(t.nodes.get('root1').childIds, ['child-a']);
|
|
74
74
|
assert.equal(t.nodes.has('ghost'), false);
|
|
75
75
|
});
|
|
76
|
+
// ── pruneNode ────────────────────────────────────────────────────────────────
|
|
77
|
+
// Regression: pressing `x` in canvas browse on an EMPTY active node looked like a
|
|
78
|
+
// no-op — closeNode hard-deletes (reaps) an empty node, but doClose never removed
|
|
79
|
+
// it from the in-memory tree, so the stale active row kept painting. pruneNode is
|
|
80
|
+
// the splice doClose now applies for a reaped (getNode===null) close.
|
|
81
|
+
test('pruneNode: reaping a leaf removes it from its parent and the node map', () => {
|
|
82
|
+
const t = tree();
|
|
83
|
+
pruneNode(t, 'grand-x'); // leaf under child-a
|
|
84
|
+
assert.equal(t.nodes.has('grand-x'), false);
|
|
85
|
+
assert.deepEqual(t.nodes.get('child-a').childIds, []);
|
|
86
|
+
});
|
|
87
|
+
test('pruneNode: reaping a mid-tree node re-homes its children to its parent', () => {
|
|
88
|
+
const t = tree();
|
|
89
|
+
pruneNode(t, 'child-a'); // child-a (under root1) owns grand-x
|
|
90
|
+
assert.equal(t.nodes.has('child-a'), false);
|
|
91
|
+
// grand-x lifts into child-a's slot in root1's child order (before child-b).
|
|
92
|
+
assert.deepEqual(t.nodes.get('root1').childIds, ['grand-x', 'child-b']);
|
|
93
|
+
assert.equal(t.nodes.get('grand-x').parentId, 'root1');
|
|
94
|
+
});
|
|
95
|
+
test('pruneNode: reaping a root lifts its children into the root list', () => {
|
|
96
|
+
const t = tree();
|
|
97
|
+
const rootIdx = t.roots.indexOf('root2');
|
|
98
|
+
pruneNode(t, 'root2'); // root2 owns child-c
|
|
99
|
+
assert.equal(t.nodes.has('root2'), false);
|
|
100
|
+
assert.equal(t.roots.includes('root2'), false);
|
|
101
|
+
assert.equal(t.roots[rootIdx], 'child-c'); // child-c took root2's slot
|
|
102
|
+
assert.equal(t.nodes.get('child-c').parentId, null);
|
|
103
|
+
});
|
|
104
|
+
test('pruneNode: absent id is a no-op', () => {
|
|
105
|
+
const t = tree();
|
|
106
|
+
const before = t.nodes.size;
|
|
107
|
+
pruneNode(t, 'ghost');
|
|
108
|
+
assert.equal(t.nodes.size, before);
|
|
109
|
+
});
|
|
76
110
|
// ── tab predicates ───────────────────────────────────────────────────────────
|
|
77
111
|
test('tabPredicate: All / Live / Dormant / Flagged', () => {
|
|
78
112
|
assert.deepEqual([...TABS], ['All', 'Live', 'Dormant', 'Flagged']);
|
|
@@ -6,14 +6,14 @@
|
|
|
6
6
|
//
|
|
7
7
|
// Resume is one action: Enter routes the chosen node through `crtr node focus`,
|
|
8
8
|
// which goes via reviveNode() — the ONLY sanctioned open. NEVER spawn
|
|
9
|
-
// `pi --session` directly (see
|
|
9
|
+
// `pi --session` directly (see reviveNode() in revive.ts for the desync hazard).
|
|
10
10
|
import { execFileSync } from 'node:child_process';
|
|
11
11
|
import { resolve } from 'node:path';
|
|
12
12
|
import { dashboardRowsAll, enrichRows, loadPreview, renderForest } from '../render.js';
|
|
13
13
|
import { listNodes, subscriptionsOf, getNode } from '../canvas.js';
|
|
14
14
|
import { closeNode } from '../../runtime/close.js';
|
|
15
15
|
import { setupTerminal, restoreTerminal, getTerminalSize, parseKeypress, } from '../../tui/terminal.js';
|
|
16
|
-
import { buildTree, flatten, TABS } from './model.js';
|
|
16
|
+
import { buildTree, flatten, pruneNode, TABS } from './model.js';
|
|
17
17
|
// Sort cycle for the `s` key. Starts on the default `attention` ordering; one `s`
|
|
18
18
|
// press restores the structural `tree` view, then relevance/recency, then back.
|
|
19
19
|
const SORT_CYCLE = ['attention', 'tree', 'relevance', 'recency'];
|
|
@@ -34,7 +34,7 @@ export async function runBrowse(opts = {}) {
|
|
|
34
34
|
}
|
|
35
35
|
// Snapshot the canvas. Drop kind:'human' control-plane decks — they have no pi
|
|
36
36
|
// session, so `node focus` refuses them; they are never a navigation/resume
|
|
37
|
-
// target (mirrors
|
|
37
|
+
// target (mirrors the node focus guard).
|
|
38
38
|
const rows = dashboardRowsAll().filter((r) => r.kind !== 'human');
|
|
39
39
|
const rootIds = listNodes()
|
|
40
40
|
.filter((n) => n.parent === null && n.kind !== 'human')
|
|
@@ -80,6 +80,17 @@ export async function runBrowse(opts = {}) {
|
|
|
80
80
|
// Safety net: an uncaught throw in the (un-unit-tested) keystroke path must
|
|
81
81
|
// never strand the tty in raw + alt-screen + hidden-cursor.
|
|
82
82
|
process.once('exit', cleanup);
|
|
83
|
+
/** On-demand kick of a hanging node: shell `canvas revive <id> --now` (which
|
|
84
|
+
* SIGTERMs the live broker — the daemon resumes it). Synchronous + quiet
|
|
85
|
+
* (stdio ignored) so it never takes over the TUI; best-effort. */
|
|
86
|
+
const reviveNow = (id) => {
|
|
87
|
+
try {
|
|
88
|
+
execFileSync('crtr', ['canvas', 'revive', id, '--now'], { stdio: 'ignore' });
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
/* best-effort — the SIGTERM is what matters; the daemon owns the resume */
|
|
92
|
+
}
|
|
93
|
+
};
|
|
83
94
|
/** Open the chosen node — the ONLY sanctioned path (reviveNode via node focus). */
|
|
84
95
|
const selectAndFocus = (id) => {
|
|
85
96
|
cleanup();
|
|
@@ -188,16 +199,26 @@ export async function runBrowse(opts = {}) {
|
|
|
188
199
|
return; // unknown/already-gone node — nothing to reflect
|
|
189
200
|
}
|
|
190
201
|
for (const cid of closed) {
|
|
202
|
+
const m = getNode(cid);
|
|
203
|
+
if (m === null) {
|
|
204
|
+
// Reaped, not parked: an EMPTY node (0k ctx, no assistant message) is
|
|
205
|
+
// hard-deleted by closeNode (row + dir gone). Splice it out of the tree so
|
|
206
|
+
// its row disappears — otherwise the stale active husk lingers on screen and
|
|
207
|
+
// `x` looks like a no-op (the row was the only thing that ever changed).
|
|
208
|
+
pruneNode(tree, cid);
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
191
211
|
const n = tree.nodes.get(cid);
|
|
192
212
|
if (n === undefined)
|
|
193
213
|
continue;
|
|
194
|
-
|
|
195
|
-
if (m !== null)
|
|
196
|
-
n.row.status = m.status; // real post-close status (done / canceled)
|
|
214
|
+
n.row.status = m.status; // real post-close status (done / canceled)
|
|
197
215
|
n.row.streaming = false;
|
|
198
216
|
n.row.viewed = false;
|
|
199
217
|
}
|
|
200
|
-
|
|
218
|
+
// Keep the cursor at the same INDEX (the row that shifts up into the closed
|
|
219
|
+
// slot), not pinned to the now-gone node — recompute() leaves state.cursor
|
|
220
|
+
// put and clamps it to the new bounds.
|
|
221
|
+
recompute();
|
|
201
222
|
};
|
|
202
223
|
const cycleTab = (dir) => {
|
|
203
224
|
const i = TABS.indexOf(state.tab);
|
|
@@ -228,10 +249,29 @@ export async function runBrowse(opts = {}) {
|
|
|
228
249
|
state.residentsOnly = !state.residentsOnly;
|
|
229
250
|
recompute(keep);
|
|
230
251
|
};
|
|
252
|
+
// Coalesced search repaint. The expensive work per keystroke is recompute()
|
|
253
|
+
// (a fuzzyMatch flatten over the whole corpus) + a full renderFrame. Doing it
|
|
254
|
+
// synchronously on every key blocks the event loop, so fast typing batches into
|
|
255
|
+
// the stdin reads — and batched chunks used to be dropped (see onKeySearch).
|
|
256
|
+
// Instead, each key only mutates state.query (cheap) and marks the view dirty;
|
|
257
|
+
// a single setImmediate drains AFTER every queued stdin 'data' callback (poll
|
|
258
|
+
// phase) has appended its chars, so N rapid keys collapse to ONE recompute+paint
|
|
259
|
+
// and no keystroke is ignored. The sort may still take a moment — that's fine.
|
|
260
|
+
let searchRepaintScheduled = false;
|
|
261
|
+
const scheduleSearchRepaint = () => {
|
|
262
|
+
if (searchRepaintScheduled)
|
|
263
|
+
return;
|
|
264
|
+
searchRepaintScheduled = true;
|
|
265
|
+
setImmediate(() => {
|
|
266
|
+
searchRepaintScheduled = false;
|
|
267
|
+
recompute();
|
|
268
|
+
flush();
|
|
269
|
+
});
|
|
270
|
+
};
|
|
231
271
|
const onKeySearch = (input, key) => {
|
|
232
272
|
if (key.escape) {
|
|
233
273
|
// Cancel the search: drop the query AND the relevance ranking it switched
|
|
234
|
-
// on, returning to the tree.
|
|
274
|
+
// on, returning to the tree. Synchronous: we're leaving search mode.
|
|
235
275
|
state.search = false;
|
|
236
276
|
state.query = '';
|
|
237
277
|
state.sort = 'tree';
|
|
@@ -240,19 +280,20 @@ export async function runBrowse(opts = {}) {
|
|
|
240
280
|
return;
|
|
241
281
|
}
|
|
242
282
|
if (key.return) {
|
|
243
|
-
// Commit: keep the filter, drop search mode, land on the first match.
|
|
283
|
+
// Commit: keep the filter, drop search mode, land on the first match. Must
|
|
284
|
+
// recompute synchronously first so the match index reflects the final query
|
|
285
|
+
// even if a coalesced repaint is still pending.
|
|
244
286
|
state.search = false;
|
|
287
|
+
recompute();
|
|
245
288
|
const firstMatch = visible.findIndex((v) => v.matched);
|
|
246
289
|
if (firstMatch >= 0)
|
|
247
290
|
state.cursor = firstMatch;
|
|
248
|
-
recompute();
|
|
249
291
|
flush();
|
|
250
292
|
return;
|
|
251
293
|
}
|
|
252
294
|
if (key.backspace) {
|
|
253
295
|
state.query = state.query.slice(0, -1);
|
|
254
|
-
|
|
255
|
-
flush();
|
|
296
|
+
scheduleSearchRepaint();
|
|
256
297
|
return;
|
|
257
298
|
}
|
|
258
299
|
// Any ctrl-combo: Ctrl+C quits; everything else is swallowed (never typed).
|
|
@@ -261,11 +302,15 @@ export async function runBrowse(opts = {}) {
|
|
|
261
302
|
quit();
|
|
262
303
|
return;
|
|
263
304
|
}
|
|
264
|
-
//
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
305
|
+
// Append every printable char in the chunk. Fast typing batches multiple
|
|
306
|
+
// chars into one stdin read, so we must take them all (not just a length-1
|
|
307
|
+
// chunk) or keystrokes get dropped. Filtering to >= ' ' strips control/escape
|
|
308
|
+
// bytes (e.g. stray \x1b from an unrecognised sequence) but keeps real input,
|
|
309
|
+
// including multi-byte UTF-8 code points.
|
|
310
|
+
const printable = [...input].filter((c) => c >= ' ').join('');
|
|
311
|
+
if (printable.length > 0) {
|
|
312
|
+
state.query += printable;
|
|
313
|
+
scheduleSearchRepaint();
|
|
269
314
|
}
|
|
270
315
|
};
|
|
271
316
|
const onKeyNav = (input, key) => {
|
|
@@ -424,6 +469,20 @@ export async function runBrowse(opts = {}) {
|
|
|
424
469
|
flush();
|
|
425
470
|
return;
|
|
426
471
|
}
|
|
472
|
+
// Revive now (on-demand kick): for a HANGING node, SIGTERM the live broker so
|
|
473
|
+
// the daemon's crash→grace→resume path brings it back in ~20s instead of
|
|
474
|
+
// waiting out the 5-min auto-revive grace. Shells `canvas revive <id> --now`
|
|
475
|
+
// (browse goes through commands, never touching the runtime directly). 'k' is
|
|
476
|
+
// vim-up here, so the kick is on SHIFT-K.
|
|
477
|
+
if (input === 'K') {
|
|
478
|
+
if (row !== undefined) {
|
|
479
|
+
const r = rowOf(row.id);
|
|
480
|
+
if (r?.hanging != null)
|
|
481
|
+
reviveNow(row.id);
|
|
482
|
+
}
|
|
483
|
+
flush();
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
427
486
|
// Resume.
|
|
428
487
|
if (key.return) {
|
|
429
488
|
if (row !== undefined)
|
|
@@ -445,6 +504,17 @@ export async function runBrowse(opts = {}) {
|
|
|
445
504
|
state.cwdScope = null;
|
|
446
505
|
recompute();
|
|
447
506
|
}
|
|
507
|
+
// Open with the cursor on the CURRENT node (the one browse was launched from),
|
|
508
|
+
// not the top of the tree. Expand its ancestors so the row is actually visible,
|
|
509
|
+
// then home the cursor onto it. No-op when launched outside a node, or when the
|
|
510
|
+
// node is filtered out of the current tab/scope.
|
|
511
|
+
const selfId = process.env['CRTR_NODE_ID'];
|
|
512
|
+
if (selfId !== undefined && selfId !== '' && tree.nodes.has(selfId)) {
|
|
513
|
+
for (let p = tree.nodes.get(selfId)?.parentId ?? null; p !== null; p = tree.nodes.get(p)?.parentId ?? null) {
|
|
514
|
+
state.collapsed.delete(p);
|
|
515
|
+
}
|
|
516
|
+
recompute(selfId);
|
|
517
|
+
}
|
|
448
518
|
setupTerminal();
|
|
449
519
|
flush();
|
|
450
520
|
// Background corpus warmer: after the instant first paint, progressively enrich
|
|
@@ -28,7 +28,7 @@ export interface Tree {
|
|
|
28
28
|
nodes: Map<string, TreeNode>;
|
|
29
29
|
}
|
|
30
30
|
/** Sort rank for roots/stragglers — live first (active, then idle), dormant
|
|
31
|
-
* after. Mirrors render.ts
|
|
31
|
+
* after. Mirrors render.ts statusRank. */
|
|
32
32
|
export declare function statusRank(status: NodeStatus): number;
|
|
33
33
|
/**
|
|
34
34
|
* Build a spanning tree of the whole canvas.
|
|
@@ -43,6 +43,15 @@ export declare function statusRank(status: NodeStatus): number;
|
|
|
43
43
|
* appended as a depth-0 straggler so "All" is genuinely the whole canvas.
|
|
44
44
|
*/
|
|
45
45
|
export declare function buildTree(rows: DashboardRow[], rootIds: string[], childIdsOf: (id: string) => string[]): Tree;
|
|
46
|
+
/** Splice a node out of an already-built tree in place (mutates `tree`). Used when
|
|
47
|
+
* a close REAPED an empty node (row + dir hard-deleted) rather than parking it:
|
|
48
|
+
* the node is gone from the db, so it must vanish from the in-memory tree too or
|
|
49
|
+
* its stale row lingers on screen (the "press x, nothing happens" bug — an empty
|
|
50
|
+
* active husk reaped by closeNode but never dropped from `tree.nodes`, so the
|
|
51
|
+
* attention/flat view kept painting it). Surviving children are re-homed to the
|
|
52
|
+
* reaped node's parent so a non-leaf reap never orphans them out of view. No-op
|
|
53
|
+
* if the id is absent. */
|
|
54
|
+
export declare function pruneNode(tree: Tree, id: string): void;
|
|
46
55
|
/** Case-insensitive subsequence match: every char of `query` appears in `text`
|
|
47
56
|
* in order (gaps allowed). Empty query matches everything. Substrings are a
|
|
48
57
|
* subsequence, so this subsumes substring matching too. */
|
|
@@ -20,7 +20,7 @@ export function tabPredicate(tab, row) {
|
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
22
|
/** Sort rank for roots/stragglers — live first (active, then idle), dormant
|
|
23
|
-
* after. Mirrors render.ts
|
|
23
|
+
* after. Mirrors render.ts statusRank. */
|
|
24
24
|
export function statusRank(status) {
|
|
25
25
|
switch (status) {
|
|
26
26
|
case 'active': return 0;
|
|
@@ -85,6 +85,42 @@ export function buildTree(rows, rootIds, childIdsOf) {
|
|
|
85
85
|
}
|
|
86
86
|
return { roots: orderedRoots, nodes };
|
|
87
87
|
}
|
|
88
|
+
/** Splice a node out of an already-built tree in place (mutates `tree`). Used when
|
|
89
|
+
* a close REAPED an empty node (row + dir hard-deleted) rather than parking it:
|
|
90
|
+
* the node is gone from the db, so it must vanish from the in-memory tree too or
|
|
91
|
+
* its stale row lingers on screen (the "press x, nothing happens" bug — an empty
|
|
92
|
+
* active husk reaped by closeNode but never dropped from `tree.nodes`, so the
|
|
93
|
+
* attention/flat view kept painting it). Surviving children are re-homed to the
|
|
94
|
+
* reaped node's parent so a non-leaf reap never orphans them out of view. No-op
|
|
95
|
+
* if the id is absent. */
|
|
96
|
+
export function pruneNode(tree, id) {
|
|
97
|
+
const node = tree.nodes.get(id);
|
|
98
|
+
if (node === undefined)
|
|
99
|
+
return;
|
|
100
|
+
const parentId = node.parentId;
|
|
101
|
+
// Children still present (their own reap, if any, already removed them) lift up
|
|
102
|
+
// to this node's parent, taking its slot in the child order.
|
|
103
|
+
const survivingKids = node.childIds.filter((c) => tree.nodes.has(c));
|
|
104
|
+
for (const c of survivingKids) {
|
|
105
|
+
const kid = tree.nodes.get(c);
|
|
106
|
+
if (kid !== undefined)
|
|
107
|
+
kid.parentId = parentId;
|
|
108
|
+
}
|
|
109
|
+
if (parentId !== null) {
|
|
110
|
+
const p = tree.nodes.get(parentId);
|
|
111
|
+
if (p !== undefined) {
|
|
112
|
+
const i = p.childIds.indexOf(id);
|
|
113
|
+
if (i >= 0)
|
|
114
|
+
p.childIds.splice(i, 1, ...survivingKids);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
const i = tree.roots.indexOf(id);
|
|
119
|
+
if (i >= 0)
|
|
120
|
+
tree.roots.splice(i, 1, ...survivingKids);
|
|
121
|
+
}
|
|
122
|
+
tree.nodes.delete(id);
|
|
123
|
+
}
|
|
88
124
|
// ---------------------------------------------------------------------------
|
|
89
125
|
// Fuzzy match
|
|
90
126
|
// ---------------------------------------------------------------------------
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
// Every hue (fg / bg color) is gated on `caps.color`; structural SGR (bold/dim/
|
|
12
12
|
// reverse) is allowed always. See detectColorCaps() for the gate, and the
|
|
13
13
|
// canvas-browse color spec for the rationale + palette.
|
|
14
|
+
import { STATUS_GLYPH, STATUS_COLOR, resolveNodeVisual, faultSummary } from '../status-glyph.js';
|
|
14
15
|
import { TABS, matchIndices, promptText, previewSnippet } from './model.js';
|
|
15
16
|
// Span/color primitives live in core/tui/draw.ts (one copy, shared with the
|
|
16
17
|
// `crtr view` host). Re-export the color caps so browse's importers + tests keep
|
|
@@ -50,30 +51,17 @@ const DIM = `${ESC}2m`;
|
|
|
50
51
|
const BOLD = `${ESC}1m`;
|
|
51
52
|
const CURSOR_BG = `${ESC}48;5;236m`; // subtle dark-gray cursor-row bg (256-color)
|
|
52
53
|
// Basic-16 ANSI fg codes used by the palette.
|
|
53
|
-
const FG_GREEN = '32';
|
|
54
54
|
const FG_YELLOW = '33';
|
|
55
55
|
const FG_RED = '31';
|
|
56
56
|
const FG_CYAN = '36';
|
|
57
57
|
const FG_GRAY = '90'; // bright-black
|
|
58
58
|
const FG_BRIGHT_YELLOW = '93';
|
|
59
|
-
const FG_BRIGHT_GREEN = '92'; // streaming pulse (brighter than the active-status green)
|
|
60
59
|
const FG_BRIGHT_CYAN = '96'; // query-match highlight (ties to the cyan search accent)
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
canceled: '⊘',
|
|
67
|
-
};
|
|
68
|
-
/** The load-bearing color: glyph hue per status. Single source of truth, mirrors
|
|
69
|
-
* STATUS_GLYPH. Reinforces the glyph everywhere it appears (rows + summary). */
|
|
70
|
-
const STATUS_COLOR = {
|
|
71
|
-
active: FG_GREEN,
|
|
72
|
-
idle: FG_YELLOW,
|
|
73
|
-
done: FG_CYAN,
|
|
74
|
-
dead: FG_RED,
|
|
75
|
-
canceled: FG_GRAY,
|
|
76
|
-
};
|
|
60
|
+
// STATUS_GLYPH + STATUS_COLOR are imported from ../status-glyph.js (the shared
|
|
61
|
+
// canvas-graph map). STATUS_COLOR there is NUMERIC SGR (e.g. 32) — wrap with
|
|
62
|
+
// `sgr()` where a string ANSI code is needed (Span fg / template literals accept
|
|
63
|
+
// the number directly).
|
|
64
|
+
const FG = (code) => String(code);
|
|
77
65
|
function fmtCtx(tokens) {
|
|
78
66
|
if (tokens <= 0)
|
|
79
67
|
return '0k';
|
|
@@ -194,16 +182,17 @@ function spansWidth(spans) {
|
|
|
194
182
|
* node is genuinely mid-turn; otherwise the lifecycle word. The word is what makes
|
|
195
183
|
* state legible at a glance and survives NO_COLOR (the glyph is the second cue). */
|
|
196
184
|
function statusRail(r) {
|
|
197
|
-
const
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
const
|
|
203
|
-
const
|
|
185
|
+
const v = resolveNodeVisual(r.status, { streaming: r.streaming === true, hanging: r.hanging ?? null });
|
|
186
|
+
// Plain status keeps browse's own status WORD; overlays (hanging/⟳)
|
|
187
|
+
// carry their own word from resolveNodeVisual. A hanging kind label can exceed
|
|
188
|
+
// STATUS_W — acceptable: the rare, attention-demanding row gets a slightly
|
|
189
|
+
// wider rail (Math.max(1,…) keeps a gap), which draws the eye.
|
|
190
|
+
const word = v.word ?? ({ active: 'active', idle: 'idle', done: 'done', dead: 'dead', canceled: 'cancel' }[r.status] ?? r.status);
|
|
191
|
+
const fg = FG(v.color);
|
|
192
|
+
const text = `${v.glyph} ${word}`;
|
|
204
193
|
const pad = Math.max(1, STATUS_W - [...text].length);
|
|
205
194
|
return [
|
|
206
|
-
{ text, style: { fg, bold:
|
|
195
|
+
{ text, style: { fg, bold: v.bold === true } },
|
|
207
196
|
{ text: ' '.repeat(pad) },
|
|
208
197
|
];
|
|
209
198
|
}
|
|
@@ -256,11 +245,15 @@ function rowLine(row, tree, width, isCursor, query, caps, showCwd, now) {
|
|
|
256
245
|
const gutter = r.viewed === true
|
|
257
246
|
? { text: '◉ ', style: { fg: FG_BRIGHT_CYAN, bold: true } }
|
|
258
247
|
: { text: ' ' };
|
|
259
|
-
// Inline attention flag that travels with the name (
|
|
248
|
+
// Inline attention flag that travels with the name (fault summary lives in the rail,
|
|
260
249
|
// attached lives in the gutter).
|
|
261
250
|
const flags = [];
|
|
262
251
|
if (r.asks > 0)
|
|
263
252
|
flags.push({ text: ` ⚑${r.asks}`, style: { fg: FG_BRIGHT_YELLOW, bold: true } }); // pending asks
|
|
253
|
+
// Hanging: the fault summary travels with the name (the ⚠ + kind
|
|
254
|
+
// label live in the rail). Dim yellow so it reads as secondary detail.
|
|
255
|
+
if (r.hanging != null)
|
|
256
|
+
flags.push({ text: ` · ${faultSummary(r.hanging, now)}`, style: { fg: FG_YELLOW, dim: true } });
|
|
264
257
|
const status = statusRail(r);
|
|
265
258
|
const right = metaCluster(r, now, showCwd);
|
|
266
259
|
const treeLead = `${indent}${collapse} `;
|
|
@@ -348,7 +341,7 @@ function snippetLine(ln, width, caps) {
|
|
|
348
341
|
return assemble(spans, width, caps.color, '', false);
|
|
349
342
|
}
|
|
350
343
|
/** The bottom preview panel — exactly PREVIEW_HEIGHT lines: a separator, a meta
|
|
351
|
-
* line (status · kind/mode · project · age · ctx · asks ·
|
|
344
|
+
* line (status · kind/mode · project · age · ctx · asks · fault/viewing), then
|
|
352
345
|
* TWO blocks — the spawn prompt / query-match (PROMPT_LINES) and the node's LAST
|
|
353
346
|
* assistant reply (REPLY_LINES, prefixed `↩`). Under a live query the prompt block
|
|
354
347
|
* is WINDOWED to the matching prompt (anywhere in the conversation) with the match
|
|
@@ -362,14 +355,17 @@ function previewPanel(r, width, caps, now, query) {
|
|
|
362
355
|
out.push('');
|
|
363
356
|
return out;
|
|
364
357
|
}
|
|
358
|
+
const v = resolveNodeVisual(r.status, { streaming: r.streaming === true, hanging: r.hanging ?? null });
|
|
365
359
|
const glyph = caps.color
|
|
366
|
-
? `${ESC}${
|
|
367
|
-
:
|
|
360
|
+
? `${ESC}${v.color}m${v.glyph}${RESET}`
|
|
361
|
+
: v.glyph;
|
|
368
362
|
const metaPieces = [`${r.status} ${r.kind}/${r.mode}`, baseDir(r.cwd), relAge(r.created, now), `ctx ${fmtCtx(r.ctx_tokens)}`];
|
|
369
363
|
if (r.asks > 0)
|
|
370
364
|
metaPieces.push(`⚑${r.asks}`);
|
|
365
|
+
if (r.hanging != null)
|
|
366
|
+
metaPieces.push(faultSummary(r.hanging, now));
|
|
371
367
|
if (r.streaming === true)
|
|
372
|
-
metaPieces.push('⟳
|
|
368
|
+
metaPieces.push('⟳');
|
|
373
369
|
if (r.viewed === true)
|
|
374
370
|
metaPieces.push('◉ viewing');
|
|
375
371
|
const metaText = clip(metaPieces.filter((p) => p !== '').join(' · '), Math.max(0, width - 2));
|
|
@@ -471,13 +467,19 @@ export function renderFrame(state, size, caps = { color: false, color256: false
|
|
|
471
467
|
if (state.pendingClose !== null && state.pendingClose !== undefined) {
|
|
472
468
|
const node = state.tree.nodes.get(state.pendingClose);
|
|
473
469
|
const who = node !== undefined ? node.row.name : state.pendingClose;
|
|
474
|
-
const warn = `⚠ ${who} is
|
|
470
|
+
const warn = `⚠ ${who} is ⟳ — close it (and its subtree) anyway? y / n`;
|
|
475
471
|
lines.push(caps.color ? `${BOLD}${ESC}${FG_BRIGHT_YELLOW}m${clip(warn, width)}${RESET}` : `${REVERSE}${clip(warn, width)}${RESET}`);
|
|
476
472
|
}
|
|
477
473
|
else {
|
|
474
|
+
// When the cursor row has an active fault, lead the footer with the on-demand kick
|
|
475
|
+
// hint (K shells `canvas revive <id> --now` — immediate revive instead of
|
|
476
|
+
// waiting for the daemon's retry schedule).
|
|
477
|
+
const selRow = state.visible[state.cursor];
|
|
478
|
+
const cursorRow = selRow !== undefined ? state.tree.nodes.get(selRow.id)?.row : undefined;
|
|
479
|
+
const hangingHint = cursorRow?.hanging != null ? 'K revive now ' : '';
|
|
478
480
|
const footer = state.search
|
|
479
481
|
? '⏎ commit Esc cancel ⌫ delete'
|
|
480
|
-
:
|
|
482
|
+
: `${hangingHint}↑↓ move →/← tree ⏎ resume x close Tab tabs / search s sort c cwd r residents p preview q quit`;
|
|
481
483
|
lines.push(`${DIM}${clip(footer, width)}${RESET}`);
|
|
482
484
|
}
|
|
483
485
|
// Assemble: home, each line cleared to EOL, then clear below.
|