@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
package/dist/core/permission.js
DELETED
|
@@ -1,369 +0,0 @@
|
|
|
1
|
-
import { basename, resolve } from 'node:path';
|
|
2
|
-
import { classifyBash, listDestructivePatterns, } from './bash-classifier.js';
|
|
3
|
-
/**
|
|
4
|
-
* Map `PermissionAction.kind` to the `HookMatch.permission` taxonomy.
|
|
5
|
-
* The hook taxonomy uses `mcp`/`subagent` slots that the permission
|
|
6
|
-
* engine does not currently emit; `workflow` has no hook-side
|
|
7
|
-
* equivalent so it falls through as undefined (any matching hook with
|
|
8
|
-
* an explicit `permission` filter will not match).
|
|
9
|
-
*/
|
|
10
|
-
function toHookPermission(kind) {
|
|
11
|
-
switch (kind) {
|
|
12
|
-
case 'read':
|
|
13
|
-
case 'edit':
|
|
14
|
-
case 'bash':
|
|
15
|
-
case 'network':
|
|
16
|
-
case 'mcp':
|
|
17
|
-
return kind;
|
|
18
|
-
case 'workflow':
|
|
19
|
-
return undefined;
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
/**
|
|
23
|
-
* Fire the `PermissionRequest` hook for the given action and decision.
|
|
24
|
-
* Caller is the permission engine's user-facing surface — when the
|
|
25
|
-
* decision is `ask`, hooks can observe (and, with `onFailure: 'block'`,
|
|
26
|
-
* pre-empt) the prompt. The hook system does NOT replace the prompt —
|
|
27
|
-
* a `block` hook simply turns the decision into a refusal that the
|
|
28
|
-
* caller surfaces as a denial.
|
|
29
|
-
*
|
|
30
|
-
* Returns true when no blocking hook objected; false when at least one
|
|
31
|
-
* `onFailure: 'block'` hook returned a non-zero exit. Callers should
|
|
32
|
-
* treat false as "deny this action".
|
|
33
|
-
*/
|
|
34
|
-
export async function firePermissionRequestHook(action, decision, hooks, sessionId) {
|
|
35
|
-
hooks.resetBatch();
|
|
36
|
-
const ctx = {
|
|
37
|
-
sessionId,
|
|
38
|
-
event: 'PermissionRequest',
|
|
39
|
-
tool: action.tool,
|
|
40
|
-
permission: toHookPermission(action.kind),
|
|
41
|
-
path: action.kind === 'read' || action.kind === 'edit' ? action.target : undefined,
|
|
42
|
-
payload: { action, decision },
|
|
43
|
-
};
|
|
44
|
-
const matching = hooks.listMatching(ctx);
|
|
45
|
-
const results = await hooks.fire(ctx);
|
|
46
|
-
for (let i = 0; i < matching.length; i += 1) {
|
|
47
|
-
const hook = matching[i];
|
|
48
|
-
const result = results[i];
|
|
49
|
-
if (hook && result && hook.onFailure === 'block' && !result.ok) {
|
|
50
|
-
return false;
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
return true;
|
|
54
|
-
}
|
|
55
|
-
const protectedBasenames = new Set([
|
|
56
|
-
'.env',
|
|
57
|
-
'.npmrc',
|
|
58
|
-
'.yarnrc',
|
|
59
|
-
'.pypirc',
|
|
60
|
-
'.gitconfig',
|
|
61
|
-
'id_rsa',
|
|
62
|
-
'id_ed25519',
|
|
63
|
-
]);
|
|
64
|
-
const protectedSuffixes = ['.pem', '.key', '.crt', '.p12', '.dump', '.sql'];
|
|
65
|
-
/**
|
|
66
|
-
* Hard-deny list. The list of destructive substrings now lives in
|
|
67
|
-
* `bash-classifier.ts::DESTRUCTIVE_PATTERNS`; this function exposes
|
|
68
|
-
* it as a list for callers (doctor, debug tooling) that need to
|
|
69
|
-
* audit the rule set without re-running `classifyBash`.
|
|
70
|
-
*
|
|
71
|
-
* Code Reviewer P2 retro: previously this list was
|
|
72
|
-
* duplicated here as `destructiveBashPatterns`. Sprint moves it
|
|
73
|
-
* into the classifier so the permission engine and the doctor surface
|
|
74
|
-
* cannot drift.
|
|
75
|
-
*/
|
|
76
|
-
export function destructiveBashPatternsList() {
|
|
77
|
-
return listDestructivePatterns();
|
|
78
|
-
}
|
|
79
|
-
/**
|
|
80
|
-
* Class-aware bash permission decision. The matrix:
|
|
81
|
-
*
|
|
82
|
-
* | plan | ask | acceptEdits | auto | dontAsk | bypass
|
|
83
|
-
* read | allow| allow| allow | allow | allow | allow
|
|
84
|
-
* build_test | deny | ask | ask | allow | allow* | allow
|
|
85
|
-
* network | deny | ask | ask | ask | allow* | allow
|
|
86
|
-
* write_workspace | deny | ask | allow | allow | allow* | allow
|
|
87
|
-
* write_protected | deny | ask | ask | ask | deny | ask
|
|
88
|
-
* destructive | deny | deny | deny | deny | deny | deny**
|
|
89
|
-
* unknown | deny | ask | ask | ask | deny | ask
|
|
90
|
-
*
|
|
91
|
-
* * dontAsk allows non-destructive classes when no settings rule
|
|
92
|
-
* contradicts; the bare-mode policy is encoded in the table below.
|
|
93
|
-
* ** destructive can be unlocked ONLY when ALL three hold:
|
|
94
|
-
* - mode === 'bypassPermissions'
|
|
95
|
-
* - PUGI_DESTRUCTIVE_OVERRIDE === '1'
|
|
96
|
-
* - source === 'human'
|
|
97
|
-
* The agent loop never sets `source: 'human'`, so even a runaway
|
|
98
|
-
* agent in bypass mode cannot trigger a destructive deletion.
|
|
99
|
-
*/
|
|
100
|
-
export function evaluateBashPermission(cmd, mode, ctx) {
|
|
101
|
-
const classification = classifyBash(cmd, {
|
|
102
|
-
workspaceRoot: ctx.workspaceRoot,
|
|
103
|
-
additionalDirectories: ctx.additionalDirectories,
|
|
104
|
-
});
|
|
105
|
-
return decisionForClass(classification, mode, ctx);
|
|
106
|
-
}
|
|
107
|
-
function decisionForClass(classification, mode, ctx) {
|
|
108
|
-
const { class: klass, reason, matched } = classification;
|
|
109
|
-
const explain = `${reason}${matched ? ` [matched=${matched}]` : ''}`;
|
|
110
|
-
if (klass === 'destructive') {
|
|
111
|
-
const overrideOk = mode === 'bypassPermissions' &&
|
|
112
|
-
ctx.source === 'human' &&
|
|
113
|
-
process.env.PUGI_DESTRUCTIVE_OVERRIDE === '1';
|
|
114
|
-
if (overrideOk) {
|
|
115
|
-
return {
|
|
116
|
-
decision: 'allow',
|
|
117
|
-
reason: `${explain}; destructive override engaged (PUGI_DESTRUCTIVE_OVERRIDE=1, human source, bypassPermissions)`,
|
|
118
|
-
source: 'mode.bypassPermissions.destructive_override',
|
|
119
|
-
};
|
|
120
|
-
}
|
|
121
|
-
return {
|
|
122
|
-
decision: 'deny',
|
|
123
|
-
reason: `${explain}; destructive class is always denied`,
|
|
124
|
-
source: 'classifier.destructive',
|
|
125
|
-
};
|
|
126
|
-
}
|
|
127
|
-
switch (klass) {
|
|
128
|
-
case 'read':
|
|
129
|
-
return { decision: 'allow', reason: explain, source: `classifier.${klass}` };
|
|
130
|
-
case 'build_test':
|
|
131
|
-
return classAwareVerdict(mode, klass, explain, {
|
|
132
|
-
plan: 'deny',
|
|
133
|
-
ask: 'ask',
|
|
134
|
-
acceptEdits: 'ask',
|
|
135
|
-
auto: 'allow',
|
|
136
|
-
dontAsk: 'allow',
|
|
137
|
-
bypassPermissions: 'allow',
|
|
138
|
-
});
|
|
139
|
-
case 'network':
|
|
140
|
-
return classAwareVerdict(mode, klass, explain, {
|
|
141
|
-
plan: 'deny',
|
|
142
|
-
ask: 'ask',
|
|
143
|
-
acceptEdits: 'ask',
|
|
144
|
-
auto: 'ask',
|
|
145
|
-
dontAsk: 'allow',
|
|
146
|
-
bypassPermissions: 'allow',
|
|
147
|
-
});
|
|
148
|
-
case 'write_workspace':
|
|
149
|
-
return classAwareVerdict(mode, klass, explain, {
|
|
150
|
-
plan: 'deny',
|
|
151
|
-
ask: 'ask',
|
|
152
|
-
acceptEdits: 'allow',
|
|
153
|
-
auto: 'allow',
|
|
154
|
-
dontAsk: 'allow',
|
|
155
|
-
bypassPermissions: 'allow',
|
|
156
|
-
});
|
|
157
|
-
case 'write_protected':
|
|
158
|
-
return classAwareVerdict(mode, klass, explain, {
|
|
159
|
-
plan: 'deny',
|
|
160
|
-
ask: 'ask',
|
|
161
|
-
acceptEdits: 'ask',
|
|
162
|
-
auto: 'ask',
|
|
163
|
-
dontAsk: 'deny',
|
|
164
|
-
bypassPermissions: 'ask',
|
|
165
|
-
});
|
|
166
|
-
case 'unknown':
|
|
167
|
-
return classAwareVerdict(mode, klass, explain, {
|
|
168
|
-
plan: 'deny',
|
|
169
|
-
ask: 'ask',
|
|
170
|
-
acceptEdits: 'ask',
|
|
171
|
-
auto: 'ask',
|
|
172
|
-
dontAsk: 'deny',
|
|
173
|
-
bypassPermissions: 'ask',
|
|
174
|
-
});
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
function classAwareVerdict(mode, klass, reason, table) {
|
|
178
|
-
const verdict = table[mode];
|
|
179
|
-
const source = `classifier.${klass}.${mode}`;
|
|
180
|
-
switch (verdict) {
|
|
181
|
-
case 'allow':
|
|
182
|
-
return { decision: 'allow', reason, source };
|
|
183
|
-
case 'deny':
|
|
184
|
-
return { decision: 'deny', reason: `${reason}; ${mode} mode denies ${klass}`, source };
|
|
185
|
-
case 'ask':
|
|
186
|
-
return { decision: 'ask', reason, risk: riskForClass(klass) };
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
function riskForClass(klass) {
|
|
190
|
-
switch (klass) {
|
|
191
|
-
case 'read':
|
|
192
|
-
return 'low';
|
|
193
|
-
case 'build_test':
|
|
194
|
-
case 'write_workspace':
|
|
195
|
-
return 'medium';
|
|
196
|
-
case 'network':
|
|
197
|
-
case 'write_protected':
|
|
198
|
-
case 'unknown':
|
|
199
|
-
case 'destructive':
|
|
200
|
-
return 'high';
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
export function decidePermission(action, settings, root) {
|
|
204
|
-
if (action.kind === 'bash') {
|
|
205
|
-
// Legacy callers (file-tools::bashTool, runtime/cli protected-file
|
|
206
|
-
// probe) still call `decidePermission` for bash. The class-aware
|
|
207
|
-
// engine is preferred; we route through it here so the hard-deny
|
|
208
|
-
// gate stays consistent regardless of entry point.
|
|
209
|
-
//
|
|
210
|
-
// Source defaults to 'agent' because every code path that calls
|
|
211
|
-
// `decidePermission({ kind: 'bash' })` today is reached through
|
|
212
|
-
// the engine loop. Direct human bash invocation goes through the
|
|
213
|
-
// new `evaluateBashPermission` with `source: 'human'`.
|
|
214
|
-
const decision = evaluateBashPermission(action.target, settings.permissions.mode, {
|
|
215
|
-
workspaceRoot: root,
|
|
216
|
-
additionalDirectories: [],
|
|
217
|
-
source: 'agent',
|
|
218
|
-
});
|
|
219
|
-
if (decision.decision === 'deny' && decision.source === 'classifier.destructive') {
|
|
220
|
-
// Locked-source identifier for the retro spec — the existing
|
|
221
|
-
// tests assert `source === 'hard_deny'`. Re-label so the
|
|
222
|
-
// semantic stays the same: a destructive command was blocked
|
|
223
|
-
// by the hard-deny gate.
|
|
224
|
-
return { ...decision, source: 'hard_deny' };
|
|
225
|
-
}
|
|
226
|
-
const signature = `${action.kind}:${action.target}`;
|
|
227
|
-
if (matchesAny(signature, settings.permissions.deny)) {
|
|
228
|
-
return { decision: 'deny', reason: `Denied by rule: ${signature}`, source: 'settings.deny' };
|
|
229
|
-
}
|
|
230
|
-
if (matchesAny(signature, settings.permissions.allow) && decision.decision !== 'deny') {
|
|
231
|
-
return { decision: 'allow', reason: `Allowed by rule: ${signature}`, source: 'settings.allow' };
|
|
232
|
-
}
|
|
233
|
-
return decision;
|
|
234
|
-
}
|
|
235
|
-
const protectedReason = protectedTargetReason(action, root);
|
|
236
|
-
if (protectedReason) {
|
|
237
|
-
return decisionForMode(settings.permissions.mode, protectedReason, 'protected_file', 'high');
|
|
238
|
-
}
|
|
239
|
-
// task — operator-declared read-only paths gate edits
|
|
240
|
-
// and writes ahead of the generic deny check so the audit trail
|
|
241
|
-
// shows `source: 'readonly_paths'` (operator intent), not
|
|
242
|
-
// `settings.deny` (generic rule). Reads always pass through; the
|
|
243
|
-
// contract is "you can look but не touch".
|
|
244
|
-
if (action.kind === 'edit' && matchesAny(action.target, settings.permissions.readonlyPaths)) {
|
|
245
|
-
return {
|
|
246
|
-
decision: 'deny',
|
|
247
|
-
reason: `Read-only path: ${action.target}`,
|
|
248
|
-
source: 'readonly_paths',
|
|
249
|
-
};
|
|
250
|
-
}
|
|
251
|
-
const signature = `${action.kind}:${action.target}`;
|
|
252
|
-
if (matchesAny(signature, settings.permissions.deny)) {
|
|
253
|
-
return { decision: 'deny', reason: `Denied by rule: ${signature}`, source: 'settings.deny' };
|
|
254
|
-
}
|
|
255
|
-
if (matchesAny(signature, settings.permissions.notAutomatic) || matchesAny(signature, settings.workflow.notAutomatic)) {
|
|
256
|
-
return { decision: 'ask', reason: `Marked not automatic: ${signature}`, risk: 'medium' };
|
|
257
|
-
}
|
|
258
|
-
if (matchesAny(signature, settings.permissions.allow)) {
|
|
259
|
-
return { decision: 'allow', reason: `Allowed by rule: ${signature}`, source: 'settings.allow' };
|
|
260
|
-
}
|
|
261
|
-
return decisionForMode(settings.permissions.mode, `Default ${settings.permissions.mode} policy`, 'mode', riskForAction(action));
|
|
262
|
-
}
|
|
263
|
-
export function protectedTargetReason(action, root) {
|
|
264
|
-
if (action.kind !== 'edit' && action.kind !== 'read')
|
|
265
|
-
return null;
|
|
266
|
-
const target = resolve(root, action.target);
|
|
267
|
-
const name = basename(target);
|
|
268
|
-
if (protectedBasenames.has(name) || name.startsWith('.env.')) {
|
|
269
|
-
return `Protected file: ${action.target}`;
|
|
270
|
-
}
|
|
271
|
-
if (protectedSuffixes.some((suffix) => name.endsWith(suffix))) {
|
|
272
|
-
return `Protected file suffix: ${action.target}`;
|
|
273
|
-
}
|
|
274
|
-
if (target.includes('/.ssh/') || target.includes('/.gnupg/') || target.includes('/.aws/')) {
|
|
275
|
-
return `Protected credential path: ${action.target}`;
|
|
276
|
-
}
|
|
277
|
-
return null;
|
|
278
|
-
}
|
|
279
|
-
function decisionForMode(mode, reason, source, risk) {
|
|
280
|
-
switch (mode) {
|
|
281
|
-
case 'plan':
|
|
282
|
-
return risk === 'low'
|
|
283
|
-
? { decision: 'allow', reason, source }
|
|
284
|
-
: { decision: 'deny', reason: `${reason}; plan mode blocks mutating actions`, source: 'mode.plan' };
|
|
285
|
-
case 'ask':
|
|
286
|
-
return { decision: 'ask', reason, risk };
|
|
287
|
-
case 'acceptEdits':
|
|
288
|
-
return risk === 'medium'
|
|
289
|
-
? { decision: 'allow', reason, source: 'mode.acceptEdits' }
|
|
290
|
-
: { decision: 'ask', reason, risk };
|
|
291
|
-
case 'auto':
|
|
292
|
-
return risk === 'high' ? { decision: 'ask', reason, risk } : { decision: 'allow', reason, source: 'mode.auto' };
|
|
293
|
-
case 'dontAsk':
|
|
294
|
-
return risk === 'high'
|
|
295
|
-
? { decision: 'deny', reason: `${reason}; dontAsk denies high-risk actions`, source: 'mode.dontAsk' }
|
|
296
|
-
: { decision: 'allow', reason, source: 'mode.dontAsk' };
|
|
297
|
-
case 'bypassPermissions':
|
|
298
|
-
return { decision: 'allow', reason, source: 'mode.bypassPermissions' };
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
function riskForAction(action) {
|
|
302
|
-
if (action.kind === 'read')
|
|
303
|
-
return 'low';
|
|
304
|
-
if (action.kind === 'edit')
|
|
305
|
-
return 'medium';
|
|
306
|
-
if (action.kind === 'bash')
|
|
307
|
-
return 'high';
|
|
308
|
-
if (action.kind === 'workflow')
|
|
309
|
-
return 'medium';
|
|
310
|
-
return 'medium';
|
|
311
|
-
}
|
|
312
|
-
/**
|
|
313
|
-
* Expand `${VAR}` and `$VAR` references in a rule string against
|
|
314
|
-
* `process.env`. Unknown variables expand к the empty string so a
|
|
315
|
-
* typo'd `${HMOE}` cannot accidentally match every signature via a
|
|
316
|
-
* literal `${HMOE}` substring; the expanded `mcp:fs/` is innocuous.
|
|
317
|
-
*
|
|
318
|
-
* task #23 — operator wants `mcp:secrets/${USER}-*`
|
|
319
|
-
* style rules so a single settings file fits multiple machines.
|
|
320
|
-
*
|
|
321
|
-
* The implementation deliberately avoids shell-style features like
|
|
322
|
-
* `$()` or default-value `${VAR:-x}`; permission rules should be
|
|
323
|
-
* easy to reason about at audit time.
|
|
324
|
-
*/
|
|
325
|
-
export function expandEnvVars(rule, env = process.env) {
|
|
326
|
-
// Single combined regex. Sequential `${VAR}` then `$VAR` passes are
|
|
327
|
-
// unsafe — if `${X}` expands to a value containing `$Y`, the second
|
|
328
|
-
// pass would also expand that. An attacker controlling one env var
|
|
329
|
-
// could rewrite permission rules. Substitute each variable exactly
|
|
330
|
-
// once by matching both forms in a single sweep.
|
|
331
|
-
return rule.replace(/\$(?:\{([A-Za-z_][A-Za-z0-9_]*)\}|([A-Za-z_][A-Za-z0-9_]*))/g, (_m, braced, bare) => {
|
|
332
|
-
const name = braced ?? bare ?? '';
|
|
333
|
-
return env[name] ?? '';
|
|
334
|
-
});
|
|
335
|
-
}
|
|
336
|
-
/**
|
|
337
|
-
* Convert a glob-style rule to a RegExp anchored full-string.
|
|
338
|
-
* Supports: `*` (greedy any), `?` (single char), all other regex
|
|
339
|
-
* metacharacters escaped. The simpler tail-`*` form remains valid —
|
|
340
|
-
* `mcp:fs/ *` works the same way as before, just via regex instead of
|
|
341
|
-
* the old `startsWith` branch. Power users get `mcp:* / write*` style
|
|
342
|
-
* cross-server denies.
|
|
343
|
-
*/
|
|
344
|
-
const REGEX_META = new Set(['\\', '^', '$', '.', '|', '+', '(', ')', '[', ']', '{', '}']);
|
|
345
|
-
function ruleToRegExp(rule) {
|
|
346
|
-
let out = '';
|
|
347
|
-
for (const ch of rule) {
|
|
348
|
-
if (ch === '*')
|
|
349
|
-
out += '.*';
|
|
350
|
-
else if (ch === '?')
|
|
351
|
-
out += '.';
|
|
352
|
-
else if (REGEX_META.has(ch))
|
|
353
|
-
out += '\\' + ch;
|
|
354
|
-
else
|
|
355
|
-
out += ch;
|
|
356
|
-
}
|
|
357
|
-
return new RegExp('^' + out + '$');
|
|
358
|
-
}
|
|
359
|
-
function matchesAny(value, rules) {
|
|
360
|
-
return rules.some((rule) => {
|
|
361
|
-
const expanded = expandEnvVars(rule);
|
|
362
|
-
if (expanded === value)
|
|
363
|
-
return true;
|
|
364
|
-
if (!expanded.includes('*') && !expanded.includes('?'))
|
|
365
|
-
return false;
|
|
366
|
-
return ruleToRegExp(expanded).test(value);
|
|
367
|
-
});
|
|
368
|
-
}
|
|
369
|
-
//# sourceMappingURL=permission.js.map
|
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Auto-mode classifier — Phase 1 (regex allowlist + denylist).
|
|
3
|
-
*
|
|
4
|
-
* `permissionMode === 'auto'` is the the upstream tool parity mode where the
|
|
5
|
-
* classifier decides safe-vs-unsafe per call. Phase 1 ships without
|
|
6
|
-
* ML — just two curated regex lists covering the 80% of "obviously
|
|
7
|
-
* safe" and "obviously catastrophic" patterns. Anything that doesn't
|
|
8
|
-
* match either list returns `ask`, so the operator stays in the loop
|
|
9
|
-
* для the ambiguous middle.
|
|
10
|
-
*
|
|
11
|
-
* Phase 2 (deferred, NOT in this PR): semantic classifier consulting
|
|
12
|
-
* the model with a tight system prompt. The interface (`AutoVerdict`)
|
|
13
|
-
* is stable so we can swap implementations without touching the gate.
|
|
14
|
-
*
|
|
15
|
-
* Design notes:
|
|
16
|
-
*
|
|
17
|
-
* - Patterns are conservative: a read-only command is only
|
|
18
|
-
* allow-listed когда its argv shape is unambiguous (no `-exec`,
|
|
19
|
-
* no `--delete`, no `|` к shell). When в doubt, fall back to ask.
|
|
20
|
-
* - The denylist matches catastrophic patterns even в auto-mode so
|
|
21
|
-
* a misclick can't shred the workspace. The circuit-breaker
|
|
22
|
-
* (`circuit-breaker.ts`) covers the same surface для bypass-mode;
|
|
23
|
-
* this denylist is the auto-mode equivalent.
|
|
24
|
-
* - All matches operate on the FULL command string, not parsed
|
|
25
|
-
* argv. This is deliberately permissive on whitespace but strict
|
|
26
|
-
* on operator characters (`|`, `&`, `;`, `>`, backticks) — a
|
|
27
|
-
* pipe-into-shell или command-chain forces fallback к ask.
|
|
28
|
-
*/
|
|
29
|
-
/**
|
|
30
|
-
* Catastrophic patterns — the auto-mode regex denylist. Each entry
|
|
31
|
-
* carries a human-readable reason surfaced в the deny payload so the
|
|
32
|
-
* operator + audit log see why the gate refused. Order matters: most-
|
|
33
|
-
* specific первой так "rm -rf /" reports as that, не the generic
|
|
34
|
-
* "rm -rf".
|
|
35
|
-
*/
|
|
36
|
-
const AUTO_DENY_PATTERNS = [
|
|
37
|
-
{ pattern: /\brm\s+(-[a-z]*r[a-z]*f|-[a-z]*f[a-z]*r)\b/i, reason: 'rm -rf (recursive force-delete)' },
|
|
38
|
-
{ pattern: /\bgit\s+push\s+(-{1,2}force\b|\-f\b)/i, reason: 'git push --force (history rewrite)' },
|
|
39
|
-
{ pattern: /\bgit\s+reset\s+--hard\b/i, reason: 'git reset --hard (uncommitted-work loss)' },
|
|
40
|
-
{ pattern: /\bdd\s+if=\/(dev|)/i, reason: 'dd if=/dev/* (raw device read/write)' },
|
|
41
|
-
{ pattern: /\bmkfs(\.|\s|$)/i, reason: 'mkfs (filesystem format)' },
|
|
42
|
-
{ pattern: /\bchmod\s+-R\s+777\b/i, reason: 'chmod -R 777 (world-writable recursive)' },
|
|
43
|
-
{ pattern: /\bchown\s+-R\b/i, reason: 'chown -R (recursive ownership change)' },
|
|
44
|
-
{ pattern: /:\(\)\s*\{\s*:\s*\|\s*:\s*&\s*\}\s*;?\s*:/, reason: 'fork bomb signature' },
|
|
45
|
-
{ pattern: /\bsudo\b/i, reason: 'sudo (privilege escalation)' },
|
|
46
|
-
{ pattern: /\b(npm|pnpm|yarn)\s+publish\b/i, reason: 'package publish (irreversible npm release)' },
|
|
47
|
-
{ pattern: /\b(curl|wget)\b[^|;&]*\|\s*(sh|bash|zsh)\b/i, reason: 'pipe-to-shell installer (curl … | sh)' },
|
|
48
|
-
];
|
|
49
|
-
/**
|
|
50
|
-
* Safe-by-default patterns — auto-mode regex allowlist. Each regex
|
|
51
|
-
* must match the FULL command (with `^…$` anchors) so a leading
|
|
52
|
-
* `sudo ls` или a trailing `; rm -rf /` does NOT slip through. The
|
|
53
|
-
* caller passes the trimmed command string; whitespace around argv
|
|
54
|
-
* tokens is tolerated.
|
|
55
|
-
*/
|
|
56
|
-
const AUTO_ALLOW_PATTERNS = [
|
|
57
|
-
{ pattern: /^ls(\s+-[a-zA-Z]+)*(\s+[^|;&`>$()\\]+)?$/, reason: 'ls (directory listing)' },
|
|
58
|
-
{ pattern: /^pwd\s*$/, reason: 'pwd (working directory)' },
|
|
59
|
-
{ pattern: /^cat\s+[^|;&`>$()\\]+$/, reason: 'cat (file read)' },
|
|
60
|
-
{ pattern: /^head(\s+-n?\s*\d+)?\s+[^|;&`>$()\\]+$/, reason: 'head (file preview)' },
|
|
61
|
-
{ pattern: /^tail(\s+-n?\s*\d+)?\s+[^|;&`>$()\\]+$/, reason: 'tail (file preview)' },
|
|
62
|
-
{ pattern: /^wc(\s+-[a-z]+)?\s+[^|;&`>$()\\]+$/, reason: 'wc (line/word count)' },
|
|
63
|
-
{ pattern: /^du\s+-sh?\s+[^|;&`>$()\\]+$/, reason: 'du -sh (disk usage summary)' },
|
|
64
|
-
{ pattern: /^df\s+-h\s*$/, reason: 'df -h (filesystem free space)' },
|
|
65
|
-
{ pattern: /^git\s+status(\s+--short|\s+-s)?\s*$/, reason: 'git status' },
|
|
66
|
-
{ pattern: /^git\s+diff(\s+[a-zA-Z0-9_./~^-]+)*\s*$/, reason: 'git diff (read-only)' },
|
|
67
|
-
{ pattern: /^git\s+log(\s+[a-zA-Z0-9_./~^-]+)*\s*$/, reason: 'git log (read-only)' },
|
|
68
|
-
{ pattern: /^git\s+branch(\s+-[a-z]+)?\s*$/, reason: 'git branch (read-only)' },
|
|
69
|
-
{ pattern: /^git\s+remote\s+-v\s*$/, reason: 'git remote -v (read-only)' },
|
|
70
|
-
{ pattern: /^pnpm\s+(typecheck|lint|test\s+--run|test\s+--watch=false)\s*$/, reason: 'pnpm read-only build check' },
|
|
71
|
-
{ pattern: /^npm\s+(--version|-v|run\s+typecheck|run\s+lint)\s*$/, reason: 'npm read-only check' },
|
|
72
|
-
{ pattern: /^node\s+--version\s*$/, reason: 'node --version' },
|
|
73
|
-
{ pattern: /^pnpm\s+--version\s*$/, reason: 'pnpm --version' },
|
|
74
|
-
{ pattern: /^which\s+[a-zA-Z0-9_-]+\s*$/, reason: 'which (command lookup)' },
|
|
75
|
-
{ pattern: /^find\s+\.\s+-type\s+f(\s+-name\s+[^|;&`>$()\\]+)?\s*$/, reason: 'find -type f (read-only)' },
|
|
76
|
-
{ pattern: /^(rg|ripgrep|grep)\s+(-[a-z]+\s+)*[^|;&`>$()\\]+(\s+[^|;&`>$()\\]+)?\s*$/, reason: 'grep/ripgrep (read-only search)' },
|
|
77
|
-
];
|
|
78
|
-
/**
|
|
79
|
-
* Classify an auto-mode command. Order:
|
|
80
|
-
* 1. Catastrophic deny patterns — surface the explicit deny reason.
|
|
81
|
-
* 2. Safe allow patterns — surface the matched reason.
|
|
82
|
-
* 3. Fallback к ask.
|
|
83
|
-
*
|
|
84
|
-
* The order matters: a destructive pattern that ALSO looks like a
|
|
85
|
-
* read-only token (e.g. `git diff ; rm -rf .`) hits deny first because
|
|
86
|
-
* the allow patterns require `^…$` anchors that the chained command
|
|
87
|
-
* fails to satisfy. Belt + suspenders.
|
|
88
|
-
*/
|
|
89
|
-
export function classifyAutoMode(command) {
|
|
90
|
-
const trimmed = command.trim();
|
|
91
|
-
if (trimmed.length === 0)
|
|
92
|
-
return { verdict: 'ask' };
|
|
93
|
-
for (const entry of AUTO_DENY_PATTERNS) {
|
|
94
|
-
if (entry.pattern.test(trimmed)) {
|
|
95
|
-
return {
|
|
96
|
-
verdict: 'deny',
|
|
97
|
-
reason: entry.reason,
|
|
98
|
-
pattern: entry.pattern.source,
|
|
99
|
-
};
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
for (const entry of AUTO_ALLOW_PATTERNS) {
|
|
103
|
-
if (entry.pattern.test(trimmed)) {
|
|
104
|
-
return {
|
|
105
|
-
verdict: 'allow',
|
|
106
|
-
reason: entry.reason,
|
|
107
|
-
pattern: entry.pattern.source,
|
|
108
|
-
};
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
return { verdict: 'ask' };
|
|
112
|
-
}
|
|
113
|
-
/**
|
|
114
|
-
* Diagnostic accessors — exposed для doctor surfaces + spec coverage.
|
|
115
|
-
* The arrays are frozen at module load so callers can iterate without
|
|
116
|
-
* mutating the source-of-truth.
|
|
117
|
-
*/
|
|
118
|
-
export function listAutoAllowPatterns() {
|
|
119
|
-
return AUTO_ALLOW_PATTERNS.map((e) => ({ pattern: e.pattern.source, reason: e.reason }));
|
|
120
|
-
}
|
|
121
|
-
export function listAutoDenyPatterns() {
|
|
122
|
-
return AUTO_DENY_PATTERNS.map((e) => ({ pattern: e.pattern.source, reason: e.reason }));
|
|
123
|
-
}
|
|
124
|
-
//# sourceMappingURL=auto-classifier.js.map
|