@pugi/cli 0.1.0-beta.9 → 0.1.0-beta.90
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/CHANGELOG.md +132 -0
- package/LICENSE +1 -1
- package/assets/pugi-prozr2-mascot.ansi +9 -0
- package/bin/run.js +33 -1
- package/dist/commands/deploy.js +40 -40
- package/dist/commands/flatten.js +191 -0
- package/dist/commands/jobs-watch.js +201 -0
- package/dist/commands/jobs.js +42 -27
- package/dist/commands/smoke.js +133 -0
- package/dist/core/agent-progress/cleanup.js +134 -0
- package/dist/core/agent-progress/schema.js +144 -0
- package/dist/core/agent-progress/writer.js +101 -0
- package/dist/core/agents/adaptive-router.js +330 -0
- package/dist/core/agents/query-decomposer.js +297 -0
- package/dist/core/agents/registry.js +3 -3
- package/dist/core/approvals/shortcut-resolver.js +98 -0
- package/dist/core/artifact-chain/dispatcher.js +148 -0
- package/dist/core/artifact-chain/exporter.js +164 -0
- package/dist/core/artifact-chain/state.js +243 -0
- package/dist/core/artifact-chain/steps.js +169 -0
- package/dist/core/ask-user/question.js +92 -0
- package/dist/core/audit/audit-trail.js +275 -0
- package/dist/core/auth/ensure-authenticated.js +129 -0
- package/dist/core/auth/env-provider.js +238 -0
- package/dist/core/auto-open-browser.js +4 -4
- package/dist/core/auto-update/channels.js +122 -0
- package/dist/core/auto-update/checker.js +241 -0
- package/dist/core/auto-update/state.js +235 -0
- package/dist/core/bare-mode/index.js +107 -0
- package/dist/core/bash/redirect.js +281 -0
- package/dist/core/bash-classifier.js +436 -40
- package/dist/core/checkpoint/resumer.js +149 -0
- package/dist/core/checkpoint/rewinder.js +291 -0
- package/dist/core/checkpoints/shadow-git.js +670 -0
- package/dist/core/citations/parser.js +109 -0
- package/dist/core/classifier/yolo-classifier.js +88 -0
- package/dist/core/codegraph/decision-store.js +248 -0
- package/dist/core/codegraph/detect-repo.js +459 -0
- package/dist/core/codegraph/install.js +134 -0
- package/dist/core/codegraph/offer-hook.js +220 -0
- package/dist/core/compact/auto-trigger.js +96 -0
- package/dist/core/compact/buffer-rewriter.js +115 -0
- package/dist/core/compact/summarizer.js +208 -0
- package/dist/core/compact/token-counter.js +108 -0
- package/dist/core/consensus/anvil-fanout.js +25 -25
- package/dist/core/consensus/diff-capture.js +121 -12
- package/dist/core/consensus/rubric.js +21 -21
- package/dist/core/context/builder.js +6 -6
- package/dist/core/context/compaction-events.js +8 -8
- package/dist/core/context/compaction.js +31 -31
- package/dist/core/context/index.js +15 -8
- package/dist/core/context/invariants.js +51 -51
- package/dist/core/context/markdown-loader.js +28 -10
- package/dist/core/context/markdown-traverse.js +255 -0
- package/dist/core/context/pugiignore.js +41 -41
- package/dist/core/context/repo-skeleton.js +37 -37
- package/dist/core/context/tool-eviction.js +55 -0
- package/dist/core/context/watcher.js +32 -32
- package/dist/core/context/working-set.js +23 -23
- package/dist/core/coordinator/agent-tools.js +77 -0
- package/dist/core/coordinator/agent-toolset.js +65 -0
- package/dist/core/coordinator/fsm.js +73 -0
- package/dist/core/coordinator/mode-fsm.js +70 -0
- package/dist/core/cost/rate-card.js +129 -0
- package/dist/core/cost/tracker.js +221 -0
- package/dist/core/credentials.js +13 -13
- package/dist/core/cron/scheduler.js +138 -0
- package/dist/core/denial-tracking/index.js +8 -0
- package/dist/core/denial-tracking/state.js +264 -0
- package/dist/core/diagnostics/probe-runner.js +93 -0
- package/dist/core/diagnostics/probes/api.js +46 -0
- package/dist/core/diagnostics/probes/auth.js +93 -0
- package/dist/core/diagnostics/probes/bare-mode.js +42 -0
- package/dist/core/diagnostics/probes/cli-version.js +127 -0
- package/dist/core/diagnostics/probes/config.js +72 -0
- package/dist/core/diagnostics/probes/denial-tracking.js +57 -0
- package/dist/core/diagnostics/probes/disk.js +81 -0
- package/dist/core/diagnostics/probes/engine-live.js +46 -0
- package/dist/core/diagnostics/probes/git.js +65 -0
- package/dist/core/diagnostics/probes/hooks.js +118 -0
- package/dist/core/diagnostics/probes/mcp.js +75 -0
- package/dist/core/diagnostics/probes/node.js +59 -0
- package/dist/core/diagnostics/probes/pnpm.js +36 -0
- package/dist/core/diagnostics/probes/pugi-md.js +89 -0
- package/dist/core/diagnostics/probes/sandbox.js +40 -0
- package/dist/core/diagnostics/probes/session.js +74 -0
- package/dist/core/diagnostics/probes/status-snapshot.js +488 -0
- package/dist/core/diagnostics/probes/workspace.js +63 -0
- package/dist/core/diagnostics/types.js +70 -0
- package/dist/core/dispatch/cache-cleanup.js +197 -0
- package/dist/core/dispatch/cache-handoff.js +295 -0
- package/dist/core/edits/apply-patch-layer-e.js +189 -0
- package/dist/core/edits/dispatch.js +333 -7
- package/dist/core/edits/format-detector.js +260 -0
- package/dist/core/edits/format-matrix.js +26 -0
- package/dist/core/edits/fuzzy-ladder.js +650 -0
- package/dist/core/edits/index.js +5 -1
- package/dist/core/edits/journal.js +199 -0
- package/dist/core/edits/layer-a-apply.js +15 -15
- package/dist/core/edits/layer-a-fuzzy-apply.js +198 -0
- package/dist/core/edits/layer-b-apply.js +9 -9
- package/dist/core/edits/layer-c-apply.js +6 -6
- package/dist/core/edits/layer-d-ast.js +557 -14
- package/dist/core/edits/marker-parser.js +12 -12
- package/dist/core/edits/security-gate.js +27 -27
- package/dist/core/edits/verify-hook.js +273 -0
- package/dist/core/edits/worktree.js +29 -29
- package/dist/core/engine/anvil-client.js +214 -26
- package/dist/core/engine/auto-compact.js +179 -0
- package/dist/core/engine/budgets.js +186 -0
- package/dist/core/engine/context-prefix.js +155 -0
- package/dist/core/engine/index.js +1 -1
- package/dist/core/engine/intensity.js +158 -0
- package/dist/core/engine/intent.js +260 -0
- package/dist/core/engine/native-pugi.js +1295 -227
- package/dist/core/engine/prompts.js +129 -19
- package/dist/core/engine/strip-internal-fields.js +124 -0
- package/dist/core/engine/tool-bridge.js +1731 -59
- package/dist/core/evaluation/golden-dataset.js +293 -0
- package/dist/core/feedback/queue.js +177 -0
- package/dist/core/feedback/submitter.js +145 -0
- package/dist/core/file-cache.js +113 -1
- package/dist/core/flatten/flatten-repo.js +439 -0
- package/dist/core/format/osc8-link.js +28 -0
- package/dist/core/hook-chains.js +392 -0
- package/dist/core/hooks/citation-verify-hook.js +138 -0
- package/dist/core/hooks/citation-verify.js +112 -0
- package/dist/core/hooks/events.js +46 -0
- package/dist/core/hooks/index.js +15 -0
- package/dist/core/hooks/registry.js +216 -0
- package/dist/core/hooks/runner.js +236 -0
- package/dist/core/hooks/v2/event-emitter.js +115 -0
- package/dist/core/hooks/v2/executor.js +282 -0
- package/dist/core/hooks/v2/index.js +25 -0
- package/dist/core/hooks/v2/lifecycle.js +104 -0
- package/dist/core/hooks/v2/loader.js +216 -0
- package/dist/core/hooks/v2/matcher.js +125 -0
- package/dist/core/hooks/v2/trust.js +143 -0
- package/dist/core/hooks/v2/types.js +86 -0
- package/dist/core/hooks/worktree-events.js +158 -0
- package/dist/core/image/renderer.js +71 -0
- package/dist/core/init/detector.js +582 -0
- package/dist/core/init/template-renderer.js +242 -0
- package/dist/core/jobs/registry.js +18 -18
- package/dist/core/ledger/results-tsv.js +142 -0
- package/dist/core/log-discipline/stdout-redirect.js +51 -0
- package/dist/core/lsp/cache.js +105 -0
- package/dist/core/lsp/client.js +551 -41
- package/dist/core/lsp/language-detect.js +66 -0
- package/dist/core/lsp/post-edit-diagnostics.js +171 -0
- package/dist/core/lsp/server-detect.js +173 -0
- package/dist/core/lsp/symbol-cache.js +162 -0
- package/dist/core/lsp/symbol-tools.js +664 -0
- package/dist/core/mcp/client.js +97 -28
- package/dist/core/mcp/http-server.js +553 -0
- package/dist/core/mcp/orchestrator-tools.js +662 -0
- package/dist/core/mcp/permission.js +190 -0
- package/dist/core/mcp/registry.js +39 -17
- package/dist/core/mcp/server-tools.js +219 -0
- package/dist/core/mcp/server.js +397 -0
- package/dist/core/mcp/trust.js +10 -10
- package/dist/core/memory/dual-write.js +416 -0
- package/dist/core/memory/passive-extract.js +130 -0
- package/dist/core/memory/phase1-kinds.js +20 -0
- package/dist/core/memory/secret-scanner.js +304 -0
- package/dist/core/memory-sync/queue.js +170 -0
- package/dist/core/metrics/extract.js +113 -0
- package/dist/core/modes/roo-modes.js +68 -0
- package/dist/core/onboarding/ensure-initialized.js +133 -0
- package/dist/core/onboarding/marker.js +111 -0
- package/dist/core/onboarding/telemetry-state.js +108 -0
- package/dist/core/output-style/presets.js +176 -0
- package/dist/core/output-style/state.js +185 -0
- package/dist/core/path-security.js +287 -5
- package/dist/core/permission.js +82 -22
- package/dist/core/permissions/auto-classifier.js +124 -0
- package/dist/core/permissions/bash-parser.js +371 -0
- package/dist/core/permissions/circuit-breaker.js +83 -0
- package/dist/core/permissions/constrained-edit.js +91 -0
- package/dist/core/permissions/gate.js +278 -0
- package/dist/core/permissions/index.js +20 -0
- package/dist/core/permissions/mode.js +174 -0
- package/dist/core/permissions/network-egress.js +137 -0
- package/dist/core/permissions/state.js +241 -0
- package/dist/core/permissions/tool-class.js +93 -0
- package/dist/core/plan-mode/ui-state.js +51 -0
- package/dist/core/plans/plan-artifact.js +721 -0
- package/dist/core/policy-limits/etag-store.js +122 -0
- package/dist/core/prd-check/parser.js +215 -0
- package/dist/core/prd-check/reporter.js +127 -0
- package/dist/core/prd-check/session-review.js +557 -0
- package/dist/core/prd-check/verifiers.js +223 -0
- package/dist/core/prompt-cache/client-cache.js +99 -0
- package/dist/core/prompts/assembly.js +29 -0
- package/dist/core/prompts/registry.js +364 -0
- package/dist/core/pugi-md/cc-compat-rules.js +735 -0
- package/dist/core/pugi-md/context-injector.js +76 -0
- package/dist/core/pugi-md/walk-up.js +207 -0
- package/dist/core/python/uv-installer.js +270 -0
- package/dist/core/python/uv-resolver.js +83 -0
- package/dist/core/rate-limit/narrator.js +146 -0
- package/dist/core/recipes/cli-types.js +20 -0
- package/dist/core/recipes/loader.js +103 -0
- package/dist/core/recipes/runner.js +345 -0
- package/dist/core/recipes/schema.js +587 -0
- package/dist/core/release-notes/parser.js +241 -0
- package/dist/core/release-notes/state.js +116 -0
- package/dist/core/repl/ask.js +37 -37
- package/dist/core/repl/cancellation.js +26 -26
- package/dist/core/repl/cap-warning.js +4 -4
- package/dist/core/repl/clipboard-read.js +11 -11
- package/dist/core/repl/dispatch-fsm.js +12 -12
- package/dist/core/repl/history-search.js +15 -15
- package/dist/core/repl/history.js +28 -18
- package/dist/core/repl/kill-ring.js +5 -5
- package/dist/core/repl/model-pricing.js +135 -0
- package/dist/core/repl/privacy-banner.js +22 -22
- package/dist/core/repl/session.js +2148 -217
- package/dist/core/repl/slash-commands.js +501 -41
- package/dist/core/repl/store/index.js +1 -1
- package/dist/core/repl/store/jsonl-log.js +22 -22
- package/dist/core/repl/store/lockfile.js +10 -10
- package/dist/core/repl/store/session-store.js +136 -107
- package/dist/core/repl/store/types.js +15 -15
- package/dist/core/repl/store/uuid-v7.js +12 -12
- package/dist/core/repl/workspace-context.js +43 -21
- package/dist/core/repo-map/build.js +125 -0
- package/dist/core/repo-map/cache.js +185 -0
- package/dist/core/repo-map/extractor.js +254 -0
- package/dist/core/repo-map/formatter.js +145 -0
- package/dist/core/repo-map/page-rank.js +105 -0
- package/dist/core/repo-map/scanner.js +211 -0
- package/dist/core/retry-budget/budget.js +284 -0
- package/dist/core/retry-budget/index.js +5 -0
- package/dist/core/retry-budget/retry-cap.js +74 -0
- package/dist/core/routing/lead-worker.js +43 -0
- package/dist/core/routing/pre-flight-estimator.js +108 -0
- package/dist/core/runs/run-tree.js +103 -0
- package/dist/core/security/injection-scanner.js +367 -0
- package/dist/core/security/output-filter.js +418 -0
- package/dist/core/session/env-file.js +105 -0
- package/dist/core/session/section-budgets.js +140 -0
- package/dist/core/session.js +92 -0
- package/dist/core/settings.js +324 -5
- package/dist/core/share/formatter.js +271 -0
- package/dist/core/share/redactor.js +221 -0
- package/dist/core/share/uploader.js +267 -0
- package/dist/core/skills/defaults.js +30 -30
- package/dist/core/skills/loader.js +22 -22
- package/dist/core/skills/sources.js +27 -27
- package/dist/core/smoke/headless-driver.js +174 -0
- package/dist/core/smoke/orchestrator.js +194 -0
- package/dist/core/smoke/runner.js +238 -0
- package/dist/core/smoke/scenario-parser.js +316 -0
- package/dist/core/statusline.js +99 -0
- package/dist/core/subagents/dispatcher-real.js +600 -0
- package/dist/core/subagents/dispatcher.js +132 -43
- package/dist/core/subagents/index.js +19 -6
- package/dist/core/subagents/isolation-matrix.js +213 -0
- package/dist/core/subagents/spawn.js +19 -4
- package/dist/core/telemetry/emitter.js +229 -0
- package/dist/core/telemetry/queue.js +251 -0
- package/dist/core/theme/context.js +91 -0
- package/dist/core/theme/presets.js +228 -0
- package/dist/core/theme/state.js +181 -0
- package/dist/core/todos/invariant.js +10 -0
- package/dist/core/todos/state.js +177 -0
- package/dist/core/tool-schema/compressor.js +89 -0
- package/dist/core/transport/version-interceptor.js +166 -0
- package/dist/core/trust.js +2 -2
- package/dist/core/tui/thinking-block.js +64 -0
- package/dist/core/vim/keymap.js +288 -0
- package/dist/core/vim/state.js +92 -0
- package/dist/core/watch-markers/marker-watcher.js +133 -0
- package/dist/core/worktree/include-parser.js +249 -0
- package/dist/core/worktree-manager/cleanup.js +123 -0
- package/dist/core/worktree-manager/manager.js +303 -0
- package/dist/index.js +36 -0
- package/dist/runtime/bootstrap.js +190 -0
- package/dist/runtime/cli.js +4185 -549
- package/dist/runtime/commands/agents.js +31 -31
- package/dist/runtime/commands/budget.js +5 -5
- package/dist/runtime/commands/cancel.js +231 -0
- package/dist/runtime/commands/chain.js +489 -0
- package/dist/runtime/commands/codegraph-status.js +227 -0
- package/dist/runtime/commands/compact.js +297 -0
- package/dist/runtime/commands/config.js +73 -39
- package/dist/runtime/commands/cost.js +199 -0
- package/dist/runtime/commands/delegate.js +27 -4
- package/dist/runtime/commands/dispatch.js +126 -0
- package/dist/runtime/commands/doctor.js +579 -0
- package/dist/runtime/commands/feedback.js +184 -0
- package/dist/runtime/commands/hooks.js +187 -0
- package/dist/runtime/commands/init.js +254 -0
- package/dist/runtime/commands/lsp.js +200 -38
- package/dist/runtime/commands/mcp.js +879 -0
- package/dist/runtime/commands/memory.js +582 -0
- package/dist/runtime/commands/model.js +237 -0
- package/dist/runtime/commands/onboarding.js +275 -0
- package/dist/runtime/commands/patch.js +12 -12
- package/dist/runtime/commands/permissions.js +112 -0
- package/dist/runtime/commands/plan.js +143 -0
- package/dist/runtime/commands/prd-check.js +285 -0
- package/dist/runtime/commands/privacy.js +17 -17
- package/dist/runtime/commands/recipe.js +325 -0
- package/dist/runtime/commands/redo-blob-store.js +92 -0
- package/dist/runtime/commands/redo.js +361 -0
- package/dist/runtime/commands/release-notes.js +229 -0
- package/dist/runtime/commands/repo-map.js +95 -0
- package/dist/runtime/commands/report.js +299 -0
- package/dist/runtime/commands/resume.js +118 -0
- package/dist/runtime/commands/review-consensus.js +68 -53
- package/dist/runtime/commands/rewind.js +333 -0
- package/dist/runtime/commands/roster.js +14 -14
- package/dist/runtime/commands/sessions.js +163 -0
- package/dist/runtime/commands/share.js +316 -0
- package/dist/runtime/commands/skills.js +31 -31
- package/dist/runtime/commands/status.js +186 -0
- package/dist/runtime/commands/stickers.js +82 -0
- package/dist/runtime/commands/style.js +194 -0
- package/dist/runtime/commands/theme.js +196 -0
- package/dist/runtime/commands/undo.js +54 -22
- package/dist/runtime/commands/update.js +289 -0
- package/dist/runtime/commands/vim.js +140 -0
- package/dist/runtime/commands/worktree.js +8 -8
- package/dist/runtime/commands/worktrees.js +155 -0
- package/dist/runtime/headless-repl.js +195 -0
- package/dist/runtime/headless.js +543 -0
- package/dist/runtime/load-hooks-or-exit.js +71 -0
- package/dist/runtime/plan-decompose.js +22 -22
- package/dist/runtime/sigint-guard.js +272 -0
- package/dist/runtime/update-check.js +28 -28
- package/dist/runtime/version.js +65 -0
- package/dist/runtime/worktree-bootstrap.js +579 -0
- package/dist/skills/bundled/batch.js +617 -0
- package/dist/skills/bundled/index.js +45 -0
- package/dist/skills/bundled/loop.js +358 -0
- package/dist/skills/bundled/remember.js +383 -0
- package/dist/skills/bundled/simplify.js +289 -0
- package/dist/skills/bundled/skillify.js +373 -0
- package/dist/skills/bundled/stuck.js +558 -0
- package/dist/skills/bundled/verify.js +439 -0
- package/dist/testing/vcr.js +486 -0
- package/dist/tools/agent-tool.js +229 -0
- package/dist/tools/apply-patch.js +89 -28
- package/dist/tools/ask-user-question.js +337 -0
- package/dist/tools/ask-user.js +115 -0
- package/dist/tools/bash.js +624 -46
- package/dist/tools/brief.js +224 -0
- package/dist/tools/enter-worktree.js +250 -0
- package/dist/tools/exit-worktree.js +147 -0
- package/dist/tools/file-tools.js +161 -44
- package/dist/tools/lsp-tools.js +377 -1
- package/dist/tools/mcp-tool.js +260 -0
- package/dist/tools/multi-edit.js +361 -0
- package/dist/tools/powershell.js +268 -0
- package/dist/tools/registry.js +86 -4
- package/dist/tools/skill-tool.js +96 -0
- package/dist/tools/sleep.js +99 -0
- package/dist/tools/synthetic-output.js +133 -0
- package/dist/tools/tasks.js +208 -0
- package/dist/tools/todo-write.js +184 -0
- package/dist/tools/verify-plan-execution.js +295 -0
- package/dist/tools/web-fetch-injection-scanner.js +207 -0
- package/dist/tools/web-fetch.js +195 -10
- package/dist/tools/web-search.js +458 -0
- package/dist/tui/agent-progress-card.js +111 -0
- package/dist/tui/agent-tree.js +11 -1
- package/dist/tui/ask-modal.js +14 -14
- package/dist/tui/ask-user-question-chips.js +315 -0
- package/dist/tui/ask-user-question-prompt.js +203 -0
- package/dist/tui/compact-banner.js +81 -0
- package/dist/tui/conversation-pane.js +85 -11
- package/dist/tui/cost-table.js +111 -0
- package/dist/tui/device-flow.js +2 -2
- package/dist/tui/doctor-table.js +46 -0
- package/dist/tui/feedback-prompt.js +156 -0
- package/dist/tui/input-box.js +247 -32
- package/dist/tui/login-picker.js +3 -3
- package/dist/tui/markdown-render.js +6 -6
- package/dist/tui/onboarding-wizard.js +240 -0
- package/dist/tui/permissions-picker.js +86 -0
- package/dist/tui/render.js +36 -1
- package/dist/tui/repl-render.js +176 -25
- package/dist/tui/repl-splash-art.js +16 -16
- package/dist/tui/repl-splash-mascot.js +48 -24
- package/dist/tui/repl-splash.js +22 -22
- package/dist/tui/repl.js +125 -45
- package/dist/tui/slash-palette.js +6 -6
- package/dist/tui/splash.js +2 -2
- package/dist/tui/status-bar.js +109 -31
- package/dist/tui/status-table.js +7 -0
- package/dist/tui/stickers-art.js +136 -0
- package/dist/tui/style-table.js +28 -0
- package/dist/tui/theme-table.js +29 -0
- package/dist/tui/thinking-spinner.js +123 -0
- package/dist/tui/tool-stream-pane.js +53 -4
- package/dist/tui/update-banner.js +27 -2
- package/dist/tui/vim-input.js +267 -0
- package/dist/tui/welcome-banner.js +107 -0
- package/dist/tui/welcome-data.js +293 -0
- package/dist/tui/workspace-context.js +2 -2
- package/package.json +31 -16
- package/test/scenarios/codegen-create-file.scenario.txt +13 -0
- package/test/scenarios/compact-force.scenario.txt +12 -0
- package/test/scenarios/identity.scenario.txt +12 -0
- package/test/scenarios/persona-handoff.scenario.txt +12 -0
- package/test/scenarios/walkback.scenario.txt +12 -0
- package/dist/core/engine/compaction-hook.js +0 -154
package/dist/tui/repl.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
/**
|
|
3
|
-
* REPL root component - Sprint
|
|
3
|
+
* REPL root component - Sprint .
|
|
4
4
|
*
|
|
5
5
|
* Three-zone layout:
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* header - `Pugi.io · workspace: <name> · v<X> on watch`
|
|
8
|
+
* main - conversation pane (top half) + agent tree (bottom half)
|
|
9
|
+
* footer - input box + status bar + key hints
|
|
10
10
|
*
|
|
11
11
|
* The component subscribes to a ReplSession instance for state. It does
|
|
12
12
|
* NOT own the SSE client or the transport - the session module does.
|
|
@@ -26,14 +26,17 @@ import { ConversationPane } from './conversation-pane.js';
|
|
|
26
26
|
import { InputBox } from './input-box.js';
|
|
27
27
|
import { ReplSplash } from './repl-splash.js';
|
|
28
28
|
import { StatusBar } from './status-bar.js';
|
|
29
|
+
import { ThinkingSpinner } from './thinking-spinner.js';
|
|
29
30
|
import { ToolStreamPane } from './tool-stream-pane.js';
|
|
30
31
|
import { UpdateBanner } from './update-banner.js';
|
|
32
|
+
import { WelcomeBanner } from './welcome-banner.js';
|
|
31
33
|
import { collectWorkspaceContext } from './workspace-context.js';
|
|
34
|
+
import { useTheme } from '../core/theme/context.js';
|
|
32
35
|
import { slugForCwd } from '../core/repl/history.js';
|
|
33
36
|
import { SLASH_COMMAND_HELP, SLASH_COMMAND_GROUPS } from '../core/repl/slash-commands.js';
|
|
34
37
|
const TICK_INTERVAL_MS = 200;
|
|
35
38
|
const PULSE_INTERVAL_MS = 700;
|
|
36
|
-
//
|
|
39
|
+
// : maximum transcript rows the conversation pane renders at once.
|
|
37
40
|
// Older rows scroll off the top; full history stays in session state.
|
|
38
41
|
const CONVERSATION_WINDOW = 12;
|
|
39
42
|
export function Repl(props) {
|
|
@@ -41,22 +44,28 @@ export function Repl(props) {
|
|
|
41
44
|
const [overlay, setOverlay] = useState('none');
|
|
42
45
|
const [pulsePhase, setPulsePhase] = useState(0);
|
|
43
46
|
const [tickNow, setTickNow] = useState((props.now ?? Date.now)());
|
|
44
|
-
//
|
|
47
|
+
// : operator-driven collapse for the tool stream pane. The CLI
|
|
45
48
|
// host can hide the pane entirely via `--no-tool-stream`; this state
|
|
46
49
|
// is the runtime toggle (Ctrl+T) for operators who want the pane on
|
|
47
50
|
// screen but folded to a single row while they read a long reply.
|
|
48
51
|
const [toolStreamCollapsed, setToolStreamCollapsed] = useState(false);
|
|
49
|
-
//
|
|
52
|
+
// wave 3: boot splash visible until first input, first
|
|
50
53
|
// `agent.spawned` event, or 10s idle. The host gates the initial
|
|
51
54
|
// visibility on `--no-splash` / PUGI_SKIP_SPLASH via `skipSplash`.
|
|
52
|
-
//
|
|
53
|
-
// (parity with
|
|
55
|
+
// CEO dogfood: default splash to HIDDEN at boot
|
|
56
|
+
// (parity with the upstream tool's minimal one-line banner). Operator can
|
|
54
57
|
// opt back in via `/splash` slash. The chafa pug pre-print + header
|
|
55
58
|
// line already give the brand cue without the multi-row Plan/Model/
|
|
56
59
|
// Tenant block crowding the top.
|
|
57
60
|
const [splashVisible, setSplashVisible] = useState(false);
|
|
58
61
|
const dismissSplash = useCallback(() => setSplashVisible(false), []);
|
|
59
|
-
//
|
|
62
|
+
// CEO P0 #2 : welcome banner. Visible from boot
|
|
63
|
+
// until the operator submits the first brief OR the session emits
|
|
64
|
+
// its first agent event. The host owns dismissal lifecycle (kept
|
|
65
|
+
// symmetric with the splash) so the welcome card never lingers
|
|
66
|
+
// behind а live transcript.
|
|
67
|
+
const [welcomeVisible, setWelcomeVisible] = useState(Boolean(props.welcomeData));
|
|
68
|
+
// wave 3: workspace context snapshot for the status bar. We
|
|
60
69
|
// read once at mount and freeze; a brand-new PUGI.md or skill is
|
|
61
70
|
// surfaced on the next REPL boot rather than via a watcher.
|
|
62
71
|
const workspaceContext = useMemo(() => props.workspaceContext ?? collectWorkspaceContext(process.cwd()), [props.workspaceContext]);
|
|
@@ -88,9 +97,9 @@ export function Repl(props) {
|
|
|
88
97
|
useEffect(() => {
|
|
89
98
|
props.onOverlayChange?.(overlay);
|
|
90
99
|
}, [overlay, props]);
|
|
91
|
-
//
|
|
100
|
+
// wave 3: dismiss the boot splash once the first agent spawns
|
|
92
101
|
// (the operator has clearly engaged the system) or the transcript
|
|
93
|
-
// gains a row. Mirrors the natural attention shift
|
|
102
|
+
// gains a row. Mirrors the natural attention shift the upstream tool /
|
|
94
103
|
// Codex / Gemini CLI all do on their boot screens.
|
|
95
104
|
useEffect(() => {
|
|
96
105
|
if (!splashVisible)
|
|
@@ -99,6 +108,20 @@ export function Repl(props) {
|
|
|
99
108
|
setSplashVisible(false);
|
|
100
109
|
}
|
|
101
110
|
}, [splashVisible, state.agents.length, state.transcript.length]);
|
|
111
|
+
// CEO P0 #2 v2: welcome banner stays until the operator
|
|
112
|
+
// actively engages the loop — first agent spawn. Boot-time auto-init
|
|
113
|
+
// emits system rows into `state.transcript` (skip-trust hints, dirty
|
|
114
|
+
// tree warnings) which used к dismiss the banner within ~2s, hiding
|
|
115
|
+
// the brand mascot before the operator could read it. Drop the
|
|
116
|
+
// `transcript.length` trigger; agent spawn (= real dispatch) remains
|
|
117
|
+
// the sole signal that the operator stopped reading the banner.
|
|
118
|
+
useEffect(() => {
|
|
119
|
+
if (!welcomeVisible)
|
|
120
|
+
return;
|
|
121
|
+
if (state.agents.length > 0) {
|
|
122
|
+
setWelcomeVisible(false);
|
|
123
|
+
}
|
|
124
|
+
}, [welcomeVisible, state.agents.length]);
|
|
102
125
|
const personaNames = useMemo(() => buildPersonaNameMap(), []);
|
|
103
126
|
const { exit } = useApp();
|
|
104
127
|
const handleSubmit = useCallback((line) => {
|
|
@@ -106,6 +129,10 @@ export function Repl(props) {
|
|
|
106
129
|
// `setSplashVisible(false)` is a no-op once the state already
|
|
107
130
|
// settled to false (timer fired or `agent.spawned` arrived).
|
|
108
131
|
setSplashVisible(false);
|
|
132
|
+
// CEO P0 #2 : same dismissal for the welcome banner
|
|
133
|
+
// — the operator engaging the input box is the cleanest signal
|
|
134
|
+
// they have finished reading the boot card.
|
|
135
|
+
setWelcomeVisible(false);
|
|
109
136
|
// Run async without awaiting - the session module owns the
|
|
110
137
|
// network call, errors land in the transcript automatically.
|
|
111
138
|
void props.session.handleInput(line).then((verdict) => {
|
|
@@ -137,7 +164,7 @@ export function Repl(props) {
|
|
|
137
164
|
setOverlay('none');
|
|
138
165
|
}
|
|
139
166
|
}, { isActive: overlay === 'help' || overlay === 'roster' });
|
|
140
|
-
//
|
|
167
|
+
// : Ctrl+T toggles the tool stream pane between expanded and
|
|
141
168
|
// collapsed states. Active only while no overlay is open, so the
|
|
142
169
|
// toggle never fights the help/roster dismiss handler. The input box
|
|
143
170
|
// owns its own raw-input mode, so this listener only fires on the
|
|
@@ -147,7 +174,7 @@ export function Repl(props) {
|
|
|
147
174
|
setToolStreamCollapsed((prev) => !prev);
|
|
148
175
|
}
|
|
149
176
|
}, { isActive: overlay === 'none' && props.hideToolStream !== true });
|
|
150
|
-
//
|
|
177
|
+
// office-hours: a pending ask or plan-review modal pauses input
|
|
151
178
|
// until the operator resolves it. The modal owns its own useInput
|
|
152
179
|
// hook, so the InputBox unmounts while a modal is open to avoid two
|
|
153
180
|
// raw-input listeners competing for the same keystroke. Resolution
|
|
@@ -161,29 +188,69 @@ export function Repl(props) {
|
|
|
161
188
|
const handlePlanReviewResolve = useCallback((result) => {
|
|
162
189
|
void props.session.resolvePlanReview(result);
|
|
163
190
|
}, [props.session]);
|
|
164
|
-
//
|
|
191
|
+
// : Ctrl+C abort handler. Forwards to ReplSession.cancel() which
|
|
165
192
|
// aborts the in-flight dispatch, closes the SSE stream, and surfaces
|
|
166
193
|
// "Aborted." in the transcript.
|
|
167
194
|
//
|
|
168
195
|
// Return contract (consumed by InputBox):
|
|
169
|
-
//
|
|
170
|
-
//
|
|
171
|
-
//
|
|
172
|
-
//
|
|
173
|
-
//
|
|
174
|
-
//
|
|
175
|
-
//
|
|
176
|
-
//
|
|
177
|
-
//
|
|
178
|
-
//
|
|
179
|
-
//
|
|
180
|
-
//
|
|
196
|
+
// - true - dispatch was cancelled (keep the buffer + DO arm
|
|
197
|
+
// the press-again-to-exit timer; second Ctrl+C in
|
|
198
|
+
// the window exits).
|
|
199
|
+
// - false - idle / nothing to cancel (legacy: clear buffer +
|
|
200
|
+
// arm the exit timer so the operator sees the hint
|
|
201
|
+
// and can confirm exit on the next press).
|
|
202
|
+
// - undefined - bypassed entirely (e.g. a modal owns the input).
|
|
203
|
+
// InputBox MUST NOT arm the exit timer and MUST
|
|
204
|
+
// NOT clear the buffer. P2 fix: previously this
|
|
205
|
+
// returned `false` and the buffer-clear path wiped
|
|
206
|
+
// the operator's mid-typed modal text on the first
|
|
207
|
+
// Ctrl+C, costing a press of work.
|
|
181
208
|
const handleCancel = useCallback(() => {
|
|
182
209
|
if (modalActive)
|
|
183
210
|
return undefined;
|
|
184
211
|
return props.session.cancel();
|
|
185
212
|
}, [props.session, modalActive]);
|
|
186
|
-
//
|
|
213
|
+
// BT 8 (the upstream tool parity): Esc-Esc walkback. Forwards to
|
|
214
|
+
// ReplSession.walkbackLastTurn which trims the trailing operator
|
|
215
|
+
// turn + its persona response from the in-memory transcript. Returns
|
|
216
|
+
// `'walked-back'` so the input box knows the host did the work;
|
|
217
|
+
// `'nothing'` covers both the empty-transcript and dispatch-in-flight
|
|
218
|
+
// refusals (the session module owns the refusal copy in both cases).
|
|
219
|
+
const handleWalkback = useCallback(() => {
|
|
220
|
+
if (modalActive)
|
|
221
|
+
return 'nothing';
|
|
222
|
+
const verdict = props.session.walkbackLastTurn();
|
|
223
|
+
return verdict === 'walked-back' ? 'walked-back' : 'nothing';
|
|
224
|
+
}, [props.session, modalActive]);
|
|
225
|
+
// — Shift+Tab cycles the 6 canonical permission modes (CC
|
|
226
|
+
// parity). Refuses while a modal is active so the operator does not
|
|
227
|
+
// accidentally flip mode mid-prompt; otherwise resolves the current
|
|
228
|
+
// mode through the workspace > global > default merge, advances via
|
|
229
|
+
// `nextPermissionMode`, и persists к .pugi/session.json. Returns the
|
|
230
|
+
// new mode string so the InputBox can flash a one-line toast.
|
|
231
|
+
const handleCyclePermissionMode = useCallback(() => {
|
|
232
|
+
if (modalActive)
|
|
233
|
+
return null;
|
|
234
|
+
try {
|
|
235
|
+
// Lazy-require так this code path doesn't drag the permissions
|
|
236
|
+
// module into the splash + boot stages where it isn't needed.
|
|
237
|
+
// The require is sync but the inner work is pure JSON IO.
|
|
238
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
239
|
+
const perm = require('../core/permissions/index.js');
|
|
240
|
+
const workspaceRoot = process.cwd();
|
|
241
|
+
const current = perm.resolveMode({ workspaceRoot });
|
|
242
|
+
const next = perm.nextPermissionMode(current);
|
|
243
|
+
perm.setCurrentMode(workspaceRoot, next);
|
|
244
|
+
return next;
|
|
245
|
+
}
|
|
246
|
+
catch {
|
|
247
|
+
// Persistence is best-effort — if .pugi/session.json is read-only
|
|
248
|
+
// или ENOENT-on-parent the toast is suppressed so we don't lie
|
|
249
|
+
// about the flip к the operator.
|
|
250
|
+
return null;
|
|
251
|
+
}
|
|
252
|
+
}, [modalActive]);
|
|
253
|
+
// CEO dogfood (parity with the upstream tool): input
|
|
187
254
|
// box pinned to alt-screen BOTTOM, conversation grows above it.
|
|
188
255
|
// Beta.3's height={rows} fix broke keystroke focus - raw echo at
|
|
189
256
|
// viewport bottom. The right pattern is minHeight on the root +
|
|
@@ -191,37 +258,48 @@ export function Repl(props) {
|
|
|
191
258
|
// input, and the input stays the sole focusable surface adjacent
|
|
192
259
|
// to the cursor row, so all keystrokes route through it.
|
|
193
260
|
const altScreenRows = process.stdout.rows ?? 24;
|
|
194
|
-
return (_jsxs(Box, { flexDirection: "column", paddingX: 1, minHeight: altScreenRows, children: [props.
|
|
261
|
+
return (_jsxs(Box, { flexDirection: "column", paddingX: 1, minHeight: altScreenRows, children: [welcomeVisible && props.welcomeData ? (_jsx(WelcomeBanner, { data: props.welcomeData, mascotPrePrinted: props.mascotPrePrinted === true, autoInitStatus: props.autoInitStatus ?? null })) : null, splashVisible ? (_jsx(ReplSplash, { cliVersion: state.cliVersion, workspaceLabel: state.workspaceLabel, plan: props.splashPlan, model: props.splashModel, tenant: props.splashTenant, onDismiss: dismissSplash, mascotPrePrinted: props.mascotPrePrinted === true })) : null, _jsx(Header, { state: state }), _jsx(Box, { flexDirection: "column", marginTop: 1, flexGrow: 1, justifyContent: "flex-end", children: overlay === 'help' ? (_jsx(HelpOverlay, {})) : overlay === 'roster' ? (_jsx(RosterOverlay, {})) : overlay === 'farewell' ? (_jsx(FarewellOverlay, {})) : (_jsx(MainArea, { state: state, personaNames: personaNames, nowEpochMs: tickNow, hideToolStream: props.hideToolStream === true, toolStreamCollapsed: toolStreamCollapsed })) }), state.pendingAsk ? (_jsx(Box, { marginTop: 1, children: _jsx(AskModal, { tag: state.pendingAsk, onResolve: handleAskResolve }) })) : null, state.pendingPlanReview ? (_jsx(Box, { marginTop: 1, children: _jsx(PlanReviewModal, { tag: state.pendingPlanReview, onResolve: handlePlanReviewResolve }) })) : null, _jsxs(Box, { flexDirection: "column", marginTop: 1, children: [overlay === 'farewell' || modalActive ? null : (_jsx(InputBox, { onSubmit: handleSubmit, onExit: handleExit, onCancel: handleCancel, onWalkback: handleWalkback, onCyclePermissionMode: handleCyclePermissionMode, now: props.now,
|
|
195
262
|
// Slug from process.cwd() (full path) so two workspaces with
|
|
196
263
|
// the same basename do not share history. state.workspaceLabel
|
|
197
264
|
// is the basename only. Codex review P2.
|
|
198
|
-
workspaceSlug: slugForCwd(process.cwd()) })), _jsx(StatusBar, { connection: state.connection, activeAgentCount: countActive(state), tokensDownstreamTotal: state.tokensDownstreamTotal, briefStartedAtEpochMs: state.briefStartedAtEpochMs, nowEpochMs: tickNow, pulsePhase: pulsePhase, pugiMdCount: workspaceContext.pugiMdCount, mcpServerCount: workspaceContext.mcpServerCount, skillCount: workspaceContext.skillCount, quotaPct: props.quotaPct, dispatchState: state.dispatchState, dispatchToolLabel: state.dispatchToolLabel
|
|
265
|
+
workspaceSlug: slugForCwd(process.cwd()) })), _jsx(ThinkingSpinner, { dispatchState: state.dispatchState, dispatchToolLabel: state.dispatchToolLabel }), _jsx(StatusBar, { connection: state.connection, activeAgentCount: countActive(state), tokensDownstreamTotal: state.tokensDownstreamTotal, briefStartedAtEpochMs: state.briefStartedAtEpochMs, nowEpochMs: tickNow, pulsePhase: pulsePhase, pugiMdCount: workspaceContext.pugiMdCount, mcpServerCount: workspaceContext.mcpServerCount, skillCount: workspaceContext.skillCount, quotaPct: props.quotaPct, dispatchState: state.dispatchState, dispatchToolLabel: state.dispatchToolLabel, lastCompletedOutcome: state.lastCompletedOutcome,
|
|
266
|
+
// cost-meter sprint — surface accumulated session totals
|
|
267
|
+
// + per-turn delta flash on the status bar's top row. The
|
|
268
|
+
// session module owns accumulation; the bar is a pure render.
|
|
269
|
+
sessionTokensIn: state.sessionTokensIn, sessionTokensOut: state.sessionTokensOut, sessionCostUsd: state.sessionCostUsd, sessionStartedAtEpochMs: state.sessionStartedAtEpochMs, lastTurnDelta: state.lastTurnDelta }), props.updateBanner ? _jsx(UpdateBanner, { result: props.updateBanner }) : null] })] }));
|
|
199
270
|
}
|
|
200
271
|
function Header({ state }) {
|
|
201
|
-
|
|
272
|
+
// the header `.io` brand accent + connection
|
|
273
|
+
// pill route through `useTheme()` so the operator's `/theme` flip
|
|
274
|
+
// (default / dark / light / colorblind) re-tints the chrome on
|
|
275
|
+
// re-mount. The `useTheme` hook returns the `default` preset's
|
|
276
|
+
// colors when no provider is mounted, preserving the previous
|
|
277
|
+
// `#3da9fc` constants for tests that import `<Repl />` standalone.
|
|
278
|
+
const theme = useTheme();
|
|
279
|
+
return (_jsxs(Box, { children: [_jsx(Text, { bold: true, children: "Pugi" }), _jsx(Text, { bold: true, color: theme.accent, children: ".io" }), _jsx(Text, { dimColor: true, children: ` · workspace: ${state.workspaceLabel} · v${state.cliVersion} · ` }), _jsx(Text, { color: theme.accent, children: state.connection === 'on_watch' ? 'on watch' : state.connection.replace('_', ' ') })] }));
|
|
202
280
|
}
|
|
203
281
|
function MainArea({ state, personaNames, nowEpochMs, hideToolStream, toolStreamCollapsed, }) {
|
|
204
|
-
//
|
|
282
|
+
// : three vertical panes stacked above the input box.
|
|
205
283
|
//
|
|
206
|
-
//
|
|
207
|
-
//
|
|
208
|
-
//
|
|
209
|
-
//
|
|
210
|
-
//
|
|
211
|
-
//
|
|
212
|
-
//
|
|
284
|
+
// 1. Conversation pane (top) - transcript with Markdown render.
|
|
285
|
+
// 2. Tool stream pane (mid) - live Read/Edit/Bash/Grep lines.
|
|
286
|
+
// Hidden when `--no-tool-stream` is
|
|
287
|
+
// set; collapsed via Ctrl+T while
|
|
288
|
+
// the pane is visible.
|
|
289
|
+
// 3. Agent tree pane (bottom) - Cyber-Zoo roster with persona /
|
|
290
|
+
// status / duration / token counts.
|
|
213
291
|
//
|
|
214
292
|
// The window over the transcript is small (last 12 rows) so the
|
|
215
293
|
// bottom of the frame stays anchored to the input box. New agents
|
|
216
|
-
// push the operator line up the screen, mirroring
|
|
217
|
-
//
|
|
294
|
+
// push the operator line up the screen, mirroring the upstream tool /
|
|
295
|
+
// peer CLI / Gemini CLI rendering.
|
|
218
296
|
const conversationSlice = state.transcript.slice(-CONVERSATION_WINDOW);
|
|
219
297
|
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(ConversationPane, { rows: conversationSlice, personaNames: personaNames }), hideToolStream ? null : (_jsx(Box, { marginTop: 1, children: _jsx(ToolStreamPane, { calls: state.toolCalls, collapsed: toolStreamCollapsed }) })), _jsx(Box, { marginTop: 1, children: _jsx(AgentTreePane, { agents: state.agents, nowEpochMs: nowEpochMs }) })] }));
|
|
220
298
|
}
|
|
221
299
|
function HelpOverlay() {
|
|
222
300
|
// Group commands by their `group` field so the operator scans the
|
|
223
301
|
// palette by intent (dispatch → session → tools → settings → meta).
|
|
224
|
-
// The
|
|
302
|
+
// The wave-2 expansion grew the surface from 6 to 20 commands;
|
|
225
303
|
// a flat list would force the operator to read 20 rows top-to-bottom
|
|
226
304
|
// every time. Grouping cuts perceived complexity dramatically.
|
|
227
305
|
const grouped = new Map();
|
|
@@ -238,14 +316,14 @@ function HelpOverlay() {
|
|
|
238
316
|
const rows = grouped.get(group);
|
|
239
317
|
if (!rows || rows.length === 0)
|
|
240
318
|
return null;
|
|
241
|
-
return (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Text, { dimColor: true, children: `
|
|
319
|
+
return (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Text, { dimColor: true, children: ` -- ${group} --` }), rows.map((row) => (_jsxs(Box, { children: [_jsx(Text, { bold: true, color: "#3da9fc", children: ` /${row.name} ${row.args}`.padEnd(22, ' ') }), _jsx(Text, { dimColor: true, children: row.gloss })] }, row.name)))] }, group));
|
|
242
320
|
}), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: `${PUGI_TAGLINE}` }) }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: 'Press any key to dismiss.' }) })] }));
|
|
243
321
|
}
|
|
244
322
|
function RosterOverlay() {
|
|
245
|
-
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: "On-watch roster" }), _jsx(Box, { marginTop: 1, flexDirection: "column", children: THE_TEN.map((persona) => (_jsxs(Box, { children: [_jsx(Text, { bold: true, children: `
|
|
323
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: "On-watch roster" }), _jsx(Box, { marginTop: 1, flexDirection: "column", children: THE_TEN.map((persona) => (_jsxs(Box, { children: [_jsx(Text, { bold: true, children: ` ${persona.name.padEnd(10, ' ')}` }), _jsx(Text, { dimColor: true, children: `${persona.role.padEnd(20, ' ')}` }), _jsx(Text, { children: persona.oneLiner })] }, persona.slug))) }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: 'Press any key to dismiss.' }) })] }));
|
|
246
324
|
}
|
|
247
325
|
function FarewellOverlay() {
|
|
248
|
-
return (_jsx(Box, { flexDirection: "column", alignItems: "center", paddingY: 2, children: _jsx(Text, { bold: true, color: "
|
|
326
|
+
return (_jsx(Box, { flexDirection: "column", alignItems: "center", paddingY: 2, children: _jsx(Text, { bold: true, color: "#3da9fc", children: PUGI_TAGLINE }) }));
|
|
249
327
|
}
|
|
250
328
|
function applyVerdictSideEffects(verdict, handlers) {
|
|
251
329
|
switch (verdict.kind) {
|
|
@@ -270,8 +348,10 @@ function applyVerdictSideEffects(verdict, handlers) {
|
|
|
270
348
|
case 'consensus':
|
|
271
349
|
case 'diff':
|
|
272
350
|
case 'cost':
|
|
351
|
+
case 'quota':
|
|
273
352
|
case 'status':
|
|
274
353
|
case 'resume':
|
|
354
|
+
case 'mcp':
|
|
275
355
|
case 'stub':
|
|
276
356
|
// All non-overlay verdicts: the session module already appended
|
|
277
357
|
// any operator-visible system lines (and, for `ask`, set
|
|
@@ -7,7 +7,7 @@ export const PALETTE_ROW_LIMIT = 8;
|
|
|
7
7
|
* Centralises the "starts-with-slash → filter SLASH_COMMAND_HELP"
|
|
8
8
|
* logic so the input box and the unit test agree on the shape.
|
|
9
9
|
*
|
|
10
|
-
*
|
|
10
|
+
* fix: returns the FULL filtered set, not just the
|
|
11
11
|
* first PALETTE_ROW_LIMIT rows. The palette renderer now windows the
|
|
12
12
|
* visible slice internally based on `focusedIndex`, so the operator
|
|
13
13
|
* can scroll past row 7 via ↑/↓ on a long list (e.g. 20 commands when
|
|
@@ -15,9 +15,9 @@ export const PALETTE_ROW_LIMIT = 8;
|
|
|
15
15
|
* shape for backward compatibility but always equals `rows.length`.
|
|
16
16
|
*
|
|
17
17
|
* Behaviour:
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
18
|
+
* - Empty / non-slash buffer → empty result; palette stays hidden.
|
|
19
|
+
* - `/` alone → all registry rows (the operator wants to browse).
|
|
20
|
+
* - `/he` → rows whose name starts with `he` (case-insensitive).
|
|
21
21
|
*/
|
|
22
22
|
export function filterPalette(buffer) {
|
|
23
23
|
if (!buffer.startsWith('/')) {
|
|
@@ -86,7 +86,7 @@ export function completePalette(buffer, rows, focusedIndex) {
|
|
|
86
86
|
export function SlashPalette(props) {
|
|
87
87
|
if (props.rows.length === 0)
|
|
88
88
|
return null;
|
|
89
|
-
//
|
|
89
|
+
// fix: compute the visible window so the operator
|
|
90
90
|
// can scroll past row 7 on long lists. Focus indexes the full rows
|
|
91
91
|
// array; the window slides to keep the focused row visible.
|
|
92
92
|
const window = computePaletteWindow(props.rows, props.focusedIndex);
|
|
@@ -101,6 +101,6 @@ export function SlashPalette(props) {
|
|
|
101
101
|
const glyph = focused ? '▸' : '·';
|
|
102
102
|
const cmd = `/${row.name}${row.args ? ` ${row.args}` : ''}`.padEnd(22, ' ');
|
|
103
103
|
return (_jsxs(Box, { children: [_jsx(Text, { color: focused ? 'cyan' : 'gray', children: `${glyph} ` }), _jsx(Text, { bold: focused, color: focused ? 'cyan' : undefined, dimColor: !focused, children: cmd }), _jsx(Text, { dimColor: true, children: row.gloss })] }, row.name));
|
|
104
|
-
}), overflow ? (_jsx(Box, { children: _jsx(Text, { dimColor: true, children: `
|
|
104
|
+
}), overflow ? (_jsx(Box, { children: _jsx(Text, { dimColor: true, children: ` → ${focusedDisplayIndex}/${window.total}` }) })) : null, _jsx(Box, { children: _jsx(Text, { dimColor: true, children: ' ↑/↓ select · Tab complete · Enter run · Esc close' }) })] }));
|
|
105
105
|
}
|
|
106
106
|
//# sourceMappingURL=slash-palette.js.map
|
package/dist/tui/splash.js
CHANGED
|
@@ -21,11 +21,11 @@ export function Splash({ data }) {
|
|
|
21
21
|
cmd: 'pugi login',
|
|
22
22
|
gloss: 'Connect this terminal to your Pugi account',
|
|
23
23
|
};
|
|
24
|
-
return (_jsxs(Box, { flexDirection: "column", paddingX: 2, paddingY: 1, children: [_jsx(Box, { children: _jsx(Text, { bold: true, color: "
|
|
24
|
+
return (_jsxs(Box, { flexDirection: "column", paddingX: 2, paddingY: 1, children: [_jsx(Box, { children: _jsx(Text, { bold: true, color: "#3da9fc", children: "pugi.io" }) }), _jsx(Box, { children: _jsx(Text, { dimColor: true, children: `v${data.cliVersion} · ${data.apiUrl}` }) }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsxs(Text, { children: [_jsx(Text, { dimColor: true, children: "Account: " }), _jsx(Text, { children: accountLine })] }), data.isAuthenticated && data.plan ? (_jsxs(Text, { children: [_jsx(Text, { dimColor: true, children: "Plan: " }), _jsx(Text, { children: data.plan })] })) : null] }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { dimColor: true, children: "Quick start:" }), _jsx(HintRow, { command: primaryHint.cmd, gloss: primaryHint.gloss }), data.isAuthenticated ? (_jsx(HintRow, { command: 'pugi login', gloss: 'Re-authenticate or switch accounts' })) : (_jsx(HintRow, { command: 'pugi code "fix the bug"', gloss: 'Run a one-shot coding task' })), _jsx(HintRow, { command: 'pugi review --triple', gloss: 'Run the Anvil triple-review gate' }), _jsx(HintRow, { command: 'pugi help', gloss: 'Full command reference' })] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "Docs: https://pugi.dev \u00B7 Status: https://pugi.io/status" }) })] }));
|
|
25
25
|
}
|
|
26
26
|
function HintRow({ command, gloss }) {
|
|
27
27
|
// Pad command names so the gloss column lines up across rows.
|
|
28
28
|
const padded = command.padEnd(28, ' ');
|
|
29
|
-
return (_jsxs(Text, { children: [_jsx(Text, { children: `
|
|
29
|
+
return (_jsxs(Text, { children: [_jsx(Text, { children: ` ${padded}` }), _jsx(Text, { dimColor: true, children: gloss })] }));
|
|
30
30
|
}
|
|
31
31
|
//# sourceMappingURL=splash.js.map
|
package/dist/tui/status-bar.js
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Box, Text } from 'ink';
|
|
3
|
+
import { formatCostUsd, formatTokens } from '../core/repl/model-pricing.js';
|
|
4
|
+
/**
|
|
5
|
+
* Window during which the per-turn delta flash stays visible on the
|
|
6
|
+
* cost-meter row. CEO spec: ~2 seconds after completion. Past that, the
|
|
7
|
+
* flash dimms out and the row shows session totals only.
|
|
8
|
+
*/
|
|
9
|
+
const TURN_DELTA_FLASH_MS = 2_000;
|
|
3
10
|
/**
|
|
4
11
|
* Cyan dot glyphs across the pulse cycle. Three steps keep the motion
|
|
5
12
|
* subtle - a true gradient would force an Ink rerender on every
|
|
@@ -12,13 +19,78 @@ export function StatusBar(props) {
|
|
|
12
19
|
const tokenLabel = formatTokens(props.tokensDownstreamTotal);
|
|
13
20
|
const phase = clampPhase(props.pulsePhase);
|
|
14
21
|
const glyph = PULSE_DOTS[Math.min(phase, PULSE_DOTS.length - 1)] ?? PULSE_DOTS[0];
|
|
15
|
-
//
|
|
22
|
+
// : composite status label — connection problems trump dispatch
|
|
16
23
|
// state because the operator needs to know about a dropped admin-api
|
|
17
24
|
// first. When the connection is healthy (`on_watch` / `connecting`),
|
|
18
25
|
// the FSM dispatch state takes over to show the dispatch lifecycle
|
|
19
26
|
// (`dispatching` / `tool: read` / `aborting` / etc.).
|
|
20
|
-
const status = composeStatusLabel(props.connection, props.dispatchState, props.dispatchToolLabel);
|
|
21
|
-
|
|
27
|
+
const status = composeStatusLabel(props.connection, props.dispatchState, props.dispatchToolLabel, props.lastCompletedOutcome);
|
|
28
|
+
// cost-meter sprint — the cost row anchors above the legacy
|
|
29
|
+
// dispatch-state line so the operator's eye lands on the meter first
|
|
30
|
+
// (matches the upstream tool TUI footer rhythm). The session-elapsed slot
|
|
31
|
+
// uses sessionStartedAtEpochMs (REPL boot), distinct from the
|
|
32
|
+
// per-brief `elapsedLabel` on the row below.
|
|
33
|
+
const costRow = renderCostMeterRow(props.sessionTokensIn ?? 0, props.sessionTokensOut ?? 0, props.sessionCostUsd ?? 0, props.sessionStartedAtEpochMs, now);
|
|
34
|
+
const deltaFlash = renderTurnDeltaFlash(props.lastTurnDelta, now);
|
|
35
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { children: [_jsx(Text, { color: "cyan", children: `↑ ${costRow.tokensInLabel}` }), _jsx(Text, { dimColor: true, children: ' ' }), _jsx(Text, { color: "cyan", children: `↓ ${costRow.tokensOutLabel}` }), _jsx(Text, { dimColor: true, children: ` · ` }), _jsx(Text, { bold: true, children: costRow.costLabel }), _jsx(Text, { dimColor: true, children: ` · ${costRow.elapsedLabel}` }), deltaFlash ? (_jsxs(_Fragment, { children: [_jsx(Text, { dimColor: true, children: ' ' }), _jsx(Text, { color: "green", children: deltaFlash })] })) : null] }), _jsxs(Box, { children: [_jsx(Text, { color: status.color, children: `${glyph ?? '●'} ${status.label}` }), _jsx(Text, { dimColor: true, children: ` · ${props.activeAgentCount} agents · ` }), _jsx(Text, { children: `↓ ${tokenLabel} tokens` }), _jsx(Text, { dimColor: true, children: ` · ${elapsedLabel}` }), typeof props.externalDispatchCount === 'number' && props.externalDispatchCount > 0 ? (_jsx(Text, { color: "yellow", children: ` · ${props.externalDispatchCount} dispatch${props.externalDispatchCount === 1 ? '' : 'es'} active. /cancel к manage.` })) : null] }), _jsx(Box, { children: _jsx(Text, { dimColor: true, children: `${formatCount(props.pugiMdCount)} PUGI.md · ${formatCount(props.mcpServerCount)} MCP · ${formatCount(props.skillCount)} skills · ${formatQuota(props.quotaPct)} quota` }) })] }));
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* cost-meter sprint — assemble the cost-meter row labels. Pure helper
|
|
39
|
+
* so the snapshot tests assert the formatted shape without standing up
|
|
40
|
+
* an Ink renderer.
|
|
41
|
+
*/
|
|
42
|
+
export function renderCostMeterRow(tokensIn, tokensOut, costUsd, sessionStartedAtEpochMs, nowEpochMs) {
|
|
43
|
+
const elapsedMs = typeof sessionStartedAtEpochMs === 'number'
|
|
44
|
+
? Math.max(0, nowEpochMs - sessionStartedAtEpochMs)
|
|
45
|
+
: 0;
|
|
46
|
+
return {
|
|
47
|
+
tokensInLabel: formatTokens(tokensIn),
|
|
48
|
+
tokensOutLabel: formatTokens(tokensOut),
|
|
49
|
+
costLabel: formatCostUsd(costUsd),
|
|
50
|
+
elapsedLabel: formatElapsedShort(elapsedMs),
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* cost-meter sprint — render the per-turn delta flash when the most
|
|
55
|
+
* recent turn completed within the flash window. Returns null when no
|
|
56
|
+
* turn has completed yet OR the flash has expired (the elapsed slot
|
|
57
|
+
* gets the slot back). The flash format mirrors the spec:
|
|
58
|
+
*
|
|
59
|
+
* `+200/+1.1k +$0.01`
|
|
60
|
+
*
|
|
61
|
+
* Exported for snapshot tests.
|
|
62
|
+
*/
|
|
63
|
+
export function renderTurnDeltaFlash(delta, nowEpochMs) {
|
|
64
|
+
if (!delta)
|
|
65
|
+
return null;
|
|
66
|
+
const elapsedSinceMs = nowEpochMs - delta.completedAtEpochMs;
|
|
67
|
+
if (elapsedSinceMs < 0 || elapsedSinceMs > TURN_DELTA_FLASH_MS)
|
|
68
|
+
return null;
|
|
69
|
+
const inLabel = formatTokens(delta.tokensIn);
|
|
70
|
+
const outLabel = formatTokens(delta.tokensOut);
|
|
71
|
+
const costLabel = delta.costUsd > 0 ? ` +${formatCostUsd(delta.costUsd)}` : '';
|
|
72
|
+
return `+${inLabel}/+${outLabel}${costLabel}`;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* cost-meter sprint — local copy of the session elapsed formatter.
|
|
76
|
+
* Mirrors the helper in `core/repl/session.ts` so the status bar stays
|
|
77
|
+
* a pure leaf component without a circular import on session.ts (the
|
|
78
|
+
* model-pricing module's `formatDuration` is similar but ships an
|
|
79
|
+
* `XhYm` ceiling that does not match the CEO spec's `2m44s` shape).
|
|
80
|
+
*/
|
|
81
|
+
function formatElapsedShort(elapsedMs) {
|
|
82
|
+
if (!Number.isFinite(elapsedMs) || elapsedMs <= 0)
|
|
83
|
+
return '0s';
|
|
84
|
+
const totalSec = Math.floor(elapsedMs / 1000);
|
|
85
|
+
if (totalSec < 60)
|
|
86
|
+
return `${totalSec}s`;
|
|
87
|
+
const min = Math.floor(totalSec / 60);
|
|
88
|
+
const sec = totalSec % 60;
|
|
89
|
+
if (min < 60)
|
|
90
|
+
return `${min}m${sec.toString().padStart(2, '0')}s`;
|
|
91
|
+
const hr = Math.floor(min / 60);
|
|
92
|
+
const restMin = min % 60;
|
|
93
|
+
return `${hr}h${restMin.toString().padStart(2, '0')}m`;
|
|
22
94
|
}
|
|
23
95
|
/**
|
|
24
96
|
* Render a count badge — number if defined, `—` placeholder otherwise.
|
|
@@ -53,29 +125,29 @@ export function connectionLabel(connection) {
|
|
|
53
125
|
}
|
|
54
126
|
}
|
|
55
127
|
/**
|
|
56
|
-
*
|
|
128
|
+
* : compose the visible status label from connection + FSM state.
|
|
57
129
|
*
|
|
58
130
|
* Priority order:
|
|
59
131
|
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
132
|
+
* 1. `offline` / `reconnecting` — transport health wins; the
|
|
133
|
+
* operator needs to know about a dropped stream before anything
|
|
134
|
+
* about the dispatch.
|
|
135
|
+
* 2. `aborting` / `aborted` / `failed` — operator-visible terminal
|
|
136
|
+
* states the FSM reached; the colour shifts to amber/red so the
|
|
137
|
+
* anomaly stands out vs the calm cyan baseline.
|
|
138
|
+
* 3. `tool_running` — surfaces the tool label when available
|
|
139
|
+
* (`tool: read`), falls back to `tool` when not.
|
|
140
|
+
* 4. `awaiting_response` — `dispatching` (matches peer CLI's verb
|
|
141
|
+
* for the same state).
|
|
142
|
+
* 5. `completed` — `shipped` (matches the agent tree status glyph
|
|
143
|
+
* so the operator's eye links the two surfaces).
|
|
144
|
+
* 6. `idle` / unknown — connection label (`on watch` / `connecting`).
|
|
73
145
|
*
|
|
74
146
|
* The dispatch label `dispatchToolLabel` is already shaped as
|
|
75
147
|
* `tool: <kind>` upstream so we just concatenate; null falls through
|
|
76
148
|
* to the bare `tool` placeholder.
|
|
77
149
|
*/
|
|
78
|
-
function composeStatusLabel(connection, dispatchState, toolLabel) {
|
|
150
|
+
function composeStatusLabel(connection, dispatchState, toolLabel, lastCompletedOutcome) {
|
|
79
151
|
// Transport health wins.
|
|
80
152
|
if (connection === 'offline' || connection === 'reconnecting') {
|
|
81
153
|
return connectionLabel(connection);
|
|
@@ -93,6 +165,16 @@ function composeStatusLabel(connection, dispatchState, toolLabel) {
|
|
|
93
165
|
case 'awaiting_response':
|
|
94
166
|
return { label: 'dispatching', color: 'cyan' };
|
|
95
167
|
case 'completed':
|
|
168
|
+
// Branch on the work-done outcome so the bottom-bar tells the
|
|
169
|
+
// same truth as the agent-tree (2026-05-26 — memory
|
|
170
|
+
// feedback_no_fake_dispatch_promises). `'replied'` = text-only
|
|
171
|
+
// turn, render with the same neutral gray + arrow used in the
|
|
172
|
+
// agent-tree. `'shipped'` = real side-effect (or older server
|
|
173
|
+
// that omits the outcome field). Defaults to `'shipped'` so
|
|
174
|
+
// older callers without the prop wired stay back-compat.
|
|
175
|
+
if (lastCompletedOutcome === 'replied') {
|
|
176
|
+
return { label: 'replied', color: 'gray' };
|
|
177
|
+
}
|
|
96
178
|
return { label: 'shipped', color: 'green' };
|
|
97
179
|
case 'idle':
|
|
98
180
|
case undefined:
|
|
@@ -111,18 +193,14 @@ function formatElapsed(startedAt, now) {
|
|
|
111
193
|
const seconds = Math.floor((ms % 60_000) / 1000);
|
|
112
194
|
return `${minutes}m ${seconds.toString().padStart(2, '0')}s`;
|
|
113
195
|
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
if (total < 1_000_000)
|
|
123
|
-
return `${(total / 1_000).toFixed(1)}k`;
|
|
124
|
-
return `${(total / 1_000_000).toFixed(1)}m`;
|
|
125
|
-
}
|
|
196
|
+
// `formatTokens` for the downstream-throughput slot is imported from
|
|
197
|
+
// `core/repl/model-pricing.ts` — single source of truth for token
|
|
198
|
+
// formatting across the cost-meter row, `/cost` slash, and the legacy
|
|
199
|
+
// downstream-tokens slot. The shape is identical to the prior local
|
|
200
|
+
// helper (`<1000` raw, `<1m` one-decimal k, `≥1m` one-decimal m); the
|
|
201
|
+
// only semantic difference is non-finite / negative inputs render as
|
|
202
|
+
// `0` instead of throwing, matching the cost-meter row's defensive
|
|
203
|
+
// posture.
|
|
126
204
|
function clampPhase(phase) {
|
|
127
205
|
if (typeof phase !== 'number' || Number.isNaN(phase))
|
|
128
206
|
return 0;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Text } from 'ink';
|
|
3
|
+
export function StatusTable({ snapshot }) {
|
|
4
|
+
const labelWidth = Math.max('Label'.length, ...snapshot.fields.map((f) => f.label.length));
|
|
5
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { bold: true, children: "Pugi status" }) }), snapshot.fields.map((field) => (_jsxs(Box, { children: [_jsxs(Text, { children: [field.label.padEnd(labelWidth, ' '), " "] }), field.available ? (_jsx(Text, { children: field.value })) : (_jsx(Text, { dimColor: true, children: field.value }))] }, field.key))), _jsx(Box, { marginTop: 1, children: _jsxs(Text, { dimColor: true, children: ["CLI ", snapshot.meta.cliVersion, " Node ", snapshot.meta.nodeVersion, " cwd ", snapshot.meta.cwd] }) })] }));
|
|
6
|
+
}
|
|
7
|
+
//# sourceMappingURL=status-table.js.map
|