@pugi/cli 0.1.0-beta.9 → 0.1.0-beta.91
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 +1792 -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/cron.js +433 -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 +99 -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/multi-file-diff-approval.js +375 -0
- 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
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Visible boundary marker for the conversation pane.
|
|
4
|
+
*
|
|
5
|
+
* Rendered inline in the transcript when the replay walker hits a
|
|
6
|
+
* `compaction` event. Conveys three facts at a glance:
|
|
7
|
+
*
|
|
8
|
+
* 1. A compaction happened (separator line).
|
|
9
|
+
* 2. How many turns were folded into the summary.
|
|
10
|
+
* 3. Whether it was manual or auto-triggered.
|
|
11
|
+
*
|
|
12
|
+
* The separator line uses U+2500 (BOX DRAWINGS LIGHT HORIZONTAL) so
|
|
13
|
+
* the visual weight matches the rest of the Ink chrome. Dim ink-color
|
|
14
|
+
* `gray` keeps the marker subdued — it is a navigation aid, not the
|
|
15
|
+
* conversation itself.
|
|
16
|
+
*
|
|
17
|
+
* Width-aware: when stdout columns are known we render the dashes to
|
|
18
|
+
* fill the line minus the centred label; on unknown width we fall
|
|
19
|
+
* back to a fixed 64-dash pad. The fallback is wide enough for any
|
|
20
|
+
* realistic terminal and narrow enough to not wrap on small ones.
|
|
21
|
+
*/
|
|
22
|
+
import { Box, Text } from 'ink';
|
|
23
|
+
const FALLBACK_COLUMNS = 80;
|
|
24
|
+
/**
|
|
25
|
+
* Render the boundary line. The wrapping `<Box>` keeps the marker on
|
|
26
|
+
* its own row even when the surrounding flex container packs siblings.
|
|
27
|
+
*/
|
|
28
|
+
export function CompactBanner(props) {
|
|
29
|
+
const columns = props.columns && props.columns > 20 ? props.columns : FALLBACK_COLUMNS;
|
|
30
|
+
const label = buildLabel(props);
|
|
31
|
+
const dashesEach = Math.max(3, Math.floor((columns - label.length - 2) / 2));
|
|
32
|
+
const dashes = '─'.repeat(dashesEach);
|
|
33
|
+
return (_jsx(Box, { children: _jsx(Text, { color: "gray", children: `${dashes} ${label} ${dashes}` }) }));
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Build the centred label. Examples:
|
|
37
|
+
* "context compacted (12 turns → 1 summary, auto)"
|
|
38
|
+
* "context compacted (4 turns → 1 summary, manual · ~3.2k tokens)"
|
|
39
|
+
* "context compacted (12 turns → summary at 14:32, auto)"
|
|
40
|
+
* "context compacted (12 turns → 1 summary, manual) · 6 turns ago"
|
|
41
|
+
*/
|
|
42
|
+
export function buildLabel(props) {
|
|
43
|
+
const trigger = props.trigger === 'auto' ? 'auto' : 'manual';
|
|
44
|
+
const turns = `${props.turnsBefore} ${props.turnsBefore === 1 ? 'turn' : 'turns'}`;
|
|
45
|
+
const summarySlot = typeof props.summaryAtEpochMs === 'number' && props.summaryAtEpochMs > 0
|
|
46
|
+
? `summary at ${formatClock(props.summaryAtEpochMs)}`
|
|
47
|
+
: '1 summary';
|
|
48
|
+
const tokens = props.summaryTokenCount && props.summaryTokenCount > 0
|
|
49
|
+
? ` · ~${formatTokens(props.summaryTokenCount)} tokens`
|
|
50
|
+
: '';
|
|
51
|
+
const base = `context compacted (${turns} → ${summarySlot}, ${trigger}${tokens})`;
|
|
52
|
+
// L29 history-replay suffix. Only render when the boundary has live
|
|
53
|
+
// turns following it — a fresh in-REPL `/compact` lands with
|
|
54
|
+
// `turnsAgo === 0` and we keep the label compact.
|
|
55
|
+
if (typeof props.turnsAgo === 'number' && props.turnsAgo > 0) {
|
|
56
|
+
const ago = `${props.turnsAgo} ${props.turnsAgo === 1 ? 'turn' : 'turns'} ago`;
|
|
57
|
+
return `${base} · ${ago}`;
|
|
58
|
+
}
|
|
59
|
+
return base;
|
|
60
|
+
}
|
|
61
|
+
/** Format token counts like 1234 → 1.2k, 950 → 950. */
|
|
62
|
+
function formatTokens(n) {
|
|
63
|
+
if (n < 1000)
|
|
64
|
+
return `${n}`;
|
|
65
|
+
return `${(n / 1000).toFixed(1)}k`;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Render an epoch-ms instant as a deterministic `HH:MM` clock in the
|
|
69
|
+
* operator's local timezone. Used only inside the centred label so a
|
|
70
|
+
* boundary that landed mid-session reads `summary at 14:32` instead of
|
|
71
|
+
* the generic `1 summary`. Pure (no side effects), constant-time.
|
|
72
|
+
*/
|
|
73
|
+
function formatClock(epochMs) {
|
|
74
|
+
const d = new Date(epochMs);
|
|
75
|
+
if (Number.isNaN(d.getTime()))
|
|
76
|
+
return '--:--';
|
|
77
|
+
const hh = d.getHours().toString().padStart(2, '0');
|
|
78
|
+
const mm = d.getMinutes().toString().padStart(2, '0');
|
|
79
|
+
return `${hh}:${mm}`;
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=compact-banner.js.map
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Box, Text } from 'ink';
|
|
3
3
|
import { MarkdownRender } from './markdown-render.js';
|
|
4
|
+
import { CompactBanner } from './compact-banner.js';
|
|
4
5
|
const HUE_COLOR_BY_SLUG = {
|
|
5
|
-
//
|
|
6
|
+
// Pugi (Pug) - coordinator
|
|
6
7
|
main: 'cyan',
|
|
7
8
|
// Olivia (Honeybee) - release
|
|
8
9
|
pm: 'yellow',
|
|
@@ -36,28 +37,101 @@ function PaneHeader({ count }) {
|
|
|
36
37
|
function ConversationRow({ row, personaNames, }) {
|
|
37
38
|
switch (row.source) {
|
|
38
39
|
case 'operator':
|
|
39
|
-
return (_jsxs(Box, { children: [_jsx(Text, { bold: true, color: "
|
|
40
|
+
return (_jsxs(Box, { children: [_jsx(Text, { bold: true, color: "#3da9fc", children: '› ' }), _jsx(Text, { children: row.text })] }));
|
|
40
41
|
case 'system':
|
|
41
42
|
return (_jsxs(Box, { children: [_jsx(Text, { dimColor: true, children: '· ' }), _jsx(Text, { dimColor: true, children: row.text })] }));
|
|
43
|
+
case 'compact-boundary': {
|
|
44
|
+
// L29 : structured render. When the row carries the
|
|
45
|
+
// `compaction` payload, drive the Ink banner directly so the
|
|
46
|
+
// operator sees a width-aware gray separator with the canonical
|
|
47
|
+
// label. Fallback: a legacy boundary missing the structured
|
|
48
|
+
// payload (older replay events, hand-built rows) renders through
|
|
49
|
+
// the dim text path so the operator still sees the marker.
|
|
50
|
+
if (row.compaction) {
|
|
51
|
+
return (_jsx(CompactBanner, { turnsBefore: row.compaction.turnsBefore, trigger: row.compaction.trigger, summaryTokenCount: row.compaction.summaryTokenCount, turnsAgo: row.compaction.turnsAgo, summaryAtEpochMs: row.timestampEpochMs, columns: process.stdout?.columns }));
|
|
52
|
+
}
|
|
53
|
+
return (_jsx(Box, { children: _jsx(Text, { dimColor: true, children: row.text }) }));
|
|
54
|
+
}
|
|
42
55
|
case 'persona': {
|
|
43
56
|
const slug = row.personaSlug ?? '';
|
|
44
57
|
const color = HUE_COLOR_BY_SLUG[slug] ?? 'white';
|
|
45
58
|
const displayName = personaNames?.get(slug) ?? slug;
|
|
46
|
-
//
|
|
47
|
-
//
|
|
48
|
-
//
|
|
49
|
-
//
|
|
50
|
-
//
|
|
51
|
-
|
|
52
|
-
|
|
59
|
+
// CEO live dogfood: a body that started with a
|
|
60
|
+
// Cyrillic letter (e.g. "Принял.") rendered as "PugiПринял." on
|
|
61
|
+
// the same line because the persona label sat at zero margin and
|
|
62
|
+
// the body fell flush behind it. The label is bold-coloured chrome,
|
|
63
|
+
// not text the operator is meant to read as a sentence stem.
|
|
64
|
+
// Split body to its own row and indent two columns so the
|
|
65
|
+
// transcript reads:
|
|
66
|
+
//
|
|
67
|
+
// ▸ Pugi
|
|
68
|
+
// Принял.
|
|
69
|
+
//
|
|
70
|
+
// matching the the upstream tool / Codex / Gemini baseline visually +
|
|
71
|
+
// preventing the "PugiПринял" glue regardless of the body's
|
|
72
|
+
// leading character.
|
|
73
|
+
//
|
|
74
|
+
// The render also strips a leading identity-intro phrase as a
|
|
75
|
+
// defense-in-depth complement to the backend output gate — when
|
|
76
|
+
// turnIndex > 1, the operator must NOT see "Я Pugi - твой
|
|
77
|
+
// инженерный напарник." opening every reply. This is belt-and-
|
|
78
|
+
// braces; the prompt + output-gate already block it upstream.
|
|
79
|
+
const stripped = stripLeadingIdentityIntro(row.text);
|
|
80
|
+
const containsMarkdown = looksLikeMarkdown(stripped);
|
|
81
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { children: _jsx(Text, { color: color, bold: true, children: `▸ ${displayName}` }) }), containsMarkdown ? (_jsx(Box, { marginLeft: 2, children: _jsx(MarkdownRender, { source: stripped }) })) : (_jsx(Box, { marginLeft: 2, children: _jsx(Text, { children: stripped }) }))] }));
|
|
53
82
|
}
|
|
54
83
|
}
|
|
55
84
|
}
|
|
85
|
+
/**
|
|
86
|
+
* Strip a leading identity-intro line ("I'm Pugi — your engineering
|
|
87
|
+
* copilot..." / "Я Pugi — твой инженерный напарник...") from the
|
|
88
|
+
* transcript body as a defense-in-depth complement to the backend
|
|
89
|
+
* output gate. The prompt + gate already block this upstream; the
|
|
90
|
+
* render guards against the rare case where a legacy model release
|
|
91
|
+
* sneaks the intro past both layers.
|
|
92
|
+
*
|
|
93
|
+
* The strip is intentionally conservative — we only remove the
|
|
94
|
+
* canonical phrasings the prompt teaches Pugi to emit. Any other
|
|
95
|
+
* prose containing the word "Pugi" passes through unchanged.
|
|
96
|
+
*
|
|
97
|
+
* Exported for spec.
|
|
98
|
+
*/
|
|
99
|
+
export function stripLeadingIdentityIntro(text) {
|
|
100
|
+
if (!text || text.length === 0)
|
|
101
|
+
return text;
|
|
102
|
+
// Try each canonical intro shape in turn (RU + EN). We strip only
|
|
103
|
+
// the LEADING occurrence; a mid-body mention is intentional citation
|
|
104
|
+
// (e.g. "the Pugi codename..." in an explainer turn).
|
|
105
|
+
const introPatterns = [
|
|
106
|
+
// RU canonical — both masculine ("напарник/напарника/напарнику") and
|
|
107
|
+
// feminine ("напарница/напарницей/напарнице") declensions land on this
|
|
108
|
+
// strip. The prior pattern `напарни[ккк][аеу]?` was a character-class
|
|
109
|
+
// typo (three `к`s collapse to one) and accepted no feminine form, so
|
|
110
|
+
// a Pugi reply opening "Я Pugi — твоя инженерная напарница…" leaked
|
|
111
|
+
// past the strip. P1 reviewer fix PR .
|
|
112
|
+
/^(?:Я\s+Pugi\s*[—–-]\s*тв(?:ой|ё|оя)\s+инженерн[аыяий][хйеомя]*\s+напарни(?:к[аеу]?|ц(?:ей|а|ы|е|у))[.,!]?\s*)/u,
|
|
113
|
+
/^(?:Я\s+Pugi\s*[—–-]\s*координатор[.,!]?\s*)/u,
|
|
114
|
+
// EN canonical
|
|
115
|
+
/^(?:I'?m\s+Pugi\s*[—–-]\s*your\s+engineering\s+copilot[.,!]?\s*)/iu,
|
|
116
|
+
/^(?:Pugi\s+here[.,!]?\s*)/iu,
|
|
117
|
+
/^(?:This\s+is\s+Pugi[.,!]?\s*)/iu,
|
|
118
|
+
];
|
|
119
|
+
let out = text;
|
|
120
|
+
for (const re of introPatterns) {
|
|
121
|
+
out = out.replace(re, '');
|
|
122
|
+
}
|
|
123
|
+
// If the body had ONLY the intro phrase, return the original — never
|
|
124
|
+
// hand the operator an empty bubble. The output gate would have
|
|
125
|
+
// logged the verbosity hit; the operator sees the unmodified text.
|
|
126
|
+
if (out.trim().length === 0)
|
|
127
|
+
return text;
|
|
128
|
+
return out;
|
|
129
|
+
}
|
|
56
130
|
/**
|
|
57
131
|
* Cheap heuristic for "this transcript row will benefit from Markdown
|
|
58
132
|
* rendering". We only pay the parser cost when the row plausibly
|
|
59
133
|
* contains a code fence, heading, list item, or inline accent. A bare
|
|
60
|
-
* "
|
|
134
|
+
* "Pugi shipped." line takes the legacy fast path.
|
|
61
135
|
*
|
|
62
136
|
* The probe is intentionally generous - false positives just route
|
|
63
137
|
* through the parser, which renders plain text identically.
|
|
@@ -67,7 +141,7 @@ function looksLikeMarkdown(text) {
|
|
|
67
141
|
return false;
|
|
68
142
|
if (text.includes('```'))
|
|
69
143
|
return true;
|
|
70
|
-
// Codex P2 PR
|
|
144
|
+
// Codex P2 PR: intro-plus-list shape ("Summary:\n- bullet")
|
|
71
145
|
// must route through renderer. Scan EVERY line, not just the first.
|
|
72
146
|
const lines = text.split('\n');
|
|
73
147
|
for (const raw of lines) {
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Text } from 'ink';
|
|
3
|
+
import { estimateUsd, formatTokensWithCommas, formatUsd, rateFor } from '../core/cost/rate-card.js';
|
|
4
|
+
/**
|
|
5
|
+
* Column widths chosen to match the L19 spec output. The model column is
|
|
6
|
+
* the widest because slug strings like `qwen3-coder-480b-instruct-fp8`
|
|
7
|
+
* run 31 chars. We pad/truncate inside the renderer so a TUI on a 80-col
|
|
8
|
+
* terminal does not wrap mid-table.
|
|
9
|
+
*/
|
|
10
|
+
const COL_MODEL = 34;
|
|
11
|
+
const COL_IN = 8;
|
|
12
|
+
const COL_OUT = 9;
|
|
13
|
+
const COL_USD = 10;
|
|
14
|
+
/**
|
|
15
|
+
* Render one cost report. Stateless — re-rendering with the same view
|
|
16
|
+
* produces the same output by construction. Tests assert against
|
|
17
|
+
* `lastFrame()` from `ink-testing-library`.
|
|
18
|
+
*/
|
|
19
|
+
export function CostTable({ view }) {
|
|
20
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { children: view.heading }), _jsx(Text, { children: "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550" }), _jsx(Text, { children: " " }), _jsxs(Text, { children: [pad('MODEL', COL_MODEL), padLeft('IN_TOK', COL_IN), padLeft('OUT_TOK', COL_OUT), padLeft('$ EST', COL_USD)] }), view.rows.length === 0 ? (_jsx(Text, { dimColor: true, children: "No calls recorded yet \u2014 brief a persona to charge the meter." })) : (view.rows.map((row) => (_jsxs(Text, { children: [pad(row.model, COL_MODEL), padLeft(formatTokensWithCommas(row.inputTokens), COL_IN), padLeft(formatTokensWithCommas(row.outputTokens), COL_OUT), padLeft(formatUsd(row.usd), COL_USD), row.note ? ` (${row.note})` : ''] }, row.model)))), _jsx(Text, { children: " " }), _jsxs(Text, { children: ["Total tokens: ", formatTokensWithCommas(view.totalInputTokens + view.totalOutputTokens), ' (in: ', formatTokensWithCommas(view.totalInputTokens), ', out: ', formatTokensWithCommas(view.totalOutputTokens), ')'] }), _jsxs(Text, { children: ["Total dollar estimate: ", formatUsd(view.totalUsd)] }), view.tier ? _jsxs(Text, { children: ["Tier: ", view.tier.tier, view.tier.quotaLine ? ` (${view.tier.quotaLine})` : ''] }) : null] }));
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Build a `CostView` from a `SessionAggregate`. The function lives here
|
|
24
|
+
* (next to the renderer) so a future caller — the REPL slash, the CLI
|
|
25
|
+
* command, a JSON-only path — uses the same view-model shape and the
|
|
26
|
+
* same row-sort rule.
|
|
27
|
+
*/
|
|
28
|
+
export function buildCostView(input) {
|
|
29
|
+
const rows = [];
|
|
30
|
+
for (const [model, entry] of Object.entries(input.aggregate.models)) {
|
|
31
|
+
const usd = estimateUsd(model, entry.input, entry.output);
|
|
32
|
+
const rate = rateFor(model);
|
|
33
|
+
rows.push({
|
|
34
|
+
model,
|
|
35
|
+
inputTokens: entry.input,
|
|
36
|
+
outputTokens: entry.output,
|
|
37
|
+
usd,
|
|
38
|
+
note: rate.note,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
// Sort by USD descending first (most-expensive-first, matches the L19
|
|
42
|
+
// sample output where the Claude row leads). Ties break by total
|
|
43
|
+
// tokens so two free open-weight rows order deterministically.
|
|
44
|
+
rows.sort((a, b) => {
|
|
45
|
+
if (b.usd !== a.usd)
|
|
46
|
+
return b.usd - a.usd;
|
|
47
|
+
return (b.inputTokens + b.outputTokens) - (a.inputTokens + a.outputTokens);
|
|
48
|
+
});
|
|
49
|
+
let totalIn = 0;
|
|
50
|
+
let totalOut = 0;
|
|
51
|
+
let totalUsd = 0;
|
|
52
|
+
for (const row of rows) {
|
|
53
|
+
totalIn += row.inputTokens;
|
|
54
|
+
totalOut += row.outputTokens;
|
|
55
|
+
totalUsd += row.usd;
|
|
56
|
+
}
|
|
57
|
+
return {
|
|
58
|
+
heading: input.heading,
|
|
59
|
+
rows,
|
|
60
|
+
totalInputTokens: totalIn,
|
|
61
|
+
totalOutputTokens: totalOut,
|
|
62
|
+
totalUsd,
|
|
63
|
+
tier: input.tier,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Plain-string renderer for non-TTY / `--json` callers. Produces the
|
|
68
|
+
* same table the Ink component would render — no ANSI / no color so it
|
|
69
|
+
* pipes cleanly into `less` or a JSON tool.
|
|
70
|
+
*/
|
|
71
|
+
export function renderCostTableText(view) {
|
|
72
|
+
const lines = [];
|
|
73
|
+
lines.push(view.heading);
|
|
74
|
+
lines.push('════════════════════════════════════════════════');
|
|
75
|
+
lines.push('');
|
|
76
|
+
lines.push(pad('MODEL', COL_MODEL) +
|
|
77
|
+
padLeft('IN_TOK', COL_IN) +
|
|
78
|
+
padLeft('OUT_TOK', COL_OUT) +
|
|
79
|
+
padLeft('$ EST', COL_USD));
|
|
80
|
+
if (view.rows.length === 0) {
|
|
81
|
+
lines.push('No calls recorded yet — brief a persona to charge the meter.');
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
for (const row of view.rows) {
|
|
85
|
+
const noteSuffix = row.note ? ` (${row.note})` : '';
|
|
86
|
+
lines.push(pad(row.model, COL_MODEL) +
|
|
87
|
+
padLeft(formatTokensWithCommas(row.inputTokens), COL_IN) +
|
|
88
|
+
padLeft(formatTokensWithCommas(row.outputTokens), COL_OUT) +
|
|
89
|
+
padLeft(formatUsd(row.usd), COL_USD) +
|
|
90
|
+
noteSuffix);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
lines.push('');
|
|
94
|
+
lines.push(`Total tokens: ${formatTokensWithCommas(view.totalInputTokens + view.totalOutputTokens)} (in: ${formatTokensWithCommas(view.totalInputTokens)}, out: ${formatTokensWithCommas(view.totalOutputTokens)})`);
|
|
95
|
+
lines.push(`Total dollar estimate: ${formatUsd(view.totalUsd)}`);
|
|
96
|
+
if (view.tier) {
|
|
97
|
+
lines.push(`Tier: ${view.tier.tier}${view.tier.quotaLine ? ` (${view.tier.quotaLine})` : ''}`);
|
|
98
|
+
}
|
|
99
|
+
return lines.join('\n');
|
|
100
|
+
}
|
|
101
|
+
function pad(value, width) {
|
|
102
|
+
if (value.length >= width)
|
|
103
|
+
return value.slice(0, Math.max(0, width - 1)) + ' ';
|
|
104
|
+
return value + ' '.repeat(width - value.length);
|
|
105
|
+
}
|
|
106
|
+
function padLeft(value, width) {
|
|
107
|
+
if (value.length >= width)
|
|
108
|
+
return value.slice(0, width);
|
|
109
|
+
return ' '.repeat(width - value.length) + value;
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=cost-table.js.map
|
package/dist/tui/device-flow.js
CHANGED
|
@@ -5,7 +5,7 @@ const SPINNER_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧',
|
|
|
5
5
|
const ACCENT_CYAN = 'cyan';
|
|
6
6
|
const ACCENT_RED = 'red';
|
|
7
7
|
/**
|
|
8
|
-
* P2-1 (triple-review
|
|
8
|
+
* P2-1 (triple-review): both `userCode` and
|
|
9
9
|
* `verificationUrl` originate from a server response and would render
|
|
10
10
|
* verbatim into Ink `<Text>`. A hostile or compromised server could
|
|
11
11
|
* embed ANSI escape sequences (clear screen, cursor moves, hyperlink
|
|
@@ -108,7 +108,7 @@ export function DeviceFlow(props) {
|
|
|
108
108
|
* cabinet page.
|
|
109
109
|
*/
|
|
110
110
|
function CodeChip({ code }) {
|
|
111
|
-
return (_jsx(Box, { marginTop: 1, justifyContent: "center", children: _jsx(Text, { bold: true, color: ACCENT_CYAN, children: `
|
|
111
|
+
return (_jsx(Box, { marginTop: 1, justifyContent: "center", children: _jsx(Text, { bold: true, color: ACCENT_CYAN, children: ` ${code} ` }) }));
|
|
112
112
|
}
|
|
113
113
|
function BrowserHintRow({ opened, url, copied, }) {
|
|
114
114
|
if (copied) {
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Text } from 'ink';
|
|
3
|
+
import { useTheme } from '../core/theme/context.js';
|
|
4
|
+
/**
|
|
5
|
+
* status colors flow through the active theme
|
|
6
|
+
* instead of being hard-coded Ink color names. The `colorblind`
|
|
7
|
+
* preset re-maps OK/WARN/ERROR to a blue-yellow-magenta axis so
|
|
8
|
+
* deuteranopia operators can still distinguish the three states; the
|
|
9
|
+
* `default` / `dark` / `light` themes route to green/yellow/red
|
|
10
|
+
* variants tuned for each background. `skipped` keeps a muted color
|
|
11
|
+
* (theme-controlled muted token) so dimmed rows still pick up the
|
|
12
|
+
* theme tint.
|
|
13
|
+
*/
|
|
14
|
+
function buildStatusColor(theme) {
|
|
15
|
+
return {
|
|
16
|
+
ok: theme.success,
|
|
17
|
+
warn: theme.warning,
|
|
18
|
+
error: theme.error,
|
|
19
|
+
skipped: theme.muted,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
function buildOverallColor(theme) {
|
|
23
|
+
return {
|
|
24
|
+
healthy: theme.success,
|
|
25
|
+
warning: theme.warning,
|
|
26
|
+
error: theme.error,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
export function DoctorTable({ envelope }) {
|
|
30
|
+
const theme = useTheme();
|
|
31
|
+
const statusColor = buildStatusColor(theme);
|
|
32
|
+
const overallColor = buildOverallColor(theme);
|
|
33
|
+
const nameWidth = Math.max('NAME'.length, ...envelope.probes.map((row) => row.name.length));
|
|
34
|
+
const statusWidth = Math.max('STATUS'.length, ...envelope.probes.map((row) => row.status.length));
|
|
35
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { bold: true, children: "Pugi Doctor" }) }), envelope.probes.map((row) => (_jsx(DoctorRow, { row: row, nameWidth: nameWidth, statusWidth: statusWidth, statusColor: statusColor }, row.name))), _jsx(Box, { marginTop: 1, children: _jsxs(Text, { children: [envelope.counts.error, " error(s), ", envelope.counts.warn, " warning(s), ", envelope.counts.ok, " ok,", ' ', envelope.counts.skipped, " skipped. Overall:", ' ', _jsx(Text, { color: overallColor[envelope.overall], bold: true, children: envelope.overall.toUpperCase() })] }) }), _jsx(Box, { children: _jsxs(Text, { dimColor: true, children: ["CLI ", envelope.meta.cliVersion, " Node ", envelope.meta.nodeVersion, " cwd ", envelope.meta.cwd] }) })] }));
|
|
36
|
+
}
|
|
37
|
+
function DoctorRow({ row, nameWidth, statusWidth, statusColor }) {
|
|
38
|
+
const namePart = row.name.padEnd(nameWidth, ' ');
|
|
39
|
+
const statusPart = row.status.toUpperCase().padEnd(statusWidth, ' ');
|
|
40
|
+
const latencyPart = typeof row.latencyMs === 'number' ? ` (${row.latencyMs}ms)` : '';
|
|
41
|
+
const showRemediation = typeof row.remediation === 'string' &&
|
|
42
|
+
row.remediation.length > 0 &&
|
|
43
|
+
(row.status === 'warn' || row.status === 'error');
|
|
44
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { children: [_jsxs(Text, { children: [namePart, " "] }), _jsx(Text, { color: statusColor[row.status], bold: true, children: statusPart }), _jsxs(Text, { children: [' ', row.detail, latencyPart] })] }), showRemediation && (_jsx(Box, { marginLeft: nameWidth + statusWidth + 4, children: _jsxs(Text, { dimColor: true, children: ["\u2192 ", row.remediation] }) }))] }));
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=doctor-table.js.map
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* `/feedback` interactive prompt — .
|
|
4
|
+
*
|
|
5
|
+
* Five-step wizard mounted from both the top-level `pugi feedback`
|
|
6
|
+
* shell handler and the in-REPL `/feedback` slash. Steps:
|
|
7
|
+
*
|
|
8
|
+
* 1. category — bug / feature / general / praise (numeric pick 1-4)
|
|
9
|
+
* 2. rating — 1-5 stars (numeric pick 1-5)
|
|
10
|
+
* 3. comment — free-text, multi-line, Ctrl-D submits, Esc cancels
|
|
11
|
+
* 4. context — y/n include last 5 turns (redacted)? default no
|
|
12
|
+
* 5. confirm — y/n submit?
|
|
13
|
+
*
|
|
14
|
+
* Brand voice gate: ASCII glyphs only, no em-dashes, no banned brand
|
|
15
|
+
* words. Copy is intentionally power-word neutral so the future i18n
|
|
16
|
+
* landing localises cleanly.
|
|
17
|
+
*
|
|
18
|
+
* The component is PURE in the Ink sense — props in, one terminal
|
|
19
|
+
* `onResolve` event out. The caller owns the submit + queue logic.
|
|
20
|
+
*
|
|
21
|
+
* The verdict the operator submits is:
|
|
22
|
+
* - `{ cancelled: true }` when the operator presses Esc at any step
|
|
23
|
+
* - `{ cancelled: false, draft }` when the wizard completes
|
|
24
|
+
*
|
|
25
|
+
* `draft` is the assembled `FeedbackDraft` — NOT yet a full envelope
|
|
26
|
+
* (the caller still injects `ts` + `cliVersion` + maybe `tier`).
|
|
27
|
+
*/
|
|
28
|
+
import { useState } from 'react';
|
|
29
|
+
import { Box, Text, render, useApp, useInput } from 'ink';
|
|
30
|
+
const CATEGORIES = [
|
|
31
|
+
{ value: 'bug', label: 'bug', gloss: 'something broke or behaves unexpectedly' },
|
|
32
|
+
{ value: 'feature', label: 'feature', gloss: 'request a new capability' },
|
|
33
|
+
{ value: 'general', label: 'general', gloss: 'observation, idea, comment' },
|
|
34
|
+
{ value: 'praise', label: 'praise', gloss: 'positive note for the team' },
|
|
35
|
+
];
|
|
36
|
+
export function FeedbackPrompt(props) {
|
|
37
|
+
const [step, setStep] = useState('category');
|
|
38
|
+
const [category, setCategory] = useState(null);
|
|
39
|
+
const [rating, setRating] = useState(null);
|
|
40
|
+
const [commentBuffer, setCommentBuffer] = useState('');
|
|
41
|
+
const [includeContext, setIncludeContext] = useState(false);
|
|
42
|
+
useInput((input, key) => {
|
|
43
|
+
if (key.escape) {
|
|
44
|
+
props.onResolve({ cancelled: true });
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
if (step === 'category') {
|
|
48
|
+
const n = Number.parseInt(input, 10);
|
|
49
|
+
if (!Number.isNaN(n) && n >= 1 && n <= CATEGORIES.length) {
|
|
50
|
+
const picked = CATEGORIES[n - 1];
|
|
51
|
+
if (picked) {
|
|
52
|
+
setCategory(picked.value);
|
|
53
|
+
setStep('rating');
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
if (step === 'rating') {
|
|
59
|
+
const n = Number.parseInt(input, 10);
|
|
60
|
+
if (!Number.isNaN(n) && n >= 1 && n <= 5) {
|
|
61
|
+
setRating(n);
|
|
62
|
+
setStep('comment');
|
|
63
|
+
}
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
if (step === 'comment') {
|
|
67
|
+
// Ctrl-D submits the comment (even when empty — rating-only
|
|
68
|
+
// feedback is allowed). Enter inserts a newline so the
|
|
69
|
+
// operator can write multi-line bug repros without the wizard
|
|
70
|
+
// ending the buffer prematurely.
|
|
71
|
+
if (key.ctrl && (input === 'd' || input === '')) {
|
|
72
|
+
setStep('context');
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
if (key.return) {
|
|
76
|
+
setCommentBuffer((prev) => prev + '\n');
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
if (key.backspace || key.delete) {
|
|
80
|
+
setCommentBuffer((prev) => prev.slice(0, -1));
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
// Printable characters land in the buffer. We accept anything
|
|
84
|
+
// non-empty + non-control. Ink's useInput sometimes delivers
|
|
85
|
+
// multi-char paste bursts as one `input` — we append the whole
|
|
86
|
+
// burst so paste lands intact.
|
|
87
|
+
if (input && !key.ctrl && !key.meta) {
|
|
88
|
+
setCommentBuffer((prev) => prev + input);
|
|
89
|
+
}
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
if (step === 'context') {
|
|
93
|
+
if (input === 'y' || input === 'Y') {
|
|
94
|
+
setIncludeContext(true);
|
|
95
|
+
setStep('confirm');
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
if (input === 'n' || input === 'N' || key.return) {
|
|
99
|
+
// Default no — Enter at the context prompt picks "no" so
|
|
100
|
+
// the operator can blast through with all defaults.
|
|
101
|
+
setIncludeContext(false);
|
|
102
|
+
setStep('confirm');
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
if (step === 'confirm') {
|
|
108
|
+
if (input === 'y' || input === 'Y' || key.return) {
|
|
109
|
+
if (category != null && rating != null) {
|
|
110
|
+
props.onResolve({
|
|
111
|
+
cancelled: false,
|
|
112
|
+
draft: {
|
|
113
|
+
category,
|
|
114
|
+
rating,
|
|
115
|
+
comment: commentBuffer,
|
|
116
|
+
includeSessionContext: includeContext,
|
|
117
|
+
},
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
if (input === 'n' || input === 'N') {
|
|
123
|
+
props.onResolve({ cancelled: true });
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
}, { isActive: !props.inert });
|
|
129
|
+
return (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, children: [_jsxs(Box, { marginBottom: 1, children: [_jsx(Text, { bold: true, color: "cyan", children: "Pugi /feedback" }), _jsx(Text, { children: " \u2014 share what you saw. Esc cancels at any step." })] }), step === 'category' && (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: "1. Category" }), CATEGORIES.map((c, idx) => (_jsx(Text, { children: ` ${idx + 1}. ${c.label.padEnd(8)} ${c.gloss}` }, c.value))), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "Pick 1-4." }) })] })), step === 'rating' && (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: "2. Rating" }), _jsx(Text, { children: ` 1=poor, 5=excellent. Picked category: ${category ?? '?'}` }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "Pick 1-5." }) })] })), step === 'comment' && (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: "3. Comment" }), _jsx(Text, { dimColor: true, children: "Multi-line. Enter inserts a newline. Ctrl-D submits." }), _jsx(Box, { marginTop: 1, flexDirection: "column", children: commentBuffer.length === 0 ? (_jsx(Text, { dimColor: true, children: "(empty \u2014 Ctrl-D submits without a comment)" })) : (commentBuffer.split('\n').map((line, idx) => (_jsx(Text, { children: `> ${line}` }, idx)))) })] })), step === 'context' && (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: "4. Include session context?" }), _jsx(Text, { children: ' Last 5 turns, redacted (tokens / *_KEY=* values stripped).' }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "y / n \u2014 default n (Enter)." }) })] })), step === 'confirm' && (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: "5. Submit?" }), _jsx(Text, { children: ` category=${category ?? '?'} rating=${rating ?? '?'} context=${includeContext ? 'yes' : 'no'}` }), _jsx(Text, { children: ` comment=${commentBuffer.length} chars` }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "y / n \u2014 default y (Enter)." }) })] }))] }));
|
|
130
|
+
}
|
|
131
|
+
export async function renderFeedbackPrompt() {
|
|
132
|
+
let resolveOuter;
|
|
133
|
+
const outerPromise = new Promise((resolve) => {
|
|
134
|
+
resolveOuter = resolve;
|
|
135
|
+
});
|
|
136
|
+
function App() {
|
|
137
|
+
const { exit } = useApp();
|
|
138
|
+
return (_jsx(FeedbackPrompt, { onResolve: (verdict) => {
|
|
139
|
+
resolveOuter(verdict);
|
|
140
|
+
// Mirror renderAskCli: tiny delay so Ink flushes unmount
|
|
141
|
+
// before the caller prints the toast line.
|
|
142
|
+
setTimeout(() => exit(), 16);
|
|
143
|
+
} }));
|
|
144
|
+
}
|
|
145
|
+
const instance = render(_jsx(App, {}));
|
|
146
|
+
const verdict = await outerPromise;
|
|
147
|
+
try {
|
|
148
|
+
await instance.waitUntilExit();
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
// Ink can throw when exit() races with a re-render — the verdict
|
|
152
|
+
// is already captured so we ignore.
|
|
153
|
+
}
|
|
154
|
+
return verdict;
|
|
155
|
+
}
|
|
156
|
+
//# sourceMappingURL=feedback-prompt.js.map
|