@pugi/cli 0.1.0-beta.99 → 1.0.0-alpha.2
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 -195
- 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
|
@@ -1,166 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Pugi CLI ↔ admin-api version handshake — CLI-side interceptor.
|
|
3
|
-
*
|
|
4
|
-
* Wraps the engine HTTP transport (and, via the same helpers, any
|
|
5
|
-
* other `fetch` call the CLI makes to admin-api) so the CLI:
|
|
6
|
-
*
|
|
7
|
-
* 1. Sends `X-Pugi-Cli-Version: <installed semver>` on every
|
|
8
|
-
* outbound request. The server middleware
|
|
9
|
-
* (`apps/admin-api/src/runtime/cli-version.middleware.ts`) reads
|
|
10
|
-
* this header and decides whether to honour, soft-warn, or 426.
|
|
11
|
-
*
|
|
12
|
-
* 2. Inspects every response for:
|
|
13
|
-
* - `X-Pugi-Cli-Upgrade-Recommended` → cache the server's
|
|
14
|
-
* recommendation so `UpdateBanner` can compare it against the
|
|
15
|
-
* npm-registry poll and show the operator the higher of the two.
|
|
16
|
-
* - `X-Pugi-Server-Version` → cache for diagnostics
|
|
17
|
-
* (`pugi doctor --json`).
|
|
18
|
-
*
|
|
19
|
-
* 3. Throws `PugiCliUpgradeRequiredError` when the server returns
|
|
20
|
-
* HTTP 426. The top-level catch in `index.ts` / `runtime/cli.ts`
|
|
21
|
-
* renders a clean upgrade banner + `process.exit(1)`.
|
|
22
|
-
*
|
|
23
|
-
* # Design: pure helpers, not a callable wrapper
|
|
24
|
-
*
|
|
25
|
-
* Wrapping fetch as a higher-order function would require every
|
|
26
|
-
* transport call site to opt in via `interceptedFetch(...)`. Instead
|
|
27
|
-
* we expose three small helpers that the existing transport classes
|
|
28
|
-
* (currently just `AnvilEngineLoopClient`) call inline:
|
|
29
|
-
*
|
|
30
|
-
* - `injectClientVersionHeader(headers)` — adds the X-Pugi-Cli-Version
|
|
31
|
-
* entry to an outbound header bag.
|
|
32
|
-
* - `inspectVersionResponse(response)` — reads recommended/server
|
|
33
|
-
* headers, updates the module cache. Returns void.
|
|
34
|
-
* - `assertNotUpgradeRequired(response)` — if status is 426, parses
|
|
35
|
-
* the JSON body and throws `PugiCliUpgradeRequiredError`. Returns
|
|
36
|
-
* void otherwise.
|
|
37
|
-
*
|
|
38
|
-
* That layout keeps the spec tests trivially focused on each branch and
|
|
39
|
-
* gives future SSE callers (engine-stream EventSource) the same hooks
|
|
40
|
-
* without touching the fetch wrapper indirection.
|
|
41
|
-
*/
|
|
42
|
-
import { PUGI_CLI_UPGRADE_RECOMMENDED_HEADER, PUGI_CLI_VERSION_HEADER, PUGI_SERVER_VERSION_HEADER, } from '../../runtime/version.js';
|
|
43
|
-
/**
|
|
44
|
-
* Thrown when admin-api responds with HTTP 426 Upgrade Required. The
|
|
45
|
-
* top-level CLI catch (see `runtime/cli.ts`) renders this with the
|
|
46
|
-
* operator-friendly upgrade banner.
|
|
47
|
-
*
|
|
48
|
-
* Mirrors the server-side `CliUpgradeRequiredBody` shape; populated
|
|
49
|
-
* from the response JSON when present and from the request context
|
|
50
|
-
* (installed version) when the body parse fails.
|
|
51
|
-
*/
|
|
52
|
-
export class PugiCliUpgradeRequiredError extends Error {
|
|
53
|
-
code = 'cli_upgrade_required';
|
|
54
|
-
installedVersion;
|
|
55
|
-
minClientVersion;
|
|
56
|
-
recommendedVersion;
|
|
57
|
-
upgradeCommand;
|
|
58
|
-
upgradeUrl;
|
|
59
|
-
constructor(details) {
|
|
60
|
-
super(details.message ??
|
|
61
|
-
`Pugi CLI ${details.installedVersion} is below the server minimum ${details.minClientVersion}. Upgrade with: ${details.upgradeCommand}`);
|
|
62
|
-
this.name = 'PugiCliUpgradeRequiredError';
|
|
63
|
-
this.installedVersion = details.installedVersion;
|
|
64
|
-
this.minClientVersion = details.minClientVersion;
|
|
65
|
-
this.recommendedVersion = details.recommendedVersion;
|
|
66
|
-
this.upgradeCommand = details.upgradeCommand;
|
|
67
|
-
this.upgradeUrl = details.upgradeUrl;
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
/**
|
|
71
|
-
* Module-level cache of the most recent server-recommended version.
|
|
72
|
-
* Read by `UpdateBanner` (via `getCachedServerRecommendation`) when
|
|
73
|
-
* computing the version it shows the operator. Kept in module scope
|
|
74
|
-
* (not class state) because both the engine client and the banner live
|
|
75
|
-
* in different module subtrees and there's no shared service container
|
|
76
|
-
* to thread DI through.
|
|
77
|
-
*/
|
|
78
|
-
let cachedServerRecommendation = null;
|
|
79
|
-
let cachedServerVersion = null;
|
|
80
|
-
/**
|
|
81
|
-
* UpdateBanner reads this to merge with its npm-registry poll. Returns
|
|
82
|
-
* `null` when no Pugi response has been seen yet (very first REPL
|
|
83
|
-
* launch before the engine has been called).
|
|
84
|
-
*/
|
|
85
|
-
export function getCachedServerRecommendation() {
|
|
86
|
-
return cachedServerRecommendation;
|
|
87
|
-
}
|
|
88
|
-
/**
|
|
89
|
-
* `pugi doctor --json` reads this for the server-version diagnostic
|
|
90
|
-
* field. Returns `null` when no response has been seen yet.
|
|
91
|
-
*/
|
|
92
|
-
export function getCachedServerVersion() {
|
|
93
|
-
return cachedServerVersion;
|
|
94
|
-
}
|
|
95
|
-
/**
|
|
96
|
-
* Test seam — reset the cache between specs so cross-test leakage
|
|
97
|
-
* doesn't make assertions flaky. Not part of the public CLI surface.
|
|
98
|
-
*/
|
|
99
|
-
export function __resetVersionCacheForTests() {
|
|
100
|
-
cachedServerRecommendation = null;
|
|
101
|
-
cachedServerVersion = null;
|
|
102
|
-
}
|
|
103
|
-
/**
|
|
104
|
-
* Mutate an outbound header bag to add the CLI version. Accepts both
|
|
105
|
-
* the plain-object header shape that `fetch` uses and Headers
|
|
106
|
-
* instances. Returns the same bag for chaining.
|
|
107
|
-
*
|
|
108
|
-
* Header names go in canonical capitalization since some
|
|
109
|
-
* test/intermediary tools (mitmproxy, charles) display the exact case
|
|
110
|
-
* the client sent rather than normalising to lowercase.
|
|
111
|
-
*/
|
|
112
|
-
export function injectClientVersionHeader(headers, cliVersion) {
|
|
113
|
-
if (headers instanceof Headers) {
|
|
114
|
-
headers.set(PUGI_CLI_VERSION_HEADER, cliVersion);
|
|
115
|
-
return headers;
|
|
116
|
-
}
|
|
117
|
-
headers[PUGI_CLI_VERSION_HEADER] = cliVersion;
|
|
118
|
-
return headers;
|
|
119
|
-
}
|
|
120
|
-
/**
|
|
121
|
-
* Inbound-header inspection. Reads `X-Pugi-Cli-Upgrade-Recommended`
|
|
122
|
-
* and `X-Pugi-Server-Version` from the response and updates the
|
|
123
|
-
* module-level cache. Caller passes a `headers.get(...)` shim so this
|
|
124
|
-
* helper stays decoupled from the concrete response type — `Response`,
|
|
125
|
-
* `undici.Dispatcher.ResponseData`, or a stub in tests.
|
|
126
|
-
*/
|
|
127
|
-
export function inspectVersionResponse(getHeader) {
|
|
128
|
-
const recommended = getHeader(PUGI_CLI_UPGRADE_RECOMMENDED_HEADER);
|
|
129
|
-
if (typeof recommended === 'string' && recommended.length > 0) {
|
|
130
|
-
cachedServerRecommendation = recommended;
|
|
131
|
-
}
|
|
132
|
-
const serverVersion = getHeader(PUGI_SERVER_VERSION_HEADER);
|
|
133
|
-
if (typeof serverVersion === 'string' && serverVersion.length > 0) {
|
|
134
|
-
cachedServerVersion = serverVersion;
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
/**
|
|
138
|
-
* If the response is HTTP 426, throw `PugiCliUpgradeRequiredError`.
|
|
139
|
-
* Otherwise return void. Caller is responsible for already having
|
|
140
|
-
* read the response body — pass it in so we can parse the
|
|
141
|
-
* documented JSON shape without consuming the stream a second time.
|
|
142
|
-
*
|
|
143
|
-
* `installedFallback` is used when the response body doesn't carry
|
|
144
|
-
* `installedVersion` (e.g. CDN-injected 426). Should be the CLI's own
|
|
145
|
-
* PUGI_CLI_VERSION constant.
|
|
146
|
-
*/
|
|
147
|
-
export function assertNotUpgradeRequired(status, bodyText, installedFallback) {
|
|
148
|
-
if (status !== 426)
|
|
149
|
-
return;
|
|
150
|
-
let parsed = {};
|
|
151
|
-
try {
|
|
152
|
-
parsed = JSON.parse(bodyText);
|
|
153
|
-
}
|
|
154
|
-
catch {
|
|
155
|
-
// Body wasn't JSON — fall back to constants below.
|
|
156
|
-
}
|
|
157
|
-
throw new PugiCliUpgradeRequiredError({
|
|
158
|
-
installedVersion: parsed.installedVersion ?? installedFallback,
|
|
159
|
-
minClientVersion: parsed.minClientVersion ?? 'unknown',
|
|
160
|
-
recommendedVersion: parsed.recommendedVersion ?? 'unknown',
|
|
161
|
-
upgradeCommand: parsed.upgradeCommand ?? 'npm i -g @pugi/cli@latest',
|
|
162
|
-
upgradeUrl: parsed.upgradeUrl ?? 'https://www.npmjs.com/package/@pugi/cli',
|
|
163
|
-
message: parsed.message,
|
|
164
|
-
});
|
|
165
|
-
}
|
|
166
|
-
//# sourceMappingURL=version-interceptor.js.map
|
package/dist/core/trust.js
DELETED
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
import { mkdirSync, readFileSync, writeFileSync, existsSync, realpathSync } from 'node:fs';
|
|
2
|
-
import { homedir } from 'node:os';
|
|
3
|
-
import { dirname, resolve } from 'node:path';
|
|
4
|
-
import { z } from 'zod';
|
|
5
|
-
/**
|
|
6
|
-
* Project trust gate for workspace-scoped configs.
|
|
7
|
-
*
|
|
8
|
-
* Hooks under `<workspaceRoot>/.pugi/hooks.json` are dangerous: they execute
|
|
9
|
-
* arbitrary shell commands on every tool event. A malicious repo could ship
|
|
10
|
-
* a `.pugi/hooks.json` that exfiltrates secrets the first time a user runs
|
|
11
|
-
* `pugi code` inside it.
|
|
12
|
-
*
|
|
13
|
-
* The gate prevents this: project hooks load ONLY when the workspace root
|
|
14
|
-
* is explicitly trusted by the user. The trust ledger lives in
|
|
15
|
-
* `~/.pugi/trusted-workspaces.json` and is keyed by absolute path.
|
|
16
|
-
*
|
|
17
|
-
* The user-level config at `~/.pugi/hooks.json` is always loaded (the user
|
|
18
|
-
* authored it themselves on their own machine). Only project-scoped hooks
|
|
19
|
-
* pass through the gate.
|
|
20
|
-
*
|
|
21
|
-
* will wire `pugi config trust .` as the user-facing entry point.
|
|
22
|
-
* For the trust ledger primitives ship so the hook registry can gate
|
|
23
|
-
* on them; manual trust during dev is documented as a one-line script
|
|
24
|
-
* (see `apps/pugi-cli/scripts/trust-workspace.ts`).
|
|
25
|
-
*/
|
|
26
|
-
const trustEntrySchema = z.object({
|
|
27
|
-
workspaceRoot: z.string().min(1),
|
|
28
|
-
trustedAt: z.string().datetime(),
|
|
29
|
-
trustedBy: z.string().min(1),
|
|
30
|
-
});
|
|
31
|
-
const trustLedgerSchema = z.object({
|
|
32
|
-
schema: z.number().int().positive().default(1),
|
|
33
|
-
entries: z.array(trustEntrySchema).default([]),
|
|
34
|
-
});
|
|
35
|
-
const TRUST_LEDGER_FILENAME = 'trusted-workspaces.json';
|
|
36
|
-
/**
|
|
37
|
-
* Resolve the trust ledger path. The PUGI_HOME env var lets tests redirect
|
|
38
|
-
* the user-home location without polluting the real `~/.pugi` directory.
|
|
39
|
-
*/
|
|
40
|
-
function trustLedgerPath() {
|
|
41
|
-
const home = process.env.PUGI_HOME ?? resolve(homedir(), '.pugi');
|
|
42
|
-
return resolve(home, TRUST_LEDGER_FILENAME);
|
|
43
|
-
}
|
|
44
|
-
function readLedger() {
|
|
45
|
-
const path = trustLedgerPath();
|
|
46
|
-
if (!existsSync(path)) {
|
|
47
|
-
return { schema: 1, entries: [] };
|
|
48
|
-
}
|
|
49
|
-
const raw = readFileSync(path, 'utf8');
|
|
50
|
-
if (raw.trim() === '') {
|
|
51
|
-
return { schema: 1, entries: [] };
|
|
52
|
-
}
|
|
53
|
-
const parsed = JSON.parse(raw);
|
|
54
|
-
return trustLedgerSchema.parse(parsed);
|
|
55
|
-
}
|
|
56
|
-
function writeLedger(ledger) {
|
|
57
|
-
const path = trustLedgerPath();
|
|
58
|
-
mkdirSync(dirname(path), { recursive: true });
|
|
59
|
-
// Mode 0o600 — only the owning user should read this file. The contents
|
|
60
|
-
// do not include secrets but they do reveal which directories on the
|
|
61
|
-
// user's disk are AI-agent workspaces.
|
|
62
|
-
writeFileSync(path, `${JSON.stringify(ledger, null, 2)}\n`, { encoding: 'utf8', mode: 0o600 });
|
|
63
|
-
}
|
|
64
|
-
function normaliseRoot(root) {
|
|
65
|
-
// Resolve to drop trailing slashes and `.` segments, then realpath so
|
|
66
|
-
// the canonical key is the underlying physical path. A user who trusts
|
|
67
|
-
// `~/projects/foo` (a symlink) trusts whatever foo points to AT TRUST
|
|
68
|
-
// TIME — `readFileSync` follows symlinks anyway, so the previous
|
|
69
|
-
// policy of storing the unresolved path created a TOCTOU window: an
|
|
70
|
-
// attacker who controlled the symlink target after grant could swap
|
|
71
|
-
// it and have their `.pugi/hooks.json` executed on the next pugi run.
|
|
72
|
-
// Storing the realpath closes that window. If the path does not
|
|
73
|
-
// exist yet (e.g. trusting a workspace that will be created later),
|
|
74
|
-
// fall back to the resolved-but-unrealpath'd form.
|
|
75
|
-
const resolved = resolve(root);
|
|
76
|
-
try {
|
|
77
|
-
return realpathSync(resolved);
|
|
78
|
-
}
|
|
79
|
-
catch {
|
|
80
|
-
return resolved;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
export async function isTrustedWorkspace(root) {
|
|
84
|
-
const key = normaliseRoot(root);
|
|
85
|
-
const ledger = readLedger();
|
|
86
|
-
return ledger.entries.some((entry) => entry.workspaceRoot === key);
|
|
87
|
-
}
|
|
88
|
-
export async function trustWorkspace(root, by) {
|
|
89
|
-
const key = normaliseRoot(root);
|
|
90
|
-
const ledger = readLedger();
|
|
91
|
-
const filtered = ledger.entries.filter((entry) => entry.workspaceRoot !== key);
|
|
92
|
-
filtered.push({
|
|
93
|
-
workspaceRoot: key,
|
|
94
|
-
trustedAt: new Date().toISOString(),
|
|
95
|
-
trustedBy: by,
|
|
96
|
-
});
|
|
97
|
-
writeLedger({ schema: ledger.schema, entries: filtered });
|
|
98
|
-
}
|
|
99
|
-
export async function revokeTrust(root) {
|
|
100
|
-
const key = normaliseRoot(root);
|
|
101
|
-
const ledger = readLedger();
|
|
102
|
-
const filtered = ledger.entries.filter((entry) => entry.workspaceRoot !== key);
|
|
103
|
-
writeLedger({ schema: ledger.schema, entries: filtered });
|
|
104
|
-
}
|
|
105
|
-
export async function listTrusted() {
|
|
106
|
-
const ledger = readLedger();
|
|
107
|
-
return [...ledger.entries];
|
|
108
|
-
}
|
|
109
|
-
//# sourceMappingURL=trust.js.map
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
const DEFAULT_PREVIEW_BYTES = 200;
|
|
2
|
-
const previewBytesByBlock = new WeakMap();
|
|
3
|
-
function normalizePreviewBytes(value) {
|
|
4
|
-
if (value === undefined)
|
|
5
|
-
return DEFAULT_PREVIEW_BYTES;
|
|
6
|
-
if (!Number.isFinite(value))
|
|
7
|
-
return DEFAULT_PREVIEW_BYTES;
|
|
8
|
-
return Math.max(0, Math.trunc(value));
|
|
9
|
-
}
|
|
10
|
-
function shownBytes(content, collapsed, previewBytes) {
|
|
11
|
-
return collapsed ? Math.min(previewBytes, content.length) : content.length;
|
|
12
|
-
}
|
|
13
|
-
function captureSignature(attrs) {
|
|
14
|
-
const match = /\bsignature\s*=\s*(["'])(.*?)\1/i.exec(attrs);
|
|
15
|
-
return match?.[2];
|
|
16
|
-
}
|
|
17
|
-
function withPreviewBytes(block, previewBytes) {
|
|
18
|
-
previewBytesByBlock.set(block, previewBytes);
|
|
19
|
-
return block;
|
|
20
|
-
}
|
|
21
|
-
export function parseThinkingBlocks(text, opts = {}) {
|
|
22
|
-
const collapsed = opts.collapseDefault ?? true;
|
|
23
|
-
const previewBytes = normalizePreviewBytes(opts.previewBytes);
|
|
24
|
-
const blocks = [];
|
|
25
|
-
const pattern = /<thinking\b([^>]*)>([\s\S]*?)<\/thinking>/gi;
|
|
26
|
-
let match;
|
|
27
|
-
while ((match = pattern.exec(text)) !== null) {
|
|
28
|
-
const attrs = match[1] ?? '';
|
|
29
|
-
const content = match[2] ?? '';
|
|
30
|
-
const signature = captureSignature(attrs);
|
|
31
|
-
const block = {
|
|
32
|
-
id: `think-${blocks.length + 1}`,
|
|
33
|
-
content,
|
|
34
|
-
collapsed,
|
|
35
|
-
bytesShown: shownBytes(content, collapsed, previewBytes),
|
|
36
|
-
};
|
|
37
|
-
if (signature !== undefined) {
|
|
38
|
-
block.signature = signature;
|
|
39
|
-
}
|
|
40
|
-
blocks.push(withPreviewBytes(block, previewBytes));
|
|
41
|
-
}
|
|
42
|
-
return blocks;
|
|
43
|
-
}
|
|
44
|
-
export function toggleBlock(block) {
|
|
45
|
-
const collapsed = !block.collapsed;
|
|
46
|
-
const previewBytes = previewBytesByBlock.get(block) ?? DEFAULT_PREVIEW_BYTES;
|
|
47
|
-
return withPreviewBytes({
|
|
48
|
-
...block,
|
|
49
|
-
collapsed,
|
|
50
|
-
bytesShown: shownBytes(block.content, collapsed, previewBytes),
|
|
51
|
-
}, previewBytes);
|
|
52
|
-
}
|
|
53
|
-
export function renderBlocks(blocks) {
|
|
54
|
-
return blocks
|
|
55
|
-
.map((block) => {
|
|
56
|
-
if (!block.collapsed) {
|
|
57
|
-
return `[-] ${block.id}:\n${block.content}`;
|
|
58
|
-
}
|
|
59
|
-
const preview = block.content.slice(0, block.bytesShown);
|
|
60
|
-
return `[+] ${block.id}: ${preview}…`;
|
|
61
|
-
})
|
|
62
|
-
.join('\n');
|
|
63
|
-
}
|
|
64
|
-
//# sourceMappingURL=thinking-block.js.map
|
package/dist/core/vim/keymap.js
DELETED
|
@@ -1,288 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* — Vim-style modal editing keymap.
|
|
3
|
-
*
|
|
4
|
-
* Pure, data-only motion + edit primitives. The TUI layer
|
|
5
|
-
* (`tui/vim-input.tsx`) maps Ink key events onto these helpers so the
|
|
6
|
-
* keymap stays trivially testable and the React component stays free
|
|
7
|
-
* of motion arithmetic.
|
|
8
|
-
*
|
|
9
|
-
* Scope (matches the upstream behavior for the upstream tool `/vim`):
|
|
10
|
-
*
|
|
11
|
-
* Normal-mode motions
|
|
12
|
-
* h / j / k / l → left / down / up / right (j/k are
|
|
13
|
-
* no-ops on a single-line buffer; we keep
|
|
14
|
-
* the kind in the discriminator so the
|
|
15
|
-
* multi-line buffer follow-up can extend
|
|
16
|
-
* without churning the call sites).
|
|
17
|
-
* 0 / $ → line start / line end
|
|
18
|
-
* w / b → word forward / word backward
|
|
19
|
-
*
|
|
20
|
-
* Normal-mode edits
|
|
21
|
-
* x → delete the char under the cursor
|
|
22
|
-
* dd → delete the entire line (clears the buffer
|
|
23
|
-
* for the single-line REPL prompt)
|
|
24
|
-
* i / a → enter insert mode (`a` advances cursor by
|
|
25
|
-
* one column first, capped at line length)
|
|
26
|
-
*
|
|
27
|
-
* Ex-line commands (typed after `:` in normal mode)
|
|
28
|
-
* :w → submit current buffer (same as Enter in
|
|
29
|
-
* insert mode)
|
|
30
|
-
* :q → clear buffer + return to prompt (no
|
|
31
|
-
* submission)
|
|
32
|
-
*
|
|
33
|
-
* Mode transitions
|
|
34
|
-
* Esc → return to normal mode
|
|
35
|
-
* i / a in normal → insert mode
|
|
36
|
-
*
|
|
37
|
-
* The motion helpers return both the new cursor position AND a
|
|
38
|
-
* structured discriminator so the TUI layer can react (e.g. animate
|
|
39
|
-
* the caret, log an undo entry, etc.) without re-parsing the key.
|
|
40
|
-
*
|
|
41
|
-
* The keymap is intentionally NOT a full vim implementation. We model
|
|
42
|
-
* just the subset that maps cleanly onto a single-line input buffer +
|
|
43
|
-
* the four REPL verbs (submit / cancel / accept-insert / leave-insert).
|
|
44
|
-
* Counts (`3dd`, `5j`), registers, marks, search (`/pattern`), visual
|
|
45
|
-
* mode, undo/redo, and macros are explicitly out of scope and left
|
|
46
|
-
* for a follow-up sprint once the single-line surface is shipped.
|
|
47
|
-
*/
|
|
48
|
-
export const PENDING_NONE = { kind: 'none' };
|
|
49
|
-
/**
|
|
50
|
-
* Whitespace classifier used by `w` / `b`. Matches vim's default
|
|
51
|
-
* "word" classification loosely — treat any non-whitespace, non-
|
|
52
|
-
* punctuation byte as a word character; collapse runs.
|
|
53
|
-
*
|
|
54
|
-
* We DELIBERATELY do not split on punctuation the way vim's `w`/`b`
|
|
55
|
-
* do (`hello-world` is two words in vim but one here). The REPL
|
|
56
|
-
* brief is prose-heavy and operators expect `w` to skip whole tokens
|
|
57
|
-
* — splitting on punctuation surprised every internal tester.
|
|
58
|
-
*/
|
|
59
|
-
function isWordChar(ch) {
|
|
60
|
-
return /[^\s]/.test(ch);
|
|
61
|
-
}
|
|
62
|
-
/**
|
|
63
|
-
* Move cursor forward by one word: skip the current run of word
|
|
64
|
-
* chars, then skip the run of whitespace, land on the first char of
|
|
65
|
-
* the next word. Clamps at `line.length` so end-of-line is safe.
|
|
66
|
-
*/
|
|
67
|
-
export function wordForward(line, cursor) {
|
|
68
|
-
if (cursor >= line.length)
|
|
69
|
-
return line.length;
|
|
70
|
-
let i = cursor;
|
|
71
|
-
// Skip current word chars (if we're on one).
|
|
72
|
-
while (i < line.length && isWordChar(line[i] ?? ''))
|
|
73
|
-
i++;
|
|
74
|
-
// Skip whitespace gap.
|
|
75
|
-
while (i < line.length && !isWordChar(line[i] ?? ''))
|
|
76
|
-
i++;
|
|
77
|
-
return i;
|
|
78
|
-
}
|
|
79
|
-
/**
|
|
80
|
-
* Move cursor backward by one word: step back over whitespace, then
|
|
81
|
-
* step back over the previous word. Clamps at 0.
|
|
82
|
-
*/
|
|
83
|
-
export function wordBackward(line, cursor) {
|
|
84
|
-
if (cursor <= 0)
|
|
85
|
-
return 0;
|
|
86
|
-
let i = cursor - 1;
|
|
87
|
-
// Skip whitespace behind cursor.
|
|
88
|
-
while (i > 0 && !isWordChar(line[i] ?? ''))
|
|
89
|
-
i--;
|
|
90
|
-
// Skip the word until we hit whitespace or start.
|
|
91
|
-
while (i > 0 && isWordChar(line[i - 1] ?? ''))
|
|
92
|
-
i--;
|
|
93
|
-
return i;
|
|
94
|
-
}
|
|
95
|
-
/**
|
|
96
|
-
* Single-character delete under the cursor (`x`). When the cursor sits
|
|
97
|
-
* past end-of-line (legal in insert mode) we no-op.
|
|
98
|
-
*
|
|
99
|
-
* The cursor stays at the same offset after the splice unless the
|
|
100
|
-
* deleted char was the trailing one — in which case we step back so
|
|
101
|
-
* the caret does not sit past EOL.
|
|
102
|
-
*/
|
|
103
|
-
function deleteCharUnder(line, cursor) {
|
|
104
|
-
if (cursor < 0 || cursor >= line.length)
|
|
105
|
-
return { line, cursor };
|
|
106
|
-
const next = line.slice(0, cursor) + line.slice(cursor + 1);
|
|
107
|
-
const nextCursor = cursor >= next.length ? Math.max(0, next.length - (next.length === 0 ? 0 : 1)) : cursor;
|
|
108
|
-
// Subtle: when the deletion empties the buffer we want cursor=0,
|
|
109
|
-
// NOT length-1=−1. The Math.max above guards it but spell it out.
|
|
110
|
-
return { line: next, cursor: next.length === 0 ? 0 : nextCursor };
|
|
111
|
-
}
|
|
112
|
-
/**
|
|
113
|
-
* Apply a normal-mode keystroke and return the new buffer + cursor +
|
|
114
|
-
* mode + pending state. Pure — no side effects. The TUI is the only
|
|
115
|
-
* caller; tests exercise this directly.
|
|
116
|
-
*/
|
|
117
|
-
export function handleNormalKey(input) {
|
|
118
|
-
const { line, cursor, pending, ch } = input;
|
|
119
|
-
// Esc resets any in-flight multi-key sequence and re-asserts normal
|
|
120
|
-
// mode (we stay in normal — the caller invoked us BECAUSE we are in
|
|
121
|
-
// normal, but a stale `pending=d` should not survive an Esc).
|
|
122
|
-
if (input.escape) {
|
|
123
|
-
return {
|
|
124
|
-
result: { kind: 'noop', cursor, line, mode: 'normal' },
|
|
125
|
-
pending: PENDING_NONE,
|
|
126
|
-
};
|
|
127
|
-
}
|
|
128
|
-
// ─── Ex-line composition ───────────────────────────────────────
|
|
129
|
-
// Once `:` has been pressed we accumulate keystrokes into the
|
|
130
|
-
// pending buffer until Enter (commit) or Esc (cancel, handled
|
|
131
|
-
// above). Backspace shortens the buffer; any other char extends.
|
|
132
|
-
if (pending.kind === 'ex') {
|
|
133
|
-
if (input.enter) {
|
|
134
|
-
const cmd = pending.buffer;
|
|
135
|
-
if (cmd === 'w') {
|
|
136
|
-
return {
|
|
137
|
-
result: { kind: 'submit', cursor, line, mode: 'normal', payload: line },
|
|
138
|
-
pending: PENDING_NONE,
|
|
139
|
-
};
|
|
140
|
-
}
|
|
141
|
-
if (cmd === 'q') {
|
|
142
|
-
return {
|
|
143
|
-
result: { kind: 'cancel', cursor: 0, line: '', mode: 'normal' },
|
|
144
|
-
pending: PENDING_NONE,
|
|
145
|
-
};
|
|
146
|
-
}
|
|
147
|
-
// Unknown ex command — drop silently. The TUI may surface a
|
|
148
|
-
// dim hint but the keymap stays narrow (only `:w` / `:q` are
|
|
149
|
-
// contractual today).
|
|
150
|
-
return {
|
|
151
|
-
result: { kind: 'noop', cursor, line, mode: 'normal' },
|
|
152
|
-
pending: PENDING_NONE,
|
|
153
|
-
};
|
|
154
|
-
}
|
|
155
|
-
if (input.backspace) {
|
|
156
|
-
const nextBuf = pending.buffer.slice(0, -1);
|
|
157
|
-
if (nextBuf.length === 0) {
|
|
158
|
-
// Operator backspaced past `:` — exit ex composition.
|
|
159
|
-
return {
|
|
160
|
-
result: { kind: 'noop', cursor, line, mode: 'normal' },
|
|
161
|
-
pending: PENDING_NONE,
|
|
162
|
-
};
|
|
163
|
-
}
|
|
164
|
-
return {
|
|
165
|
-
result: { kind: 'noop', cursor, line, mode: 'normal' },
|
|
166
|
-
pending: { kind: 'ex', buffer: nextBuf },
|
|
167
|
-
};
|
|
168
|
-
}
|
|
169
|
-
if (ch.length > 0) {
|
|
170
|
-
return {
|
|
171
|
-
result: { kind: 'noop', cursor, line, mode: 'normal' },
|
|
172
|
-
pending: { kind: 'ex', buffer: pending.buffer + ch },
|
|
173
|
-
};
|
|
174
|
-
}
|
|
175
|
-
return {
|
|
176
|
-
result: { kind: 'noop', cursor, line, mode: 'normal' },
|
|
177
|
-
pending,
|
|
178
|
-
};
|
|
179
|
-
}
|
|
180
|
-
// ─── Single-shot bindings ──────────────────────────────────────
|
|
181
|
-
switch (ch) {
|
|
182
|
-
case 'i':
|
|
183
|
-
return {
|
|
184
|
-
result: { kind: 'mode', cursor, line, mode: 'insert' },
|
|
185
|
-
pending: PENDING_NONE,
|
|
186
|
-
};
|
|
187
|
-
case 'a': {
|
|
188
|
-
// `a` = append: advance cursor by one (capped) then enter
|
|
189
|
-
// insert. Matches vim semantics for the single-line case.
|
|
190
|
-
const next = Math.min(line.length, cursor + 1);
|
|
191
|
-
return {
|
|
192
|
-
result: { kind: 'mode', cursor: next, line, mode: 'insert' },
|
|
193
|
-
pending: PENDING_NONE,
|
|
194
|
-
};
|
|
195
|
-
}
|
|
196
|
-
case 'h':
|
|
197
|
-
return {
|
|
198
|
-
result: { kind: 'move', cursor: Math.max(0, cursor - 1), line, mode: 'normal' },
|
|
199
|
-
pending: PENDING_NONE,
|
|
200
|
-
};
|
|
201
|
-
case 'l':
|
|
202
|
-
return {
|
|
203
|
-
result: { kind: 'move', cursor: Math.min(line.length, cursor + 1), line, mode: 'normal' },
|
|
204
|
-
pending: PENDING_NONE,
|
|
205
|
-
};
|
|
206
|
-
case 'j':
|
|
207
|
-
case 'k':
|
|
208
|
-
// Single-line buffer — j/k are inert today. Returning a `move`
|
|
209
|
-
// keeps the discriminator consistent; the multi-line follow-up
|
|
210
|
-
// will compute a real new offset here.
|
|
211
|
-
return {
|
|
212
|
-
result: { kind: 'move', cursor, line, mode: 'normal' },
|
|
213
|
-
pending: PENDING_NONE,
|
|
214
|
-
};
|
|
215
|
-
case '0':
|
|
216
|
-
return {
|
|
217
|
-
result: { kind: 'move', cursor: 0, line, mode: 'normal' },
|
|
218
|
-
pending: PENDING_NONE,
|
|
219
|
-
};
|
|
220
|
-
case '$':
|
|
221
|
-
return {
|
|
222
|
-
result: { kind: 'move', cursor: line.length, line, mode: 'normal' },
|
|
223
|
-
pending: PENDING_NONE,
|
|
224
|
-
};
|
|
225
|
-
case 'w':
|
|
226
|
-
return {
|
|
227
|
-
result: { kind: 'move', cursor: wordForward(line, cursor), line, mode: 'normal' },
|
|
228
|
-
pending: PENDING_NONE,
|
|
229
|
-
};
|
|
230
|
-
case 'b':
|
|
231
|
-
return {
|
|
232
|
-
result: { kind: 'move', cursor: wordBackward(line, cursor), line, mode: 'normal' },
|
|
233
|
-
pending: PENDING_NONE,
|
|
234
|
-
};
|
|
235
|
-
case 'x': {
|
|
236
|
-
const { line: nextLine, cursor: nextCursor } = deleteCharUnder(line, cursor);
|
|
237
|
-
return {
|
|
238
|
-
result: { kind: 'edit', cursor: nextCursor, line: nextLine, mode: 'normal' },
|
|
239
|
-
pending: PENDING_NONE,
|
|
240
|
-
};
|
|
241
|
-
}
|
|
242
|
-
case 'd': {
|
|
243
|
-
// First `d` arms the dd sequence; second `d` commits the line
|
|
244
|
-
// delete. Any other key intervenes the sequence collapses
|
|
245
|
-
// (handled below in the fall-through path).
|
|
246
|
-
if (pending.kind === 'd') {
|
|
247
|
-
return {
|
|
248
|
-
result: { kind: 'edit', cursor: 0, line: '', mode: 'normal' },
|
|
249
|
-
pending: PENDING_NONE,
|
|
250
|
-
};
|
|
251
|
-
}
|
|
252
|
-
return {
|
|
253
|
-
result: { kind: 'noop', cursor, line, mode: 'normal' },
|
|
254
|
-
pending: { kind: 'd' },
|
|
255
|
-
};
|
|
256
|
-
}
|
|
257
|
-
case ':':
|
|
258
|
-
return {
|
|
259
|
-
result: { kind: 'noop', cursor, line, mode: 'normal' },
|
|
260
|
-
pending: { kind: 'ex', buffer: '' },
|
|
261
|
-
};
|
|
262
|
-
default:
|
|
263
|
-
// Unknown binding in normal mode — drop the keystroke + reset
|
|
264
|
-
// any pending sequence so a stale `d` does not silently arm a
|
|
265
|
-
// future delete after the operator wandered through unbound
|
|
266
|
-
// keys.
|
|
267
|
-
return {
|
|
268
|
-
result: { kind: 'noop', cursor, line, mode: 'normal' },
|
|
269
|
-
pending: PENDING_NONE,
|
|
270
|
-
};
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
/**
|
|
274
|
-
* Tiny helper exposed for the TUI so the banner / status line can
|
|
275
|
-
* render the active pending state without re-implementing the
|
|
276
|
-
* discriminator switch.
|
|
277
|
-
*/
|
|
278
|
-
export function describePending(pending) {
|
|
279
|
-
switch (pending.kind) {
|
|
280
|
-
case 'none':
|
|
281
|
-
return '';
|
|
282
|
-
case 'd':
|
|
283
|
-
return 'd';
|
|
284
|
-
case 'ex':
|
|
285
|
-
return `:${pending.buffer}`;
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
//# sourceMappingURL=keymap.js.map
|