@pugi/cli 0.1.0-beta.98 → 1.0.0-alpha.1
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/LICENSE +1 -1
- package/README.md +11 -191
- package/bin/pugi +8 -0
- package/package.json +15 -71
- package/postinstall.mjs +31 -0
- package/CHANGELOG.md +0 -132
- package/THIRD_PARTY_NOTICES.md +0 -40
- package/assets/pugi-mascot.ansi +0 -16
- package/assets/pugi-prozr2-mascot.ansi +0 -9
- package/bin/run.js +0 -34
- package/dist/commands/deploy.js +0 -439
- package/dist/commands/flatten.js +0 -191
- package/dist/commands/jobs-watch.js +0 -201
- package/dist/commands/jobs.js +0 -260
- package/dist/commands/retro.js +0 -210
- package/dist/commands/smoke.js +0 -133
- package/dist/core/agent-progress/cleanup.js +0 -134
- package/dist/core/agent-progress/schema.js +0 -144
- package/dist/core/agent-progress/writer.js +0 -101
- package/dist/core/agents/adaptive-router.js +0 -330
- package/dist/core/agents/loader.js +0 -104
- package/dist/core/agents/query-decomposer.js +0 -297
- package/dist/core/agents/registry.js +0 -69
- package/dist/core/approvals/shortcut-resolver.js +0 -98
- package/dist/core/artifact-chain/dispatcher.js +0 -148
- package/dist/core/artifact-chain/exporter.js +0 -164
- package/dist/core/artifact-chain/state.js +0 -243
- package/dist/core/artifact-chain/steps.js +0 -169
- package/dist/core/ask-user/question.js +0 -92
- package/dist/core/audit/audit-trail.js +0 -275
- package/dist/core/auth/ensure-authenticated.js +0 -129
- package/dist/core/auth/env-provider.js +0 -238
- package/dist/core/auto-open-browser.js +0 -128
- package/dist/core/auto-update/channels.js +0 -122
- package/dist/core/auto-update/checker.js +0 -241
- package/dist/core/auto-update/state.js +0 -235
- package/dist/core/bare-mode/index.js +0 -107
- package/dist/core/bash/redirect.js +0 -281
- package/dist/core/bash-classifier.js +0 -1397
- package/dist/core/checkpoint/resumer.js +0 -149
- package/dist/core/checkpoint/rewinder.js +0 -291
- package/dist/core/checkpoints/shadow-git.js +0 -670
- package/dist/core/citations/parser.js +0 -109
- package/dist/core/classifier/yolo-classifier.js +0 -88
- package/dist/core/clipboard.js +0 -70
- package/dist/core/codegraph/decision-store.js +0 -248
- package/dist/core/codegraph/detect-repo.js +0 -459
- package/dist/core/codegraph/install.js +0 -134
- package/dist/core/codegraph/offer-hook.js +0 -220
- package/dist/core/compact/auto-trigger.js +0 -96
- package/dist/core/compact/buffer-rewriter.js +0 -115
- package/dist/core/compact/summarizer.js +0 -208
- package/dist/core/compact/token-counter.js +0 -108
- package/dist/core/consensus/anvil-fanout.js +0 -276
- package/dist/core/consensus/diff-capture.js +0 -491
- package/dist/core/consensus/rubric.js +0 -233
- package/dist/core/context/builder.js +0 -114
- package/dist/core/context/compaction-events.js +0 -99
- package/dist/core/context/compaction.js +0 -602
- package/dist/core/context/index.js +0 -28
- package/dist/core/context/invariants.js +0 -250
- package/dist/core/context/markdown-loader.js +0 -288
- package/dist/core/context/markdown-traverse.js +0 -255
- package/dist/core/context/pugiignore.js +0 -316
- package/dist/core/context/repo-skeleton.js +0 -533
- package/dist/core/context/tool-eviction.js +0 -55
- package/dist/core/context/watcher.js +0 -342
- package/dist/core/context/working-set.js +0 -165
- package/dist/core/coordinator/agent-tools.js +0 -77
- package/dist/core/coordinator/agent-toolset.js +0 -65
- package/dist/core/coordinator/fsm.js +0 -73
- package/dist/core/coordinator/mode-fsm.js +0 -70
- package/dist/core/cost/rate-card.js +0 -129
- package/dist/core/cost/tracker.js +0 -221
- package/dist/core/credentials.js +0 -355
- package/dist/core/cron/scheduler.js +0 -138
- package/dist/core/denial-tracking/index.js +0 -8
- package/dist/core/denial-tracking/state.js +0 -264
- package/dist/core/diagnostics/probe-runner.js +0 -93
- package/dist/core/diagnostics/probes/api.js +0 -46
- package/dist/core/diagnostics/probes/auth.js +0 -93
- package/dist/core/diagnostics/probes/bare-mode.js +0 -42
- package/dist/core/diagnostics/probes/cli-version.js +0 -127
- package/dist/core/diagnostics/probes/config.js +0 -72
- package/dist/core/diagnostics/probes/denial-tracking.js +0 -57
- package/dist/core/diagnostics/probes/disk.js +0 -81
- package/dist/core/diagnostics/probes/engine-live.js +0 -46
- package/dist/core/diagnostics/probes/git.js +0 -65
- package/dist/core/diagnostics/probes/hooks.js +0 -118
- package/dist/core/diagnostics/probes/mcp.js +0 -75
- package/dist/core/diagnostics/probes/node.js +0 -59
- package/dist/core/diagnostics/probes/pnpm.js +0 -36
- package/dist/core/diagnostics/probes/pugi-md.js +0 -89
- package/dist/core/diagnostics/probes/sandbox.js +0 -72
- package/dist/core/diagnostics/probes/session.js +0 -74
- package/dist/core/diagnostics/probes/status-snapshot.js +0 -488
- package/dist/core/diagnostics/probes/workspace.js +0 -63
- package/dist/core/diagnostics/types.js +0 -70
- package/dist/core/dispatch/cache-cleanup.js +0 -197
- package/dist/core/dispatch/cache-handoff.js +0 -295
- package/dist/core/edits/apply-patch-layer-e.js +0 -189
- package/dist/core/edits/dispatch.js +0 -511
- package/dist/core/edits/format-detector.js +0 -260
- package/dist/core/edits/format-matrix.js +0 -26
- package/dist/core/edits/fuzzy-ladder.js +0 -650
- package/dist/core/edits/index.js +0 -19
- package/dist/core/edits/journal.js +0 -199
- package/dist/core/edits/layer-a-apply.js +0 -217
- package/dist/core/edits/layer-a-fuzzy-apply.js +0 -198
- package/dist/core/edits/layer-b-apply.js +0 -211
- package/dist/core/edits/layer-c-apply.js +0 -160
- package/dist/core/edits/layer-d-ast.js +0 -572
- package/dist/core/edits/marker-parser.js +0 -401
- package/dist/core/edits/security-gate.js +0 -223
- package/dist/core/edits/verify-hook.js +0 -273
- package/dist/core/edits/worktree.js +0 -322
- package/dist/core/engine/adapter-runner.js +0 -8
- package/dist/core/engine/anvil-client.js +0 -344
- package/dist/core/engine/auto-compact.js +0 -179
- package/dist/core/engine/budgets.js +0 -192
- package/dist/core/engine/context-prefix.js +0 -155
- package/dist/core/engine/index.js +0 -12
- package/dist/core/engine/intensity.js +0 -163
- package/dist/core/engine/intent.js +0 -260
- package/dist/core/engine/native-pugi.js +0 -1616
- package/dist/core/engine/noop.js +0 -27
- package/dist/core/engine/prompts.js +0 -236
- package/dist/core/engine/strip-internal-fields.js +0 -124
- package/dist/core/engine/tool-bridge.js +0 -2173
- package/dist/core/engine/verification-patterns.js +0 -195
- package/dist/core/evaluation/golden-dataset.js +0 -293
- package/dist/core/feedback/queue.js +0 -177
- package/dist/core/feedback/submitter.js +0 -145
- package/dist/core/file-cache.js +0 -141
- package/dist/core/flatten/flatten-repo.js +0 -439
- package/dist/core/format/osc8-link.js +0 -28
- package/dist/core/hook-chains.js +0 -392
- package/dist/core/hooks/citation-verify-hook.js +0 -138
- package/dist/core/hooks/citation-verify.js +0 -112
- package/dist/core/hooks/events.js +0 -46
- package/dist/core/hooks/index.js +0 -15
- package/dist/core/hooks/registry.js +0 -216
- package/dist/core/hooks/runner.js +0 -236
- package/dist/core/hooks/v2/event-emitter.js +0 -115
- package/dist/core/hooks/v2/executor.js +0 -282
- package/dist/core/hooks/v2/index.js +0 -25
- package/dist/core/hooks/v2/lifecycle.js +0 -104
- package/dist/core/hooks/v2/loader.js +0 -216
- package/dist/core/hooks/v2/matcher.js +0 -125
- package/dist/core/hooks/v2/trust.js +0 -143
- package/dist/core/hooks/v2/types.js +0 -86
- package/dist/core/hooks/worktree-events.js +0 -158
- package/dist/core/hooks.js +0 -415
- package/dist/core/image/renderer.js +0 -71
- package/dist/core/index-store.js +0 -260
- package/dist/core/init/detector.js +0 -582
- package/dist/core/init/template-renderer.js +0 -242
- package/dist/core/jobs/registry.js +0 -462
- package/dist/core/ledger/results-tsv.js +0 -142
- package/dist/core/log-discipline/stdout-redirect.js +0 -51
- package/dist/core/lsp/cache.js +0 -105
- package/dist/core/lsp/client.js +0 -1229
- package/dist/core/lsp/language-detect.js +0 -66
- package/dist/core/lsp/post-edit-diagnostics.js +0 -171
- package/dist/core/lsp/server-detect.js +0 -173
- package/dist/core/lsp/symbol-cache.js +0 -162
- package/dist/core/lsp/symbol-tools.js +0 -664
- package/dist/core/mcp/client.js +0 -385
- package/dist/core/mcp/http-server.js +0 -553
- package/dist/core/mcp/orchestrator-config.js +0 -192
- package/dist/core/mcp/orchestrator-tools.js +0 -806
- package/dist/core/mcp/permission.js +0 -190
- package/dist/core/mcp/registry.js +0 -193
- package/dist/core/mcp/server-tools.js +0 -219
- package/dist/core/mcp/server.js +0 -397
- package/dist/core/mcp/trust.js +0 -91
- package/dist/core/memory/dual-write.js +0 -416
- package/dist/core/memory/passive-extract.js +0 -130
- package/dist/core/memory/phase1-kinds.js +0 -20
- package/dist/core/memory/secret-scanner.js +0 -304
- package/dist/core/memory-sync/queue.js +0 -170
- package/dist/core/metrics/extract.js +0 -113
- package/dist/core/modes/roo-modes.js +0 -68
- package/dist/core/onboarding/ensure-initialized.js +0 -133
- package/dist/core/onboarding/marker.js +0 -111
- package/dist/core/onboarding/telemetry-state.js +0 -108
- package/dist/core/output-style/presets.js +0 -176
- package/dist/core/output-style/state.js +0 -185
- package/dist/core/path-security.js +0 -345
- package/dist/core/permission.js +0 -369
- package/dist/core/permissions/auto-classifier.js +0 -124
- package/dist/core/permissions/bash-parser.js +0 -371
- package/dist/core/permissions/circuit-breaker.js +0 -83
- package/dist/core/permissions/constrained-edit.js +0 -91
- package/dist/core/permissions/gate.js +0 -278
- package/dist/core/permissions/index.js +0 -20
- package/dist/core/permissions/mode.js +0 -174
- package/dist/core/permissions/network-egress.js +0 -137
- package/dist/core/permissions/state.js +0 -241
- package/dist/core/permissions/tool-class.js +0 -107
- package/dist/core/plan-mode/ui-state.js +0 -51
- package/dist/core/plans/plan-artifact.js +0 -721
- package/dist/core/policy-limits/etag-store.js +0 -122
- package/dist/core/prd-check/parser.js +0 -215
- package/dist/core/prd-check/reporter.js +0 -127
- package/dist/core/prd-check/session-review.js +0 -557
- package/dist/core/prd-check/verifiers.js +0 -223
- package/dist/core/prompt-cache/client-cache.js +0 -99
- package/dist/core/prompts/assembly.js +0 -29
- package/dist/core/prompts/registry.js +0 -364
- package/dist/core/pugi-gitignore.js +0 -52
- package/dist/core/pugi-md/cc-compat-rules.js +0 -735
- package/dist/core/pugi-md/context-injector.js +0 -76
- package/dist/core/pugi-md/walk-up.js +0 -207
- package/dist/core/python/uv-installer.js +0 -270
- package/dist/core/python/uv-resolver.js +0 -83
- package/dist/core/rate-limit/narrator.js +0 -146
- package/dist/core/recipes/cli-types.js +0 -20
- package/dist/core/recipes/loader.js +0 -103
- package/dist/core/recipes/runner.js +0 -345
- package/dist/core/recipes/schema.js +0 -587
- package/dist/core/release-notes/parser.js +0 -241
- package/dist/core/release-notes/state.js +0 -116
- package/dist/core/repl/ask.js +0 -512
- package/dist/core/repl/cancellation.js +0 -98
- package/dist/core/repl/cap-warning.js +0 -91
- package/dist/core/repl/clipboard-read.js +0 -174
- package/dist/core/repl/dispatch-fsm.js +0 -220
- package/dist/core/repl/engine-bridge.js +0 -303
- package/dist/core/repl/history-search.js +0 -175
- package/dist/core/repl/history.js +0 -182
- package/dist/core/repl/kill-ring.js +0 -138
- package/dist/core/repl/model-pricing.js +0 -135
- package/dist/core/repl/privacy-banner.js +0 -71
- package/dist/core/repl/session.js +0 -4962
- package/dist/core/repl/slash-commands.js +0 -747
- package/dist/core/repl/store/index.js +0 -12
- package/dist/core/repl/store/jsonl-log.js +0 -321
- package/dist/core/repl/store/lockfile.js +0 -155
- package/dist/core/repl/store/session-store.js +0 -821
- package/dist/core/repl/store/types.js +0 -44
- package/dist/core/repl/store/uuid-v7.js +0 -68
- package/dist/core/repl/tool-route.js +0 -382
- package/dist/core/repl/workspace-context.js +0 -206
- package/dist/core/repo-map/build.js +0 -125
- package/dist/core/repo-map/cache.js +0 -185
- package/dist/core/repo-map/extractor.js +0 -254
- package/dist/core/repo-map/formatter.js +0 -145
- package/dist/core/repo-map/page-rank.js +0 -105
- package/dist/core/repo-map/scanner.js +0 -211
- package/dist/core/retro/git-collector.js +0 -251
- package/dist/core/retro/health-card.js +0 -25
- package/dist/core/retro/metrics.js +0 -342
- package/dist/core/retro/narrative.js +0 -249
- package/dist/core/retro/plane-collector.js +0 -274
- package/dist/core/retro/pr-issue-link.js +0 -65
- package/dist/core/retro/types.js +0 -16
- package/dist/core/retry-budget/budget.js +0 -284
- package/dist/core/retry-budget/index.js +0 -5
- package/dist/core/retry-budget/retry-cap.js +0 -74
- package/dist/core/routing/lead-worker.js +0 -43
- package/dist/core/routing/pre-flight-estimator.js +0 -108
- package/dist/core/runs/run-tree.js +0 -103
- package/dist/core/sandboxing/adapter.js +0 -29
- package/dist/core/sandboxing/index.js +0 -49
- package/dist/core/sandboxing/none.js +0 -19
- package/dist/core/sandboxing/seatbelt.js +0 -183
- package/dist/core/security/injection-scanner.js +0 -367
- package/dist/core/security/output-filter.js +0 -418
- package/dist/core/session/env-file.js +0 -105
- package/dist/core/session/section-budgets.js +0 -140
- package/dist/core/session.js +0 -377
- package/dist/core/settings.js +0 -400
- package/dist/core/share/formatter.js +0 -271
- package/dist/core/share/redactor.js +0 -221
- package/dist/core/share/uploader.js +0 -267
- package/dist/core/skills/defaults.js +0 -457
- package/dist/core/skills/loader.js +0 -454
- package/dist/core/skills/sources.js +0 -480
- package/dist/core/skills/trust.js +0 -172
- package/dist/core/smoke/headless-driver.js +0 -174
- package/dist/core/smoke/orchestrator.js +0 -194
- package/dist/core/smoke/runner.js +0 -238
- package/dist/core/smoke/scenario-parser.js +0 -316
- package/dist/core/statusline.js +0 -99
- package/dist/core/subagents/dispatcher-real.js +0 -600
- package/dist/core/subagents/dispatcher.js +0 -352
- package/dist/core/subagents/index.js +0 -39
- package/dist/core/subagents/isolation-matrix.js +0 -213
- package/dist/core/subagents/spawn.js +0 -101
- package/dist/core/telemetry/emitter.js +0 -229
- package/dist/core/telemetry/queue.js +0 -251
- package/dist/core/theme/context.js +0 -91
- package/dist/core/theme/presets.js +0 -228
- package/dist/core/theme/state.js +0 -181
- package/dist/core/todos/invariant.js +0 -10
- package/dist/core/todos/state.js +0 -177
- package/dist/core/tool-schema/compressor.js +0 -89
- package/dist/core/transport/version-interceptor.js +0 -166
- package/dist/core/trust.js +0 -109
- package/dist/core/tui/thinking-block.js +0 -64
- package/dist/core/vim/keymap.js +0 -288
- package/dist/core/vim/state.js +0 -92
- package/dist/core/watch-markers/marker-watcher.js +0 -133
- package/dist/core/worktree/include-parser.js +0 -249
- package/dist/core/worktree-manager/cleanup.js +0 -123
- package/dist/core/worktree-manager/manager.js +0 -303
- package/dist/index.js +0 -44
- package/dist/runtime/bootstrap.js +0 -190
- package/dist/runtime/cli.js +0 -8121
- package/dist/runtime/commands/agents.js +0 -385
- package/dist/runtime/commands/budget.js +0 -192
- package/dist/runtime/commands/cancel.js +0 -231
- package/dist/runtime/commands/chain.js +0 -489
- package/dist/runtime/commands/codegraph-status.js +0 -227
- package/dist/runtime/commands/compact.js +0 -297
- package/dist/runtime/commands/config.js +0 -595
- package/dist/runtime/commands/cost.js +0 -199
- package/dist/runtime/commands/delegate.js +0 -312
- package/dist/runtime/commands/dispatch.js +0 -126
- package/dist/runtime/commands/doctor.js +0 -579
- package/dist/runtime/commands/feedback.js +0 -184
- package/dist/runtime/commands/hooks.js +0 -187
- package/dist/runtime/commands/init.js +0 -254
- package/dist/runtime/commands/lsp.js +0 -368
- package/dist/runtime/commands/mcp.js +0 -935
- package/dist/runtime/commands/memory.js +0 -582
- package/dist/runtime/commands/model.js +0 -237
- package/dist/runtime/commands/onboarding.js +0 -275
- package/dist/runtime/commands/patch.js +0 -128
- package/dist/runtime/commands/permissions.js +0 -112
- package/dist/runtime/commands/plan.js +0 -143
- package/dist/runtime/commands/prd-check.js +0 -285
- package/dist/runtime/commands/privacy.js +0 -107
- package/dist/runtime/commands/recipe.js +0 -325
- package/dist/runtime/commands/redo-blob-store.js +0 -92
- package/dist/runtime/commands/redo.js +0 -361
- package/dist/runtime/commands/release-notes.js +0 -229
- package/dist/runtime/commands/repo-map.js +0 -95
- package/dist/runtime/commands/report.js +0 -299
- package/dist/runtime/commands/resume.js +0 -118
- package/dist/runtime/commands/review-consensus.js +0 -414
- package/dist/runtime/commands/rewind.js +0 -333
- package/dist/runtime/commands/roster.js +0 -117
- package/dist/runtime/commands/sessions.js +0 -163
- package/dist/runtime/commands/share.js +0 -316
- package/dist/runtime/commands/skills.js +0 -401
- package/dist/runtime/commands/status.js +0 -186
- package/dist/runtime/commands/stickers.js +0 -82
- package/dist/runtime/commands/style.js +0 -194
- package/dist/runtime/commands/theme.js +0 -196
- package/dist/runtime/commands/undo.js +0 -361
- package/dist/runtime/commands/update.js +0 -289
- package/dist/runtime/commands/vim.js +0 -140
- package/dist/runtime/commands/worktree.js +0 -177
- package/dist/runtime/commands/worktrees.js +0 -155
- package/dist/runtime/deprecation-warning.js +0 -69
- package/dist/runtime/engine-exit-code.js +0 -50
- package/dist/runtime/headless-repl.js +0 -195
- package/dist/runtime/headless.js +0 -548
- package/dist/runtime/load-hooks-or-exit.js +0 -71
- package/dist/runtime/plan-decompose.js +0 -531
- package/dist/runtime/sigint-guard.js +0 -272
- package/dist/runtime/stream-renderer.js +0 -195
- package/dist/runtime/update-check.js +0 -294
- package/dist/runtime/version.js +0 -65
- package/dist/runtime/worktree-bootstrap.js +0 -579
- package/dist/skills/bundled/batch.js +0 -617
- package/dist/skills/bundled/index.js +0 -45
- package/dist/skills/bundled/loop.js +0 -358
- package/dist/skills/bundled/remember.js +0 -383
- package/dist/skills/bundled/simplify.js +0 -289
- package/dist/skills/bundled/skillify.js +0 -373
- package/dist/skills/bundled/stuck.js +0 -558
- package/dist/skills/bundled/verify.js +0 -439
- package/dist/testing/vcr.js +0 -486
- package/dist/tools/agent-tool.js +0 -229
- package/dist/tools/apply-patch.js +0 -556
- package/dist/tools/ask-user-question.js +0 -337
- package/dist/tools/ask-user.js +0 -115
- package/dist/tools/bash.js +0 -1238
- package/dist/tools/brief.js +0 -224
- package/dist/tools/cron.js +0 -433
- package/dist/tools/enter-worktree.js +0 -250
- package/dist/tools/exit-worktree.js +0 -147
- package/dist/tools/file-tools.js +0 -553
- package/dist/tools/http-request.js +0 -336
- package/dist/tools/lsp-tools.js +0 -565
- package/dist/tools/mcp-tool.js +0 -260
- package/dist/tools/multi-edit.js +0 -361
- package/dist/tools/powershell.js +0 -268
- package/dist/tools/registry.js +0 -166
- package/dist/tools/server-tools.js +0 -892
- package/dist/tools/skill-tool.js +0 -96
- package/dist/tools/sleep.js +0 -99
- package/dist/tools/synthetic-output.js +0 -133
- package/dist/tools/tasks.js +0 -208
- package/dist/tools/todo-write.js +0 -184
- package/dist/tools/verify-plan-execution.js +0 -295
- package/dist/tools/web-fetch-injection-scanner.js +0 -207
- package/dist/tools/web-fetch.js +0 -720
- package/dist/tools/web-search.js +0 -458
- package/dist/tui/agent-progress-card.js +0 -111
- package/dist/tui/agent-tree-pane.js +0 -9
- package/dist/tui/agent-tree.js +0 -87
- package/dist/tui/ask-cli.js +0 -52
- package/dist/tui/ask-modal.js +0 -211
- package/dist/tui/ask-user-question-chips.js +0 -315
- package/dist/tui/ask-user-question-prompt.js +0 -203
- package/dist/tui/compact-banner.js +0 -81
- package/dist/tui/conversation-pane.js +0 -164
- package/dist/tui/cost-table.js +0 -111
- package/dist/tui/device-flow.js +0 -142
- package/dist/tui/doctor-table.js +0 -46
- package/dist/tui/feedback-prompt.js +0 -156
- package/dist/tui/input-box.js +0 -732
- package/dist/tui/login-picker.js +0 -69
- package/dist/tui/markdown-render.js +0 -266
- package/dist/tui/multi-file-diff-approval.js +0 -375
- package/dist/tui/onboarding-wizard.js +0 -240
- package/dist/tui/permissions-picker.js +0 -86
- package/dist/tui/render.js +0 -160
- package/dist/tui/repl-render.js +0 -770
- package/dist/tui/repl-splash-art.js +0 -64
- package/dist/tui/repl-splash-mascot.js +0 -154
- package/dist/tui/repl-splash.js +0 -117
- package/dist/tui/repl.js +0 -378
- package/dist/tui/slash-palette.js +0 -106
- package/dist/tui/splash-data.js +0 -61
- package/dist/tui/splash.js +0 -31
- package/dist/tui/status-bar.js +0 -209
- package/dist/tui/status-table.js +0 -7
- package/dist/tui/stickers-art.js +0 -136
- package/dist/tui/style-table.js +0 -28
- package/dist/tui/theme-table.js +0 -29
- package/dist/tui/thinking-spinner.js +0 -123
- package/dist/tui/tool-stream-pane.js +0 -140
- package/dist/tui/update-banner.js +0 -33
- package/dist/tui/vim-input.js +0 -267
- package/dist/tui/welcome-banner.js +0 -107
- package/dist/tui/welcome-data.js +0 -293
- package/dist/tui/workspace-context.js +0 -105
- package/docs/examples/codegraph.mcp.json +0 -10
- package/test/scenarios/codegen-create-file.scenario.txt +0 -13
- package/test/scenarios/compact-force.scenario.txt +0 -12
- package/test/scenarios/identity.scenario.txt +0 -11
- package/test/scenarios/persona-handoff.scenario.txt +0 -12
- package/test/scenarios/walkback.scenario.txt +0 -12
package/dist/tui/ask-cli.js
DELETED
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { render, useApp } from 'ink';
|
|
3
|
-
import { AskModal, PlanReviewModal } from './ask-modal.js';
|
|
4
|
-
export async function renderAskCli(options) {
|
|
5
|
-
let resolveOuter;
|
|
6
|
-
const outerPromise = new Promise((resolve) => {
|
|
7
|
-
resolveOuter = resolve;
|
|
8
|
-
});
|
|
9
|
-
function App() {
|
|
10
|
-
const { exit } = useApp();
|
|
11
|
-
return (_jsx(AskModal, { tag: options.tag, onResolve: (verdict) => {
|
|
12
|
-
resolveOuter(verdict);
|
|
13
|
-
// Slight delay so Ink flushes the unmount before the parent
|
|
14
|
-
// CLI prints the verdict line. Otherwise the modal frame and
|
|
15
|
-
// the verdict line can interleave on slow terminals.
|
|
16
|
-
setTimeout(() => exit(), 16);
|
|
17
|
-
} }));
|
|
18
|
-
}
|
|
19
|
-
const instance = render(_jsx(App, {}));
|
|
20
|
-
const verdict = await outerPromise;
|
|
21
|
-
try {
|
|
22
|
-
await instance.waitUntilExit();
|
|
23
|
-
}
|
|
24
|
-
catch {
|
|
25
|
-
// Ink may throw if exit() races with a re-render; the verdict is
|
|
26
|
-
// already captured so we ignore.
|
|
27
|
-
}
|
|
28
|
-
return verdict;
|
|
29
|
-
}
|
|
30
|
-
export async function renderPlanReviewCli(options) {
|
|
31
|
-
let resolveOuter;
|
|
32
|
-
const outerPromise = new Promise((resolve) => {
|
|
33
|
-
resolveOuter = resolve;
|
|
34
|
-
});
|
|
35
|
-
function App() {
|
|
36
|
-
const { exit } = useApp();
|
|
37
|
-
return (_jsx(PlanReviewModal, { tag: options.tag, onResolve: (result) => {
|
|
38
|
-
resolveOuter(result);
|
|
39
|
-
setTimeout(() => exit(), 16);
|
|
40
|
-
} }));
|
|
41
|
-
}
|
|
42
|
-
const instance = render(_jsx(App, {}));
|
|
43
|
-
const result = await outerPromise;
|
|
44
|
-
try {
|
|
45
|
-
await instance.waitUntilExit();
|
|
46
|
-
}
|
|
47
|
-
catch {
|
|
48
|
-
// See renderAskCli — captured verdict supersedes a late Ink throw.
|
|
49
|
-
}
|
|
50
|
-
return result;
|
|
51
|
-
}
|
|
52
|
-
//# sourceMappingURL=ask-cli.js.map
|
package/dist/tui/ask-modal.js
DELETED
|
@@ -1,211 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
/**
|
|
3
|
-
* Office-hours forcing questions + plan-review modals - Sprint .
|
|
4
|
-
*
|
|
5
|
-
* Two Ink components feed by the parsed `<pugi-ask>` / `<pugi-plan-review>`
|
|
6
|
-
* tag records the session module extracts from persona output.
|
|
7
|
-
*
|
|
8
|
-
* - <AskModal />: numbered options (1-4) + optional "Other" custom-input
|
|
9
|
-
* fallback. Operator types `1`, `2`, `3`, `4`, or `o <free text>`.
|
|
10
|
-
*
|
|
11
|
-
* - <PlanReviewModal />: numbered steps + optional risk callout +
|
|
12
|
-
* three-way verdict `[a] approve · [m] modify · [c] cancel`.
|
|
13
|
-
* Operator types `a`, `m`, or `c`. `m` opens a free-text editor;
|
|
14
|
-
* the operator's edited text is returned verbatim to the session.
|
|
15
|
-
*
|
|
16
|
-
* Both components are PURE in the Ink sense — they read props + own
|
|
17
|
-
* useState for the input buffer, and emit one final `onResolve` callback
|
|
18
|
-
* when the operator submits. The REPL root handles wiring the verdict
|
|
19
|
-
* back into the session as the next user turn (prefixed with
|
|
20
|
-
* `[ASK-RESPONSE:<value>]` / `[PLAN-VERDICT:approve|modify|cancel] ...`)
|
|
21
|
-
* so the persona transcript stays linear.
|
|
22
|
-
*
|
|
23
|
-
* Brand voice gate: ASCII glyphs only, no em-dashes, no banned brand
|
|
24
|
-
* words (journey, explore, delight, magical, friendly, AI-powered,
|
|
25
|
-
* pug-tastic). The modal copy is power-word neutral so a localized
|
|
26
|
-
* variant lands cleanly later.
|
|
27
|
-
*/
|
|
28
|
-
import { useState } from 'react';
|
|
29
|
-
import { Box, Text, useInput } from 'ink';
|
|
30
|
-
export function AskModal(props) {
|
|
31
|
-
const [mode, setMode] = useState('pick');
|
|
32
|
-
const [buffer, setBuffer] = useState('');
|
|
33
|
-
useInput((input, key) => {
|
|
34
|
-
// Esc cancels the modal in either mode.
|
|
35
|
-
if (key.escape) {
|
|
36
|
-
props.onResolve({ value: '', cancelled: true });
|
|
37
|
-
return;
|
|
38
|
-
}
|
|
39
|
-
if (mode === 'pick') {
|
|
40
|
-
// Numeric keys 1..N select the matching option, capped at the
|
|
41
|
-
// total option count. Out-of-range keys are ignored.
|
|
42
|
-
const numeric = Number.parseInt(input, 10);
|
|
43
|
-
if (!Number.isNaN(numeric) && numeric >= 1 && numeric <= props.tag.options.length) {
|
|
44
|
-
const opt = props.tag.options[numeric - 1];
|
|
45
|
-
if (opt) {
|
|
46
|
-
props.onResolve({ value: opt.value, cancelled: false });
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
// The "Other" sentinel is the index = options.length + 1. We
|
|
51
|
-
// also accept `o` (lowercase) as a hotkey since the cursor key
|
|
52
|
-
// for "Other" is always one-past-the-last numeric.
|
|
53
|
-
const otherIndex = props.tag.options.length + 1;
|
|
54
|
-
if ((!Number.isNaN(numeric) && numeric === otherIndex)
|
|
55
|
-
|| input === 'o'
|
|
56
|
-
|| input === 'O') {
|
|
57
|
-
setMode('custom');
|
|
58
|
-
setBuffer('');
|
|
59
|
-
return;
|
|
60
|
-
}
|
|
61
|
-
// Any other keystroke: ignored. The hint footer tells the
|
|
62
|
-
// operator the legal keys.
|
|
63
|
-
return;
|
|
64
|
-
}
|
|
65
|
-
// Custom-input mode: line editor.
|
|
66
|
-
if (key.return) {
|
|
67
|
-
// Empty buffer + Enter = cancel custom (return to pick).
|
|
68
|
-
if (buffer.trim().length === 0) {
|
|
69
|
-
setMode('pick');
|
|
70
|
-
setBuffer('');
|
|
71
|
-
return;
|
|
72
|
-
}
|
|
73
|
-
props.onResolve({
|
|
74
|
-
value: '',
|
|
75
|
-
customInput: buffer.trim(),
|
|
76
|
-
cancelled: false,
|
|
77
|
-
});
|
|
78
|
-
return;
|
|
79
|
-
}
|
|
80
|
-
if (key.backspace || key.delete) {
|
|
81
|
-
setBuffer((prev) => prev.slice(0, -1));
|
|
82
|
-
return;
|
|
83
|
-
}
|
|
84
|
-
if (input && !key.meta && !key.ctrl) {
|
|
85
|
-
setBuffer((prev) => prev + input);
|
|
86
|
-
}
|
|
87
|
-
}, { isActive: props.inert !== true });
|
|
88
|
-
return (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [_jsxs(Box, { children: [_jsx(Text, { bold: true, color: "yellow", children: '? ' }), _jsx(Text, { bold: true, children: 'Need your call before I continue' })] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { children: props.tag.question }) }), _jsxs(Box, { flexDirection: "column", marginTop: 1, children: [props.tag.options.map((opt, idx) => (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { children: [_jsx(Text, { color: "#3da9fc", bold: true, children: ` ${idx + 1}. ` }), _jsx(Text, { children: opt.label })] }), opt.desc ? (_jsx(Box, { marginLeft: 5, children: _jsx(Text, { dimColor: true, children: opt.desc }) })) : null] }, opt.value))), _jsxs(Box, { children: [_jsx(Text, { color: "#3da9fc", bold: true, children: ` ${props.tag.options.length + 1}. ` }), _jsx(Text, { dimColor: true, children: 'Other (type a custom answer)' })] })] }), mode === 'pick' ? (_jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: `Press 1-${props.tag.options.length + 1} to choose. Esc cancels.` }) })) : (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsxs(Box, { children: [_jsx(Text, { color: "#3da9fc", children: '> ' }), _jsx(Text, { children: buffer }), _jsx(Text, { inverse: true, children: ' ' })] }), _jsx(Box, { children: _jsx(Text, { dimColor: true, children: 'Type your custom answer. Enter submits. Esc cancels.' }) })] }))] }));
|
|
89
|
-
}
|
|
90
|
-
export function PlanReviewModal(props) {
|
|
91
|
-
const [mode, setMode] = useState('pick');
|
|
92
|
-
const [buffer, setBuffer] = useState('');
|
|
93
|
-
useInput((input, key) => {
|
|
94
|
-
if (key.escape) {
|
|
95
|
-
// Escape from EITHER mode means cancel — symmetric with AskModal.
|
|
96
|
-
props.onResolve({ verdict: 'cancel' });
|
|
97
|
-
return;
|
|
98
|
-
}
|
|
99
|
-
if (mode === 'pick') {
|
|
100
|
-
if (input === 'a' || input === 'A') {
|
|
101
|
-
props.onResolve({ verdict: 'approve' });
|
|
102
|
-
return;
|
|
103
|
-
}
|
|
104
|
-
if (input === 'c' || input === 'C') {
|
|
105
|
-
props.onResolve({ verdict: 'cancel' });
|
|
106
|
-
return;
|
|
107
|
-
}
|
|
108
|
-
if (input === 'm' || input === 'M') {
|
|
109
|
-
setMode('modify');
|
|
110
|
-
setBuffer('');
|
|
111
|
-
return;
|
|
112
|
-
}
|
|
113
|
-
return;
|
|
114
|
-
}
|
|
115
|
-
// Modify-mode line editor.
|
|
116
|
-
if (key.return) {
|
|
117
|
-
if (buffer.trim().length === 0) {
|
|
118
|
-
setMode('pick');
|
|
119
|
-
setBuffer('');
|
|
120
|
-
return;
|
|
121
|
-
}
|
|
122
|
-
props.onResolve({ verdict: 'modify', modifyText: buffer.trim() });
|
|
123
|
-
return;
|
|
124
|
-
}
|
|
125
|
-
if (key.backspace || key.delete) {
|
|
126
|
-
setBuffer((prev) => prev.slice(0, -1));
|
|
127
|
-
return;
|
|
128
|
-
}
|
|
129
|
-
if (input && !key.meta && !key.ctrl) {
|
|
130
|
-
setBuffer((prev) => prev + input);
|
|
131
|
-
}
|
|
132
|
-
}, { isActive: props.inert !== true });
|
|
133
|
-
return (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "magenta", paddingX: 1, children: [_jsxs(Box, { children: [_jsx(Text, { bold: true, color: "magenta", children: '? ' }), _jsx(Text, { bold: true, children: 'Plan review - approve before I execute' })] }), _jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Text, { dimColor: true, children: 'Steps:' }), props.tag.steps.map((step, idx) => (_jsxs(Box, { children: [_jsx(Text, { color: "#3da9fc", bold: true, children: ` ${idx + 1}. ` }), _jsx(Text, { children: step.text })] }, `step-${idx}`)))] }), props.tag.risk ? (_jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { bold: true, color: "red", children: 'Risk:' }), _jsx(Box, { marginLeft: 2, children: _jsx(Text, { children: props.tag.risk }) })] })) : null, mode === 'pick' ? (_jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsxs(Box, { children: [_jsx(Text, { color: "green", bold: true, children: ' [a] approve ' }), _jsx(Text, { color: "yellow", bold: true, children: '[m] modify ' }), _jsx(Text, { color: "red", bold: true, children: '[c] cancel' })] }), _jsx(Box, { children: _jsx(Text, { dimColor: true, children: 'Press a, m, or c. Esc cancels.' }) })] })) : (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsxs(Box, { children: [_jsx(Text, { color: "yellow", children: 'modify > ' }), _jsx(Text, { children: buffer }), _jsx(Text, { inverse: true, children: ' ' })] }), _jsx(Box, { children: _jsx(Text, { dimColor: true, children: 'Type the revision. Enter submits. Esc cancels.' }) })] }))] }));
|
|
134
|
-
}
|
|
135
|
-
/* ------------------------------------------------------------------ */
|
|
136
|
-
/* Verdict serialisation */
|
|
137
|
-
/* ------------------------------------------------------------------ */
|
|
138
|
-
/**
|
|
139
|
-
* Encode an ask-modal verdict into the literal string the session
|
|
140
|
-
* injects as the next operator turn. The persona's prompt teaches it
|
|
141
|
-
* to recognise this prefix, so the conversation stays coherent without
|
|
142
|
-
* a side channel.
|
|
143
|
-
*
|
|
144
|
-
* Examples:
|
|
145
|
-
* { value: 'vercel' } -> "[ASK-RESPONSE:vercel]"
|
|
146
|
-
* { value: '', customInput: 'gcp'} -> "[ASK-RESPONSE:other] gcp"
|
|
147
|
-
* { cancelled: true } -> "[ASK-RESPONSE:cancelled]"
|
|
148
|
-
*
|
|
149
|
-
* customInput is stripped of any leading `[ASK-RESPONSE:...]` /
|
|
150
|
-
* `[PLAN-VERDICT:...]` pattern so a forged operator prefix cannot be
|
|
151
|
-
* read as a different verdict by a prefix-greedy persona (Claude
|
|
152
|
-
* triple-review P1, PR).
|
|
153
|
-
*/
|
|
154
|
-
export function encodeAskVerdict(verdict) {
|
|
155
|
-
if (verdict.cancelled)
|
|
156
|
-
return '[ASK-RESPONSE:cancelled]';
|
|
157
|
-
if (verdict.value.length > 0)
|
|
158
|
-
return `[ASK-RESPONSE:${verdict.value}]`;
|
|
159
|
-
const customInput = verdict.customInput
|
|
160
|
-
? sanitiseVerdictText(verdict.customInput)
|
|
161
|
-
: '';
|
|
162
|
-
if (customInput.length > 0) {
|
|
163
|
-
return `[ASK-RESPONSE:other] ${customInput}`;
|
|
164
|
-
}
|
|
165
|
-
return '[ASK-RESPONSE:cancelled]';
|
|
166
|
-
}
|
|
167
|
-
/**
|
|
168
|
-
* Encode a plan-review verdict into the next operator turn. Mirrors
|
|
169
|
-
* the ask encoding so the persona sees a single grammar.
|
|
170
|
-
*
|
|
171
|
-
* modifyText is sanitised against verdict-header forgery the same way
|
|
172
|
-
* customInput is in encodeAskVerdict (Claude triple-review P1).
|
|
173
|
-
*/
|
|
174
|
-
export function encodePlanReviewVerdict(result) {
|
|
175
|
-
switch (result.verdict) {
|
|
176
|
-
case 'approve':
|
|
177
|
-
return '[PLAN-VERDICT:approve]';
|
|
178
|
-
case 'cancel':
|
|
179
|
-
return '[PLAN-VERDICT:cancel]';
|
|
180
|
-
case 'modify': {
|
|
181
|
-
const modifyText = result.modifyText
|
|
182
|
-
? sanitiseVerdictText(result.modifyText)
|
|
183
|
-
: '';
|
|
184
|
-
if (modifyText.length > 0) {
|
|
185
|
-
return `[PLAN-VERDICT:modify] ${modifyText}`;
|
|
186
|
-
}
|
|
187
|
-
return '[PLAN-VERDICT:cancel]';
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
/**
|
|
192
|
-
* Strip any leading `[ASK-RESPONSE:...]` or `[PLAN-VERDICT:...]`
|
|
193
|
-
* pattern from free-text operator input so a malicious or accidental
|
|
194
|
-
* operator string cannot forge a verdict header. Iterates because the
|
|
195
|
-
* operator could prepend several forged headers in a row.
|
|
196
|
-
*
|
|
197
|
-
* Mirrors the same-named helper in core/repl/session.ts. Kept in both
|
|
198
|
-
* modules so the test surfaces of ask-modal and session are
|
|
199
|
-
* independently exercisable without circular imports.
|
|
200
|
-
*/
|
|
201
|
-
function sanitiseVerdictText(raw) {
|
|
202
|
-
let cleaned = raw;
|
|
203
|
-
for (let i = 0; i < raw.length + 4; i += 1) {
|
|
204
|
-
const stripped = cleaned.replace(/^\s*\[(?:ASK-RESPONSE|PLAN-VERDICT):[^\]]*\]\s*/u, '');
|
|
205
|
-
if (stripped === cleaned)
|
|
206
|
-
break;
|
|
207
|
-
cleaned = stripped;
|
|
208
|
-
}
|
|
209
|
-
return cleaned.trim();
|
|
210
|
-
}
|
|
211
|
-
//# sourceMappingURL=ask-modal.js.map
|
|
@@ -1,315 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
/**
|
|
3
|
-
* AskUserQuestionChips — multi-question short-format chip renderer
|
|
4
|
-
* for the structured AskUserQuestion tool (PUGI-480).
|
|
5
|
-
*
|
|
6
|
-
* Renders up to 3 question chips side-by-side, each with up to ~5 short
|
|
7
|
-
* (≤ 5-word) option labels. Replaces paragraph-wall prompts с a compact
|
|
8
|
-
* keyboard-driven picker:
|
|
9
|
-
*
|
|
10
|
-
* Pugi: 2 quick choices
|
|
11
|
-
* ┌────────────────────────┐
|
|
12
|
-
* │ Stack │
|
|
13
|
-
* │ ▸ 1 Browser JS │
|
|
14
|
-
* │ 2 React │
|
|
15
|
-
* │ 3 Other... │
|
|
16
|
-
* └────────────────────────┘
|
|
17
|
-
* ┌────────────────────────┐
|
|
18
|
-
* │ Size │
|
|
19
|
-
* │ ▸ 1 9x9 (classic) │
|
|
20
|
-
* │ 2 16x16 │
|
|
21
|
-
* │ 3 Custom │
|
|
22
|
-
* └────────────────────────┘
|
|
23
|
-
* [Enter] use highlighted defaults · [↑↓] navigate · [s] skip
|
|
24
|
-
*
|
|
25
|
-
* Contract:
|
|
26
|
-
* - questions[].options[].label MUST be ≤ 5 words (renderer truncates
|
|
27
|
-
* overflow с ellipsis defensively; the schema validator rejects at
|
|
28
|
-
* the tool entry point so the model never produces overflow legally).
|
|
29
|
-
* - First option of each question is pre-highlighted (default).
|
|
30
|
-
* - Enter без movement applies defaults across ALL questions.
|
|
31
|
-
* - 1-9 jump к the matching option in the focused question.
|
|
32
|
-
* - ↑↓ navigate within the focused question.
|
|
33
|
-
* - ←→ (and Tab/Shift-Tab) move between questions.
|
|
34
|
-
* - [s] skips the focused question (uses its default) and advances to
|
|
35
|
-
* the next; [s] on the last question commits.
|
|
36
|
-
* - [Esc] cancels the entire dialog (user_cancelled).
|
|
37
|
-
*
|
|
38
|
-
* Non-TTY fallback: when the `forceFallback` prop is true, renders a
|
|
39
|
-
* static numbered text list and does NOT mount the Ink useInput hook.
|
|
40
|
-
* The component itself does NOT sniff `process.stdin.isTTY` — that gate
|
|
41
|
-
* lives in the upstream REPL wiring, which either mounts this chip
|
|
42
|
-
* component (TTY path) or emits the legacy `[user_input_required]`
|
|
43
|
-
* envelope (non-TTY path). Keeping the gate в the caller keeps the
|
|
44
|
-
* component testable under ink-testing-library (which mocks stdin but
|
|
45
|
-
* не sets isTTY).
|
|
46
|
-
*
|
|
47
|
-
* Brand voice gate: ASCII glyphs only, zero attribution to external AIs,
|
|
48
|
-
* no em-dashes. Power-word neutral so localised variants land cleanly.
|
|
49
|
-
*/
|
|
50
|
-
import { useMemo, useState } from 'react';
|
|
51
|
-
import { Box, Text, useInput } from 'ink';
|
|
52
|
-
/**
|
|
53
|
-
* Hard truncation cap. The schema validator enforces ≤ 5 words per
|
|
54
|
-
* label, but the renderer still defends in depth: a malicious or
|
|
55
|
-
* legacy payload with a 14-word label MUST not blow the chip width.
|
|
56
|
-
*/
|
|
57
|
-
export const ASK_CHIPS_LABEL_WORD_CAP = 5;
|
|
58
|
-
export const ASK_CHIPS_LABEL_CHAR_CAP = 22;
|
|
59
|
-
export const ASK_CHIPS_QUESTION_CAP = 3;
|
|
60
|
-
export const ASK_CHIPS_SKIP_LABEL = 'Skip — use defaults';
|
|
61
|
-
/**
|
|
62
|
-
* PUGI-130 preview pane width (columns). Sized for a default 80-col
|
|
63
|
-
* terminal: option column ≈ 32, preview pane ≈ 44 with 4 gutters/borders.
|
|
64
|
-
* The pane wraps long lines defensively but the schema-level 5000-char
|
|
65
|
-
* cap is the primary guard against runaway payloads.
|
|
66
|
-
*/
|
|
67
|
-
export const ASK_CHIPS_PREVIEW_PANE_WIDTH = 44;
|
|
68
|
-
/**
|
|
69
|
-
* Defensive char cap applied at render time. Schema enforces 5000 chars
|
|
70
|
-
* at the tool boundary; renderer mirrors the cap so a legacy / malformed
|
|
71
|
-
* payload sneaking past Zod still cannot exhaust the terminal scrollback.
|
|
72
|
-
*/
|
|
73
|
-
export const ASK_CHIPS_PREVIEW_CHAR_CAP = 5000;
|
|
74
|
-
/**
|
|
75
|
-
* True if ANY option в ANY question of the bundle carries a non-empty
|
|
76
|
-
* `preview` field. The renderer uses this gate to switch к side-by-side
|
|
77
|
-
* layout (vertical option list + preview pane). When false, the legacy
|
|
78
|
-
* horizontal chip layout is preserved (zero regression for callers
|
|
79
|
-
* shipping previewless payloads — contract intact).
|
|
80
|
-
*/
|
|
81
|
-
export function hasAnyPreview(questions) {
|
|
82
|
-
return questions.some((q) => q.options.some((opt) => typeof opt.preview === 'string' && opt.preview.length > 0));
|
|
83
|
-
}
|
|
84
|
-
/**
|
|
85
|
-
* Defensive char cap on preview content. Schema enforces 5000 chars at
|
|
86
|
-
* the tool boundary; we mirror the cap here so legacy / malformed
|
|
87
|
-
* payloads slipping past Zod still cannot exhaust the terminal.
|
|
88
|
-
*/
|
|
89
|
-
export function clampPreview(raw) {
|
|
90
|
-
if (raw.length <= ASK_CHIPS_PREVIEW_CHAR_CAP)
|
|
91
|
-
return raw;
|
|
92
|
-
return `${raw.slice(0, ASK_CHIPS_PREVIEW_CHAR_CAP - 1)}…`;
|
|
93
|
-
}
|
|
94
|
-
/**
|
|
95
|
-
* Truncate a label к the configured word / character caps. Always
|
|
96
|
-
* append "…" when truncation happens so the operator knows the chip
|
|
97
|
-
* is hiding content.
|
|
98
|
-
*/
|
|
99
|
-
export function truncateLabel(raw) {
|
|
100
|
-
const trimmed = raw.trim().replace(/\s+/gu, ' ');
|
|
101
|
-
// Word cap (whitespace-delimited). 5-word rule is the canonical limit.
|
|
102
|
-
const words = trimmed.split(' ');
|
|
103
|
-
let candidate = trimmed;
|
|
104
|
-
if (words.length > ASK_CHIPS_LABEL_WORD_CAP) {
|
|
105
|
-
candidate = `${words.slice(0, ASK_CHIPS_LABEL_WORD_CAP).join(' ')}…`;
|
|
106
|
-
}
|
|
107
|
-
// Char cap (defense-in-depth — protects chip width even с short-word
|
|
108
|
-
// payloads like "AAAAAAAAAAAAAAAAAAAAA" that pass the word gate).
|
|
109
|
-
if (candidate.length > ASK_CHIPS_LABEL_CHAR_CAP) {
|
|
110
|
-
candidate = `${candidate.slice(0, ASK_CHIPS_LABEL_CHAR_CAP - 1)}…`;
|
|
111
|
-
}
|
|
112
|
-
return candidate;
|
|
113
|
-
}
|
|
114
|
-
/**
|
|
115
|
-
* The component renders в interactive mode by default. The non-TTY
|
|
116
|
-
* gate is the caller's responsibility — see ask-user-question-chips.tsx
|
|
117
|
-
* header docs. Production REPL wiring should check `process.stdin.isTTY`
|
|
118
|
-
* upstream и pass `forceFallback={true}` when stdin is not a TTY, OR
|
|
119
|
-
* skip mounting the chip component entirely и emit the legacy
|
|
120
|
-
* `[user_input_required]` envelope.
|
|
121
|
-
*/
|
|
122
|
-
function shouldUseFallback(forceFallback) {
|
|
123
|
-
return forceFallback === true;
|
|
124
|
-
}
|
|
125
|
-
/**
|
|
126
|
-
* Build the default selections vector (all first options) so Enter
|
|
127
|
-
* без movement applies a coherent set, and so non-TTY fallback can
|
|
128
|
-
* commit immediately.
|
|
129
|
-
*/
|
|
130
|
-
function buildDefaults(questions) {
|
|
131
|
-
return questions.map((q) => {
|
|
132
|
-
const first = q.options[0];
|
|
133
|
-
return {
|
|
134
|
-
header: q.header,
|
|
135
|
-
pickedIndex: 0,
|
|
136
|
-
pickedLabel: first ? truncateLabel(first.label) : '',
|
|
137
|
-
skipped: false,
|
|
138
|
-
};
|
|
139
|
-
});
|
|
140
|
-
}
|
|
141
|
-
export function AskUserQuestionChips(props) {
|
|
142
|
-
const useFallback = shouldUseFallback(props.forceFallback);
|
|
143
|
-
// Defensive cap: never render more than ASK_CHIPS_QUESTION_CAP chips.
|
|
144
|
-
// Schema enforces this at the tool boundary; the renderer still trims
|
|
145
|
-
// в case a legacy / malicious payload sneaks through.
|
|
146
|
-
const questions = useMemo(() => props.questions.slice(0, ASK_CHIPS_QUESTION_CAP), [props.questions]);
|
|
147
|
-
// Cursor state: which question is focused, and which option within
|
|
148
|
-
// each question is highlighted. Initialised к first option (default).
|
|
149
|
-
const [focusedQuestion, setFocusedQuestion] = useState(0);
|
|
150
|
-
const [cursorPerQuestion, setCursorPerQuestion] = useState(() => questions.map(() => 0));
|
|
151
|
-
// Per-question explicit skip flag. Skipped questions still emit their
|
|
152
|
-
// default index, but flagged `skipped: true` so downstream can audit.
|
|
153
|
-
const [skipped, setSkipped] = useState(() => questions.map(() => false));
|
|
154
|
-
function commit(skipMask = skipped) {
|
|
155
|
-
const selections = questions.map((q, qIdx) => {
|
|
156
|
-
const optionIdx = cursorPerQuestion[qIdx] ?? 0;
|
|
157
|
-
const opt = q.options[optionIdx] ?? q.options[0];
|
|
158
|
-
return {
|
|
159
|
-
header: q.header,
|
|
160
|
-
pickedIndex: optionIdx,
|
|
161
|
-
pickedLabel: opt ? truncateLabel(opt.label) : '',
|
|
162
|
-
skipped: skipMask[qIdx] === true,
|
|
163
|
-
};
|
|
164
|
-
});
|
|
165
|
-
props.onResolve({ selections, cancelled: false });
|
|
166
|
-
}
|
|
167
|
-
function cancel() {
|
|
168
|
-
props.onResolve({ selections: buildDefaults(questions), cancelled: true });
|
|
169
|
-
}
|
|
170
|
-
useInput((input, key) => {
|
|
171
|
-
// Esc cancels the entire AskUserQuestion (user_cancelled).
|
|
172
|
-
if (key.escape) {
|
|
173
|
-
cancel();
|
|
174
|
-
return;
|
|
175
|
-
}
|
|
176
|
-
// Enter commits the current selection (applies defaults across
|
|
177
|
-
// any questions the operator did not touch).
|
|
178
|
-
if (key.return) {
|
|
179
|
-
commit();
|
|
180
|
-
return;
|
|
181
|
-
}
|
|
182
|
-
// [s] skips the focused question (uses default), advances to next.
|
|
183
|
-
// [s] on the LAST question commits the whole set.
|
|
184
|
-
if (input === 's' || input === 'S') {
|
|
185
|
-
const nextSkipped = skipped.slice();
|
|
186
|
-
nextSkipped[focusedQuestion] = true;
|
|
187
|
-
if (focusedQuestion >= questions.length - 1) {
|
|
188
|
-
setSkipped(nextSkipped);
|
|
189
|
-
commit(nextSkipped);
|
|
190
|
-
return;
|
|
191
|
-
}
|
|
192
|
-
setSkipped(nextSkipped);
|
|
193
|
-
setFocusedQuestion((q) => Math.min(q + 1, questions.length - 1));
|
|
194
|
-
return;
|
|
195
|
-
}
|
|
196
|
-
// ↑/↓ navigate within the focused question.
|
|
197
|
-
if (key.upArrow) {
|
|
198
|
-
setCursorPerQuestion((prev) => {
|
|
199
|
-
const next = prev.slice();
|
|
200
|
-
const opts = questions[focusedQuestion]?.options ?? [];
|
|
201
|
-
const len = opts.length;
|
|
202
|
-
if (len === 0)
|
|
203
|
-
return prev;
|
|
204
|
-
const cur = next[focusedQuestion] ?? 0;
|
|
205
|
-
next[focusedQuestion] = (cur - 1 + len) % len;
|
|
206
|
-
return next;
|
|
207
|
-
});
|
|
208
|
-
return;
|
|
209
|
-
}
|
|
210
|
-
if (key.downArrow) {
|
|
211
|
-
setCursorPerQuestion((prev) => {
|
|
212
|
-
const next = prev.slice();
|
|
213
|
-
const opts = questions[focusedQuestion]?.options ?? [];
|
|
214
|
-
const len = opts.length;
|
|
215
|
-
if (len === 0)
|
|
216
|
-
return prev;
|
|
217
|
-
const cur = next[focusedQuestion] ?? 0;
|
|
218
|
-
next[focusedQuestion] = (cur + 1) % len;
|
|
219
|
-
return next;
|
|
220
|
-
});
|
|
221
|
-
return;
|
|
222
|
-
}
|
|
223
|
-
// ←/→ (or Tab/Shift-Tab) navigate between questions.
|
|
224
|
-
if (key.leftArrow || (key.shift && key.tab)) {
|
|
225
|
-
setFocusedQuestion((q) => (q - 1 + questions.length) % questions.length);
|
|
226
|
-
return;
|
|
227
|
-
}
|
|
228
|
-
if (key.rightArrow || key.tab) {
|
|
229
|
-
setFocusedQuestion((q) => (q + 1) % questions.length);
|
|
230
|
-
return;
|
|
231
|
-
}
|
|
232
|
-
// 1-9 jumps к the matching option in the focused question.
|
|
233
|
-
const numeric = Number.parseInt(input, 10);
|
|
234
|
-
if (!Number.isNaN(numeric) && numeric >= 1 && numeric <= 9) {
|
|
235
|
-
const opts = questions[focusedQuestion]?.options ?? [];
|
|
236
|
-
const target = numeric - 1;
|
|
237
|
-
if (target >= 0 && target < opts.length) {
|
|
238
|
-
setCursorPerQuestion((prev) => {
|
|
239
|
-
const next = prev.slice();
|
|
240
|
-
next[focusedQuestion] = target;
|
|
241
|
-
return next;
|
|
242
|
-
});
|
|
243
|
-
}
|
|
244
|
-
return;
|
|
245
|
-
}
|
|
246
|
-
}, { isActive: props.inert !== true && useFallback === false });
|
|
247
|
-
// ─── Non-TTY fallback ────────────────────────────────────────────────
|
|
248
|
-
// When stdin is not a TTY we still need to render SOMETHING — but
|
|
249
|
-
// we cannot drive keystrokes, so we emit a numbered text list per
|
|
250
|
-
// question and let the upstream envelope handler ask the operator.
|
|
251
|
-
if (useFallback) {
|
|
252
|
-
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { children: _jsx(Text, { bold: true, children: `Pugi: ${questions.length} quick ${questions.length === 1 ? 'choice' : 'choices'}` }) }), questions.map((q, qIdx) => (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Text, { bold: true, color: "cyan", children: `${q.header}` }), q.options.map((opt, oIdx) => (_jsx(Text, { children: ` ${oIdx + 1}. ${truncateLabel(opt.label)}${oIdx === 0 ? ' (default)' : ''}` }, `q-${qIdx}-o-${oIdx}`)))] }, `q-${qIdx}-${q.header}`))), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, italic: true, children: `(non-interactive — defaults apply)` }) })] }));
|
|
253
|
-
}
|
|
254
|
-
// ─── Interactive Ink render ─────────────────────────────────────────
|
|
255
|
-
// PUGI-130: when ANY option carries `preview`, switch layout — chips
|
|
256
|
-
// stack vertically (option list) and a preview pane mounts on the
|
|
257
|
-
// right, updating as the cursor moves. Otherwise fall back to the
|
|
258
|
-
// existing horizontal chip layout (zero-regression contract).
|
|
259
|
-
const previewMode = hasAnyPreview(questions);
|
|
260
|
-
if (previewMode) {
|
|
261
|
-
const focusedQ = questions[focusedQuestion];
|
|
262
|
-
const focusedCursor = cursorPerQuestion[focusedQuestion] ?? 0;
|
|
263
|
-
const focusedOpt = focusedQ?.options[focusedCursor];
|
|
264
|
-
const rawPreview = focusedOpt?.preview ?? '';
|
|
265
|
-
const previewText = clampPreview(rawPreview);
|
|
266
|
-
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { children: _jsx(Text, { bold: true, children: `Pugi: ${questions.length} quick ${questions.length === 1 ? 'choice' : 'choices'}` }) }), _jsxs(Box, { flexDirection: "row", marginTop: 1, children: [_jsx(Box, { flexDirection: "column", marginRight: 2, children: questions.map((q, qIdx) => {
|
|
267
|
-
const isFocused = qIdx === focusedQuestion;
|
|
268
|
-
const cursor = cursorPerQuestion[qIdx] ?? 0;
|
|
269
|
-
const isSkipped = skipped[qIdx] === true;
|
|
270
|
-
return (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: isFocused ? 'cyan' : 'gray', paddingX: 1, marginBottom: qIdx < questions.length - 1 ? 1 : 0, minWidth: 28, children: [_jsxs(Box, { children: [_jsx(Text, { bold: true, color: isFocused ? 'cyan' : undefined, children: q.header }), isSkipped ? (_jsx(Text, { dimColor: true, italic: true, children: ' (skipped)' })) : null] }), q.options.map((opt, oIdx) => {
|
|
271
|
-
const isHighlighted = isFocused && oIdx === cursor;
|
|
272
|
-
const label = truncateLabel(opt.label);
|
|
273
|
-
const isSkipOption = label === ASK_CHIPS_SKIP_LABEL ||
|
|
274
|
-
opt.label === ASK_CHIPS_SKIP_LABEL;
|
|
275
|
-
const hasPreview = typeof opt.preview === 'string' && opt.preview.length > 0;
|
|
276
|
-
return (_jsxs(Box, { children: [_jsx(Text, { color: isHighlighted ? 'cyan' : undefined, bold: isHighlighted, children: isHighlighted ? '▸ ' : ' ' }), _jsx(Text, { color: isHighlighted ? 'cyan' : undefined, children: `${oIdx + 1} ` }), isSkipOption ? (_jsx(Text, { dimColor: true, italic: true, children: label })) : (_jsx(Text, { bold: isHighlighted, children: label })), hasPreview ? (_jsx(Text, { dimColor: true, children: ' ⊕' })) : null] }, `opt-${qIdx}-${oIdx}-${opt.label}`));
|
|
277
|
-
})] }, `chip-${qIdx}-${q.header}`));
|
|
278
|
-
}) }), _jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: "gray", paddingX: 1, minWidth: ASK_CHIPS_PREVIEW_PANE_WIDTH, flexGrow: 1, children: [_jsxs(Box, { children: [_jsx(Text, { bold: true, dimColor: true, children: 'Preview' }), focusedOpt ? (_jsx(Text, { dimColor: true, children: ` · ${truncateLabel(focusedOpt.label)}` })) : null] }), previewText.length > 0 ? (previewText.split('\n').map((line, lineIdx) => (_jsx(Text, { children: line.length > 0 ? line : ' ' }, `pv-${focusedQuestion}-${focusedCursor}-${lineIdx}`)))) : (_jsx(Text, { dimColor: true, italic: true, children: '(no preview for this option)' }))] })] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: `[Enter] use highlighted defaults · [↑↓] navigate · [←→] switch · [s] skip · [Esc] cancel` }) })] }));
|
|
279
|
-
}
|
|
280
|
-
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { children: _jsx(Text, { bold: true, children: `Pugi: ${questions.length} quick ${questions.length === 1 ? 'choice' : 'choices'}` }) }), _jsx(Box, { flexDirection: "row", marginTop: 1, children: questions.map((q, qIdx) => {
|
|
281
|
-
const isFocused = qIdx === focusedQuestion;
|
|
282
|
-
const cursor = cursorPerQuestion[qIdx] ?? 0;
|
|
283
|
-
const isSkipped = skipped[qIdx] === true;
|
|
284
|
-
return (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: isFocused ? 'cyan' : 'gray', paddingX: 1, marginRight: 1, minWidth: 24, children: [_jsxs(Box, { children: [_jsx(Text, { bold: true, color: isFocused ? 'cyan' : undefined, children: q.header }), isSkipped ? (_jsx(Text, { dimColor: true, italic: true, children: ' (skipped)' })) : null] }), q.options.map((opt, oIdx) => {
|
|
285
|
-
const isHighlighted = isFocused && oIdx === cursor;
|
|
286
|
-
const label = truncateLabel(opt.label);
|
|
287
|
-
const isSkipOption = label === ASK_CHIPS_SKIP_LABEL ||
|
|
288
|
-
opt.label === ASK_CHIPS_SKIP_LABEL;
|
|
289
|
-
return (_jsxs(Box, { children: [_jsx(Text, { color: isHighlighted ? 'cyan' : undefined, bold: isHighlighted, children: isHighlighted ? '▸ ' : ' ' }), _jsx(Text, { color: isHighlighted ? 'cyan' : undefined, children: `${oIdx + 1} ` }), isSkipOption ? (_jsx(Text, { dimColor: true, italic: true, children: label })) : (_jsx(Text, { bold: isHighlighted, children: label }))] }, `opt-${qIdx}-${oIdx}-${opt.label}`));
|
|
290
|
-
})] }, `chip-${qIdx}-${q.header}`));
|
|
291
|
-
}) }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: `[Enter] use highlighted defaults · [↑↓] navigate · [←→] switch · [s] skip · [Esc] cancel` }) })] }));
|
|
292
|
-
}
|
|
293
|
-
/**
|
|
294
|
-
* Encode the chip verdict into the persona-recognisable turn-injection
|
|
295
|
-
* string. Mirrors `encodeAskUserQuestionVerdict` from the single-question
|
|
296
|
-
* prompt so the model sees a consistent grammar.
|
|
297
|
-
*
|
|
298
|
-
* Examples:
|
|
299
|
-
* - { cancelled: true } → "[ASK-USER-QUESTION:cancelled]"
|
|
300
|
-
* - All defaults → "[ASK-USER-QUESTION:answered] Stack=Browser JS; Size=9x9 (classic)"
|
|
301
|
-
* - One skipped → "[ASK-USER-QUESTION:answered] Stack=Browser JS; Size=(skipped: 9x9)"
|
|
302
|
-
*/
|
|
303
|
-
export function encodeAskUserQuestionChipsVerdict(result) {
|
|
304
|
-
if (result.cancelled)
|
|
305
|
-
return '[ASK-USER-QUESTION:cancelled]';
|
|
306
|
-
if (result.selections.length === 0)
|
|
307
|
-
return '[ASK-USER-QUESTION:cancelled]';
|
|
308
|
-
const parts = result.selections.map((sel) => {
|
|
309
|
-
if (sel.skipped)
|
|
310
|
-
return `${sel.header}=(skipped: ${sel.pickedLabel})`;
|
|
311
|
-
return `${sel.header}=${sel.pickedLabel}`;
|
|
312
|
-
});
|
|
313
|
-
return `[ASK-USER-QUESTION:answered] ${parts.join('; ')}`;
|
|
314
|
-
}
|
|
315
|
-
//# sourceMappingURL=ask-user-question-chips.js.map
|