@pugi/cli 0.1.0-beta.98 → 1.0.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +11 -191
- package/bin/pugi +8 -0
- package/package.json +15 -71
- package/postinstall.mjs +31 -0
- package/CHANGELOG.md +0 -132
- package/THIRD_PARTY_NOTICES.md +0 -40
- package/assets/pugi-mascot.ansi +0 -16
- package/assets/pugi-prozr2-mascot.ansi +0 -9
- package/bin/run.js +0 -34
- package/dist/commands/deploy.js +0 -439
- package/dist/commands/flatten.js +0 -191
- package/dist/commands/jobs-watch.js +0 -201
- package/dist/commands/jobs.js +0 -260
- package/dist/commands/retro.js +0 -210
- package/dist/commands/smoke.js +0 -133
- package/dist/core/agent-progress/cleanup.js +0 -134
- package/dist/core/agent-progress/schema.js +0 -144
- package/dist/core/agent-progress/writer.js +0 -101
- package/dist/core/agents/adaptive-router.js +0 -330
- package/dist/core/agents/loader.js +0 -104
- package/dist/core/agents/query-decomposer.js +0 -297
- package/dist/core/agents/registry.js +0 -69
- package/dist/core/approvals/shortcut-resolver.js +0 -98
- package/dist/core/artifact-chain/dispatcher.js +0 -148
- package/dist/core/artifact-chain/exporter.js +0 -164
- package/dist/core/artifact-chain/state.js +0 -243
- package/dist/core/artifact-chain/steps.js +0 -169
- package/dist/core/ask-user/question.js +0 -92
- package/dist/core/audit/audit-trail.js +0 -275
- package/dist/core/auth/ensure-authenticated.js +0 -129
- package/dist/core/auth/env-provider.js +0 -238
- package/dist/core/auto-open-browser.js +0 -128
- package/dist/core/auto-update/channels.js +0 -122
- package/dist/core/auto-update/checker.js +0 -241
- package/dist/core/auto-update/state.js +0 -235
- package/dist/core/bare-mode/index.js +0 -107
- package/dist/core/bash/redirect.js +0 -281
- package/dist/core/bash-classifier.js +0 -1397
- package/dist/core/checkpoint/resumer.js +0 -149
- package/dist/core/checkpoint/rewinder.js +0 -291
- package/dist/core/checkpoints/shadow-git.js +0 -670
- package/dist/core/citations/parser.js +0 -109
- package/dist/core/classifier/yolo-classifier.js +0 -88
- package/dist/core/clipboard.js +0 -70
- package/dist/core/codegraph/decision-store.js +0 -248
- package/dist/core/codegraph/detect-repo.js +0 -459
- package/dist/core/codegraph/install.js +0 -134
- package/dist/core/codegraph/offer-hook.js +0 -220
- package/dist/core/compact/auto-trigger.js +0 -96
- package/dist/core/compact/buffer-rewriter.js +0 -115
- package/dist/core/compact/summarizer.js +0 -208
- package/dist/core/compact/token-counter.js +0 -108
- package/dist/core/consensus/anvil-fanout.js +0 -276
- package/dist/core/consensus/diff-capture.js +0 -491
- package/dist/core/consensus/rubric.js +0 -233
- package/dist/core/context/builder.js +0 -114
- package/dist/core/context/compaction-events.js +0 -99
- package/dist/core/context/compaction.js +0 -602
- package/dist/core/context/index.js +0 -28
- package/dist/core/context/invariants.js +0 -250
- package/dist/core/context/markdown-loader.js +0 -288
- package/dist/core/context/markdown-traverse.js +0 -255
- package/dist/core/context/pugiignore.js +0 -316
- package/dist/core/context/repo-skeleton.js +0 -533
- package/dist/core/context/tool-eviction.js +0 -55
- package/dist/core/context/watcher.js +0 -342
- package/dist/core/context/working-set.js +0 -165
- package/dist/core/coordinator/agent-tools.js +0 -77
- package/dist/core/coordinator/agent-toolset.js +0 -65
- package/dist/core/coordinator/fsm.js +0 -73
- package/dist/core/coordinator/mode-fsm.js +0 -70
- package/dist/core/cost/rate-card.js +0 -129
- package/dist/core/cost/tracker.js +0 -221
- package/dist/core/credentials.js +0 -355
- package/dist/core/cron/scheduler.js +0 -138
- package/dist/core/denial-tracking/index.js +0 -8
- package/dist/core/denial-tracking/state.js +0 -264
- package/dist/core/diagnostics/probe-runner.js +0 -93
- package/dist/core/diagnostics/probes/api.js +0 -46
- package/dist/core/diagnostics/probes/auth.js +0 -93
- package/dist/core/diagnostics/probes/bare-mode.js +0 -42
- package/dist/core/diagnostics/probes/cli-version.js +0 -127
- package/dist/core/diagnostics/probes/config.js +0 -72
- package/dist/core/diagnostics/probes/denial-tracking.js +0 -57
- package/dist/core/diagnostics/probes/disk.js +0 -81
- package/dist/core/diagnostics/probes/engine-live.js +0 -46
- package/dist/core/diagnostics/probes/git.js +0 -65
- package/dist/core/diagnostics/probes/hooks.js +0 -118
- package/dist/core/diagnostics/probes/mcp.js +0 -75
- package/dist/core/diagnostics/probes/node.js +0 -59
- package/dist/core/diagnostics/probes/pnpm.js +0 -36
- package/dist/core/diagnostics/probes/pugi-md.js +0 -89
- package/dist/core/diagnostics/probes/sandbox.js +0 -72
- package/dist/core/diagnostics/probes/session.js +0 -74
- package/dist/core/diagnostics/probes/status-snapshot.js +0 -488
- package/dist/core/diagnostics/probes/workspace.js +0 -63
- package/dist/core/diagnostics/types.js +0 -70
- package/dist/core/dispatch/cache-cleanup.js +0 -197
- package/dist/core/dispatch/cache-handoff.js +0 -295
- package/dist/core/edits/apply-patch-layer-e.js +0 -189
- package/dist/core/edits/dispatch.js +0 -511
- package/dist/core/edits/format-detector.js +0 -260
- package/dist/core/edits/format-matrix.js +0 -26
- package/dist/core/edits/fuzzy-ladder.js +0 -650
- package/dist/core/edits/index.js +0 -19
- package/dist/core/edits/journal.js +0 -199
- package/dist/core/edits/layer-a-apply.js +0 -217
- package/dist/core/edits/layer-a-fuzzy-apply.js +0 -198
- package/dist/core/edits/layer-b-apply.js +0 -211
- package/dist/core/edits/layer-c-apply.js +0 -160
- package/dist/core/edits/layer-d-ast.js +0 -572
- package/dist/core/edits/marker-parser.js +0 -401
- package/dist/core/edits/security-gate.js +0 -223
- package/dist/core/edits/verify-hook.js +0 -273
- package/dist/core/edits/worktree.js +0 -322
- package/dist/core/engine/adapter-runner.js +0 -8
- package/dist/core/engine/anvil-client.js +0 -344
- package/dist/core/engine/auto-compact.js +0 -179
- package/dist/core/engine/budgets.js +0 -192
- package/dist/core/engine/context-prefix.js +0 -155
- package/dist/core/engine/index.js +0 -12
- package/dist/core/engine/intensity.js +0 -163
- package/dist/core/engine/intent.js +0 -260
- package/dist/core/engine/native-pugi.js +0 -1616
- package/dist/core/engine/noop.js +0 -27
- package/dist/core/engine/prompts.js +0 -236
- package/dist/core/engine/strip-internal-fields.js +0 -124
- package/dist/core/engine/tool-bridge.js +0 -2173
- package/dist/core/engine/verification-patterns.js +0 -195
- package/dist/core/evaluation/golden-dataset.js +0 -293
- package/dist/core/feedback/queue.js +0 -177
- package/dist/core/feedback/submitter.js +0 -145
- package/dist/core/file-cache.js +0 -141
- package/dist/core/flatten/flatten-repo.js +0 -439
- package/dist/core/format/osc8-link.js +0 -28
- package/dist/core/hook-chains.js +0 -392
- package/dist/core/hooks/citation-verify-hook.js +0 -138
- package/dist/core/hooks/citation-verify.js +0 -112
- package/dist/core/hooks/events.js +0 -46
- package/dist/core/hooks/index.js +0 -15
- package/dist/core/hooks/registry.js +0 -216
- package/dist/core/hooks/runner.js +0 -236
- package/dist/core/hooks/v2/event-emitter.js +0 -115
- package/dist/core/hooks/v2/executor.js +0 -282
- package/dist/core/hooks/v2/index.js +0 -25
- package/dist/core/hooks/v2/lifecycle.js +0 -104
- package/dist/core/hooks/v2/loader.js +0 -216
- package/dist/core/hooks/v2/matcher.js +0 -125
- package/dist/core/hooks/v2/trust.js +0 -143
- package/dist/core/hooks/v2/types.js +0 -86
- package/dist/core/hooks/worktree-events.js +0 -158
- package/dist/core/hooks.js +0 -415
- package/dist/core/image/renderer.js +0 -71
- package/dist/core/index-store.js +0 -260
- package/dist/core/init/detector.js +0 -582
- package/dist/core/init/template-renderer.js +0 -242
- package/dist/core/jobs/registry.js +0 -462
- package/dist/core/ledger/results-tsv.js +0 -142
- package/dist/core/log-discipline/stdout-redirect.js +0 -51
- package/dist/core/lsp/cache.js +0 -105
- package/dist/core/lsp/client.js +0 -1229
- package/dist/core/lsp/language-detect.js +0 -66
- package/dist/core/lsp/post-edit-diagnostics.js +0 -171
- package/dist/core/lsp/server-detect.js +0 -173
- package/dist/core/lsp/symbol-cache.js +0 -162
- package/dist/core/lsp/symbol-tools.js +0 -664
- package/dist/core/mcp/client.js +0 -385
- package/dist/core/mcp/http-server.js +0 -553
- package/dist/core/mcp/orchestrator-config.js +0 -192
- package/dist/core/mcp/orchestrator-tools.js +0 -806
- package/dist/core/mcp/permission.js +0 -190
- package/dist/core/mcp/registry.js +0 -193
- package/dist/core/mcp/server-tools.js +0 -219
- package/dist/core/mcp/server.js +0 -397
- package/dist/core/mcp/trust.js +0 -91
- package/dist/core/memory/dual-write.js +0 -416
- package/dist/core/memory/passive-extract.js +0 -130
- package/dist/core/memory/phase1-kinds.js +0 -20
- package/dist/core/memory/secret-scanner.js +0 -304
- package/dist/core/memory-sync/queue.js +0 -170
- package/dist/core/metrics/extract.js +0 -113
- package/dist/core/modes/roo-modes.js +0 -68
- package/dist/core/onboarding/ensure-initialized.js +0 -133
- package/dist/core/onboarding/marker.js +0 -111
- package/dist/core/onboarding/telemetry-state.js +0 -108
- package/dist/core/output-style/presets.js +0 -176
- package/dist/core/output-style/state.js +0 -185
- package/dist/core/path-security.js +0 -345
- package/dist/core/permission.js +0 -369
- package/dist/core/permissions/auto-classifier.js +0 -124
- package/dist/core/permissions/bash-parser.js +0 -371
- package/dist/core/permissions/circuit-breaker.js +0 -83
- package/dist/core/permissions/constrained-edit.js +0 -91
- package/dist/core/permissions/gate.js +0 -278
- package/dist/core/permissions/index.js +0 -20
- package/dist/core/permissions/mode.js +0 -174
- package/dist/core/permissions/network-egress.js +0 -137
- package/dist/core/permissions/state.js +0 -241
- package/dist/core/permissions/tool-class.js +0 -107
- package/dist/core/plan-mode/ui-state.js +0 -51
- package/dist/core/plans/plan-artifact.js +0 -721
- package/dist/core/policy-limits/etag-store.js +0 -122
- package/dist/core/prd-check/parser.js +0 -215
- package/dist/core/prd-check/reporter.js +0 -127
- package/dist/core/prd-check/session-review.js +0 -557
- package/dist/core/prd-check/verifiers.js +0 -223
- package/dist/core/prompt-cache/client-cache.js +0 -99
- package/dist/core/prompts/assembly.js +0 -29
- package/dist/core/prompts/registry.js +0 -364
- package/dist/core/pugi-gitignore.js +0 -52
- package/dist/core/pugi-md/cc-compat-rules.js +0 -735
- package/dist/core/pugi-md/context-injector.js +0 -76
- package/dist/core/pugi-md/walk-up.js +0 -207
- package/dist/core/python/uv-installer.js +0 -270
- package/dist/core/python/uv-resolver.js +0 -83
- package/dist/core/rate-limit/narrator.js +0 -146
- package/dist/core/recipes/cli-types.js +0 -20
- package/dist/core/recipes/loader.js +0 -103
- package/dist/core/recipes/runner.js +0 -345
- package/dist/core/recipes/schema.js +0 -587
- package/dist/core/release-notes/parser.js +0 -241
- package/dist/core/release-notes/state.js +0 -116
- package/dist/core/repl/ask.js +0 -512
- package/dist/core/repl/cancellation.js +0 -98
- package/dist/core/repl/cap-warning.js +0 -91
- package/dist/core/repl/clipboard-read.js +0 -174
- package/dist/core/repl/dispatch-fsm.js +0 -220
- package/dist/core/repl/engine-bridge.js +0 -303
- package/dist/core/repl/history-search.js +0 -175
- package/dist/core/repl/history.js +0 -182
- package/dist/core/repl/kill-ring.js +0 -138
- package/dist/core/repl/model-pricing.js +0 -135
- package/dist/core/repl/privacy-banner.js +0 -71
- package/dist/core/repl/session.js +0 -4962
- package/dist/core/repl/slash-commands.js +0 -747
- package/dist/core/repl/store/index.js +0 -12
- package/dist/core/repl/store/jsonl-log.js +0 -321
- package/dist/core/repl/store/lockfile.js +0 -155
- package/dist/core/repl/store/session-store.js +0 -821
- package/dist/core/repl/store/types.js +0 -44
- package/dist/core/repl/store/uuid-v7.js +0 -68
- package/dist/core/repl/tool-route.js +0 -382
- package/dist/core/repl/workspace-context.js +0 -206
- package/dist/core/repo-map/build.js +0 -125
- package/dist/core/repo-map/cache.js +0 -185
- package/dist/core/repo-map/extractor.js +0 -254
- package/dist/core/repo-map/formatter.js +0 -145
- package/dist/core/repo-map/page-rank.js +0 -105
- package/dist/core/repo-map/scanner.js +0 -211
- package/dist/core/retro/git-collector.js +0 -251
- package/dist/core/retro/health-card.js +0 -25
- package/dist/core/retro/metrics.js +0 -342
- package/dist/core/retro/narrative.js +0 -249
- package/dist/core/retro/plane-collector.js +0 -274
- package/dist/core/retro/pr-issue-link.js +0 -65
- package/dist/core/retro/types.js +0 -16
- package/dist/core/retry-budget/budget.js +0 -284
- package/dist/core/retry-budget/index.js +0 -5
- package/dist/core/retry-budget/retry-cap.js +0 -74
- package/dist/core/routing/lead-worker.js +0 -43
- package/dist/core/routing/pre-flight-estimator.js +0 -108
- package/dist/core/runs/run-tree.js +0 -103
- package/dist/core/sandboxing/adapter.js +0 -29
- package/dist/core/sandboxing/index.js +0 -49
- package/dist/core/sandboxing/none.js +0 -19
- package/dist/core/sandboxing/seatbelt.js +0 -183
- package/dist/core/security/injection-scanner.js +0 -367
- package/dist/core/security/output-filter.js +0 -418
- package/dist/core/session/env-file.js +0 -105
- package/dist/core/session/section-budgets.js +0 -140
- package/dist/core/session.js +0 -377
- package/dist/core/settings.js +0 -400
- package/dist/core/share/formatter.js +0 -271
- package/dist/core/share/redactor.js +0 -221
- package/dist/core/share/uploader.js +0 -267
- package/dist/core/skills/defaults.js +0 -457
- package/dist/core/skills/loader.js +0 -454
- package/dist/core/skills/sources.js +0 -480
- package/dist/core/skills/trust.js +0 -172
- package/dist/core/smoke/headless-driver.js +0 -174
- package/dist/core/smoke/orchestrator.js +0 -194
- package/dist/core/smoke/runner.js +0 -238
- package/dist/core/smoke/scenario-parser.js +0 -316
- package/dist/core/statusline.js +0 -99
- package/dist/core/subagents/dispatcher-real.js +0 -600
- package/dist/core/subagents/dispatcher.js +0 -352
- package/dist/core/subagents/index.js +0 -39
- package/dist/core/subagents/isolation-matrix.js +0 -213
- package/dist/core/subagents/spawn.js +0 -101
- package/dist/core/telemetry/emitter.js +0 -229
- package/dist/core/telemetry/queue.js +0 -251
- package/dist/core/theme/context.js +0 -91
- package/dist/core/theme/presets.js +0 -228
- package/dist/core/theme/state.js +0 -181
- package/dist/core/todos/invariant.js +0 -10
- package/dist/core/todos/state.js +0 -177
- package/dist/core/tool-schema/compressor.js +0 -89
- package/dist/core/transport/version-interceptor.js +0 -166
- package/dist/core/trust.js +0 -109
- package/dist/core/tui/thinking-block.js +0 -64
- package/dist/core/vim/keymap.js +0 -288
- package/dist/core/vim/state.js +0 -92
- package/dist/core/watch-markers/marker-watcher.js +0 -133
- package/dist/core/worktree/include-parser.js +0 -249
- package/dist/core/worktree-manager/cleanup.js +0 -123
- package/dist/core/worktree-manager/manager.js +0 -303
- package/dist/index.js +0 -44
- package/dist/runtime/bootstrap.js +0 -190
- package/dist/runtime/cli.js +0 -8121
- package/dist/runtime/commands/agents.js +0 -385
- package/dist/runtime/commands/budget.js +0 -192
- package/dist/runtime/commands/cancel.js +0 -231
- package/dist/runtime/commands/chain.js +0 -489
- package/dist/runtime/commands/codegraph-status.js +0 -227
- package/dist/runtime/commands/compact.js +0 -297
- package/dist/runtime/commands/config.js +0 -595
- package/dist/runtime/commands/cost.js +0 -199
- package/dist/runtime/commands/delegate.js +0 -312
- package/dist/runtime/commands/dispatch.js +0 -126
- package/dist/runtime/commands/doctor.js +0 -579
- package/dist/runtime/commands/feedback.js +0 -184
- package/dist/runtime/commands/hooks.js +0 -187
- package/dist/runtime/commands/init.js +0 -254
- package/dist/runtime/commands/lsp.js +0 -368
- package/dist/runtime/commands/mcp.js +0 -935
- package/dist/runtime/commands/memory.js +0 -582
- package/dist/runtime/commands/model.js +0 -237
- package/dist/runtime/commands/onboarding.js +0 -275
- package/dist/runtime/commands/patch.js +0 -128
- package/dist/runtime/commands/permissions.js +0 -112
- package/dist/runtime/commands/plan.js +0 -143
- package/dist/runtime/commands/prd-check.js +0 -285
- package/dist/runtime/commands/privacy.js +0 -107
- package/dist/runtime/commands/recipe.js +0 -325
- package/dist/runtime/commands/redo-blob-store.js +0 -92
- package/dist/runtime/commands/redo.js +0 -361
- package/dist/runtime/commands/release-notes.js +0 -229
- package/dist/runtime/commands/repo-map.js +0 -95
- package/dist/runtime/commands/report.js +0 -299
- package/dist/runtime/commands/resume.js +0 -118
- package/dist/runtime/commands/review-consensus.js +0 -414
- package/dist/runtime/commands/rewind.js +0 -333
- package/dist/runtime/commands/roster.js +0 -117
- package/dist/runtime/commands/sessions.js +0 -163
- package/dist/runtime/commands/share.js +0 -316
- package/dist/runtime/commands/skills.js +0 -401
- package/dist/runtime/commands/status.js +0 -186
- package/dist/runtime/commands/stickers.js +0 -82
- package/dist/runtime/commands/style.js +0 -194
- package/dist/runtime/commands/theme.js +0 -196
- package/dist/runtime/commands/undo.js +0 -361
- package/dist/runtime/commands/update.js +0 -289
- package/dist/runtime/commands/vim.js +0 -140
- package/dist/runtime/commands/worktree.js +0 -177
- package/dist/runtime/commands/worktrees.js +0 -155
- package/dist/runtime/deprecation-warning.js +0 -69
- package/dist/runtime/engine-exit-code.js +0 -50
- package/dist/runtime/headless-repl.js +0 -195
- package/dist/runtime/headless.js +0 -548
- package/dist/runtime/load-hooks-or-exit.js +0 -71
- package/dist/runtime/plan-decompose.js +0 -531
- package/dist/runtime/sigint-guard.js +0 -272
- package/dist/runtime/stream-renderer.js +0 -195
- package/dist/runtime/update-check.js +0 -294
- package/dist/runtime/version.js +0 -65
- package/dist/runtime/worktree-bootstrap.js +0 -579
- package/dist/skills/bundled/batch.js +0 -617
- package/dist/skills/bundled/index.js +0 -45
- package/dist/skills/bundled/loop.js +0 -358
- package/dist/skills/bundled/remember.js +0 -383
- package/dist/skills/bundled/simplify.js +0 -289
- package/dist/skills/bundled/skillify.js +0 -373
- package/dist/skills/bundled/stuck.js +0 -558
- package/dist/skills/bundled/verify.js +0 -439
- package/dist/testing/vcr.js +0 -486
- package/dist/tools/agent-tool.js +0 -229
- package/dist/tools/apply-patch.js +0 -556
- package/dist/tools/ask-user-question.js +0 -337
- package/dist/tools/ask-user.js +0 -115
- package/dist/tools/bash.js +0 -1238
- package/dist/tools/brief.js +0 -224
- package/dist/tools/cron.js +0 -433
- package/dist/tools/enter-worktree.js +0 -250
- package/dist/tools/exit-worktree.js +0 -147
- package/dist/tools/file-tools.js +0 -553
- package/dist/tools/http-request.js +0 -336
- package/dist/tools/lsp-tools.js +0 -565
- package/dist/tools/mcp-tool.js +0 -260
- package/dist/tools/multi-edit.js +0 -361
- package/dist/tools/powershell.js +0 -268
- package/dist/tools/registry.js +0 -166
- package/dist/tools/server-tools.js +0 -892
- package/dist/tools/skill-tool.js +0 -96
- package/dist/tools/sleep.js +0 -99
- package/dist/tools/synthetic-output.js +0 -133
- package/dist/tools/tasks.js +0 -208
- package/dist/tools/todo-write.js +0 -184
- package/dist/tools/verify-plan-execution.js +0 -295
- package/dist/tools/web-fetch-injection-scanner.js +0 -207
- package/dist/tools/web-fetch.js +0 -720
- package/dist/tools/web-search.js +0 -458
- package/dist/tui/agent-progress-card.js +0 -111
- package/dist/tui/agent-tree-pane.js +0 -9
- package/dist/tui/agent-tree.js +0 -87
- package/dist/tui/ask-cli.js +0 -52
- package/dist/tui/ask-modal.js +0 -211
- package/dist/tui/ask-user-question-chips.js +0 -315
- package/dist/tui/ask-user-question-prompt.js +0 -203
- package/dist/tui/compact-banner.js +0 -81
- package/dist/tui/conversation-pane.js +0 -164
- package/dist/tui/cost-table.js +0 -111
- package/dist/tui/device-flow.js +0 -142
- package/dist/tui/doctor-table.js +0 -46
- package/dist/tui/feedback-prompt.js +0 -156
- package/dist/tui/input-box.js +0 -732
- package/dist/tui/login-picker.js +0 -69
- package/dist/tui/markdown-render.js +0 -266
- package/dist/tui/multi-file-diff-approval.js +0 -375
- package/dist/tui/onboarding-wizard.js +0 -240
- package/dist/tui/permissions-picker.js +0 -86
- package/dist/tui/render.js +0 -160
- package/dist/tui/repl-render.js +0 -770
- package/dist/tui/repl-splash-art.js +0 -64
- package/dist/tui/repl-splash-mascot.js +0 -154
- package/dist/tui/repl-splash.js +0 -117
- package/dist/tui/repl.js +0 -378
- package/dist/tui/slash-palette.js +0 -106
- package/dist/tui/splash-data.js +0 -61
- package/dist/tui/splash.js +0 -31
- package/dist/tui/status-bar.js +0 -209
- package/dist/tui/status-table.js +0 -7
- package/dist/tui/stickers-art.js +0 -136
- package/dist/tui/style-table.js +0 -28
- package/dist/tui/theme-table.js +0 -29
- package/dist/tui/thinking-spinner.js +0 -123
- package/dist/tui/tool-stream-pane.js +0 -140
- package/dist/tui/update-banner.js +0 -33
- package/dist/tui/vim-input.js +0 -267
- package/dist/tui/welcome-banner.js +0 -107
- package/dist/tui/welcome-data.js +0 -293
- package/dist/tui/workspace-context.js +0 -105
- package/docs/examples/codegraph.mcp.json +0 -10
- package/test/scenarios/codegen-create-file.scenario.txt +0 -13
- package/test/scenarios/compact-force.scenario.txt +0 -12
- package/test/scenarios/identity.scenario.txt +0 -11
- package/test/scenarios/persona-handoff.scenario.txt +0 -12
- package/test/scenarios/walkback.scenario.txt +0 -12
package/dist/core/hook-chains.js
DELETED
|
@@ -1,392 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Hook chains — `PostToolUseFailure` + `TaskCompleted` first-class events.
|
|
3
|
-
*
|
|
4
|
-
* #24 (CEO P1). Pugi already had primitives for
|
|
5
|
-
* `PostToolUseFailure` in the legacy `core/hooks.ts` registry but no
|
|
6
|
-
* way to declare a **fallback chain** that fires automatically when a
|
|
7
|
-
* tool dispatch fails or when an entire `pugi <command>` dispatch
|
|
8
|
-
* completes. the upstream tool exposes both events as first-class hook
|
|
9
|
-
* sources; this module wires them on the Pugi side.
|
|
10
|
-
*
|
|
11
|
-
* Why a fresh module rather than extending `core/hooks.ts`:
|
|
12
|
-
* - The legacy registry reads flat `hooks: [{event, match, run}]`
|
|
13
|
-
* arrays from `~/.pugi/hooks.json` + `.pugi/hooks.json`. Chains
|
|
14
|
-
* are declared in a nested `hooks: { EventName: [{matcher, run}] }`
|
|
15
|
-
* shape from `.pugi/settings.json` — a different file, different
|
|
16
|
-
* shape, and importantly a different opt-out grammar (per-chain
|
|
17
|
-
* `enabled: false`). Mixing the two readers would force every
|
|
18
|
-
* legacy caller to learn the chain shape.
|
|
19
|
-
* - Chains have richer payloads (TaskCompleted ships durationMs,
|
|
20
|
-
* toolCalls, filesChanged). Stuffing them through the legacy
|
|
21
|
-
* stdin/env contract would silently break v1 scripts that key on
|
|
22
|
-
* the existing payload shape.
|
|
23
|
-
* - Chain failures MUST NOT crash the dispatch (the model already
|
|
24
|
-
* finished). The legacy registry's `onFailure: 'block'` semantics
|
|
25
|
-
* would propagate the error — chains explicitly swallow it and
|
|
26
|
-
* log instead.
|
|
27
|
-
*
|
|
28
|
-
* The chain runner is intentionally self-contained: no dependency on
|
|
29
|
-
* `HookRegistry`, no trust-ledger gating (project settings are already
|
|
30
|
-
* trusted by virtue of being in the workspace's `.pugi/` dir, same as
|
|
31
|
-
* persona prompts), and a single `firePostToolUseFailureChain` /
|
|
32
|
-
* `fireTaskCompletedChain` entry point per event.
|
|
33
|
-
*
|
|
34
|
-
* Brand voice: ASCII only, no emoji, no em-dashes.
|
|
35
|
-
*/
|
|
36
|
-
import { spawn } from 'node:child_process';
|
|
37
|
-
import { z } from 'zod';
|
|
38
|
-
import { loadSettings } from './settings.js';
|
|
39
|
-
/**
|
|
40
|
-
* Per-hook matcher. Both keys are optional and AND together — a hook
|
|
41
|
-
* with `matcher: { tool: 'write' }` fires for every PostToolUseFailure
|
|
42
|
-
* whose tool is `write`, regardless of command. A hook with no matcher
|
|
43
|
-
* at all (or `matcher: {}`) fires on every event of its kind.
|
|
44
|
-
*/
|
|
45
|
-
const chainMatcherSchema = z
|
|
46
|
-
.object({
|
|
47
|
-
/** Compare against the failing tool name (PostToolUseFailure). */
|
|
48
|
-
tool: z.string().min(1).optional(),
|
|
49
|
-
/** Compare against the completed command name (TaskCompleted). */
|
|
50
|
-
command: z.string().min(1).optional(),
|
|
51
|
-
})
|
|
52
|
-
.strict();
|
|
53
|
-
/**
|
|
54
|
-
* Single chain entry. `run` is an array so chain authors can express
|
|
55
|
-
* "do A, then B, then C" without needing shell `&&` chaining — the
|
|
56
|
-
* runner executes the list sequentially.
|
|
57
|
-
*
|
|
58
|
-
* Hard-coded constants:
|
|
59
|
-
* - Default timeout per command: 10s.
|
|
60
|
-
* - Max timeout: 60s (matches legacy hooks.ts cap).
|
|
61
|
-
* - Stream cap: 256KB per stream (smaller than legacy 1MB because
|
|
62
|
-
* chain hooks are post-hoc notifications, not blocking gates).
|
|
63
|
-
*/
|
|
64
|
-
const chainEntrySchema = z
|
|
65
|
-
.object({
|
|
66
|
-
matcher: chainMatcherSchema.optional(),
|
|
67
|
-
run: z.array(z.string().min(1)).min(1),
|
|
68
|
-
timeoutMs: z.number().int().positive().max(60_000).optional(),
|
|
69
|
-
})
|
|
70
|
-
.strict();
|
|
71
|
-
/**
|
|
72
|
-
* Per-event chain config. `enabled: false` short-circuits the whole
|
|
73
|
-
* chain (opt-out switch the operator can flip without deleting the
|
|
74
|
-
* entries). `entries` defaults to empty so the operator can declare
|
|
75
|
-
* `{ enabled: false }` to suppress a chain inherited from a parent
|
|
76
|
-
* config layer later.
|
|
77
|
-
*/
|
|
78
|
-
const chainConfigSchema = z
|
|
79
|
-
.object({
|
|
80
|
-
enabled: z.boolean().default(true),
|
|
81
|
-
entries: z.array(chainEntrySchema).default([]),
|
|
82
|
-
})
|
|
83
|
-
.strict();
|
|
84
|
-
/**
|
|
85
|
-
* Settings-level `hooks` block. We accept BOTH:
|
|
86
|
-
* 1. The canonical nested form
|
|
87
|
-
* `hooks: { PostToolUseFailure: { enabled, entries: [...] } }`
|
|
88
|
-
* 2. The shorthand array form
|
|
89
|
-
* `hooks: { PostToolUseFailure: [...entries] }`
|
|
90
|
-
* — which the CEO spec uses as the example. The reader normalises
|
|
91
|
-
* it to the canonical form before handing back to callers.
|
|
92
|
-
*/
|
|
93
|
-
const settingsHooksShape = z
|
|
94
|
-
.object({
|
|
95
|
-
PostToolUseFailure: z
|
|
96
|
-
.union([chainConfigSchema, z.array(chainEntrySchema)])
|
|
97
|
-
.optional(),
|
|
98
|
-
TaskCompleted: z
|
|
99
|
-
.union([chainConfigSchema, z.array(chainEntrySchema)])
|
|
100
|
-
.optional(),
|
|
101
|
-
})
|
|
102
|
-
.strict()
|
|
103
|
-
.optional();
|
|
104
|
-
const DEFAULT_TIMEOUT_MS = 10_000;
|
|
105
|
-
const SIGKILL_GRACE_MS = 2_000;
|
|
106
|
-
const STREAM_CAP_BYTES = 256 * 1024;
|
|
107
|
-
/**
|
|
108
|
-
* Resolve chain config from settings. Accepts both the canonical
|
|
109
|
-
* `{ enabled, entries }` shape and the shorthand array form the CEO
|
|
110
|
-
* spec example uses.
|
|
111
|
-
*/
|
|
112
|
-
export function resolveChain(settings, event) {
|
|
113
|
-
const rawHooks = settings.hooks;
|
|
114
|
-
const parsed = settingsHooksShape.safeParse(rawHooks);
|
|
115
|
-
if (!parsed.success || !parsed.data) {
|
|
116
|
-
return { enabled: true, entries: [] };
|
|
117
|
-
}
|
|
118
|
-
const raw = parsed.data[event];
|
|
119
|
-
if (!raw)
|
|
120
|
-
return { enabled: true, entries: [] };
|
|
121
|
-
if (Array.isArray(raw)) {
|
|
122
|
-
return { enabled: true, entries: raw };
|
|
123
|
-
}
|
|
124
|
-
return raw;
|
|
125
|
-
}
|
|
126
|
-
/**
|
|
127
|
-
* Parse the `hooks` section out of a raw settings JSON value. Exposed
|
|
128
|
-
* for tests and the settings reload path. Returns the normalised shape
|
|
129
|
-
* (both events present, canonical `{enabled, entries}` form) so the
|
|
130
|
-
* caller can assert structure without re-running Zod.
|
|
131
|
-
*/
|
|
132
|
-
export function parseHookChains(rawHooks) {
|
|
133
|
-
const parsed = settingsHooksShape.safeParse(rawHooks);
|
|
134
|
-
const out = {
|
|
135
|
-
PostToolUseFailure: { enabled: true, entries: [] },
|
|
136
|
-
TaskCompleted: { enabled: true, entries: [] },
|
|
137
|
-
};
|
|
138
|
-
if (!parsed.success || !parsed.data)
|
|
139
|
-
return out;
|
|
140
|
-
const pf = parsed.data.PostToolUseFailure;
|
|
141
|
-
if (pf) {
|
|
142
|
-
out.PostToolUseFailure = Array.isArray(pf) ? { enabled: true, entries: pf } : pf;
|
|
143
|
-
}
|
|
144
|
-
const tc = parsed.data.TaskCompleted;
|
|
145
|
-
if (tc) {
|
|
146
|
-
out.TaskCompleted = Array.isArray(tc) ? { enabled: true, entries: tc } : tc;
|
|
147
|
-
}
|
|
148
|
-
return out;
|
|
149
|
-
}
|
|
150
|
-
/**
|
|
151
|
-
* Decide whether a chain entry matches a `PostToolUseFailure` payload.
|
|
152
|
-
* An entry with no matcher (or only the empty object) fires on every
|
|
153
|
-
* failure of its event kind.
|
|
154
|
-
*/
|
|
155
|
-
function matchesPostToolUseFailure(entry, payload) {
|
|
156
|
-
const m = entry.matcher;
|
|
157
|
-
if (!m || (!m.tool && !m.command))
|
|
158
|
-
return true;
|
|
159
|
-
if (m.tool !== undefined && m.tool !== payload.toolName)
|
|
160
|
-
return false;
|
|
161
|
-
return true;
|
|
162
|
-
}
|
|
163
|
-
/**
|
|
164
|
-
* Decide whether a chain entry matches a `TaskCompleted` payload.
|
|
165
|
-
*/
|
|
166
|
-
function matchesTaskCompleted(entry, payload) {
|
|
167
|
-
const m = entry.matcher;
|
|
168
|
-
if (!m || (!m.tool && !m.command))
|
|
169
|
-
return true;
|
|
170
|
-
if (m.command !== undefined && m.command !== payload.command)
|
|
171
|
-
return false;
|
|
172
|
-
return true;
|
|
173
|
-
}
|
|
174
|
-
/**
|
|
175
|
-
* Fire the `PostToolUseFailure` chain. Best-effort: a hook crash, a
|
|
176
|
-
* spawn error, or a timeout never propagates back to the caller.
|
|
177
|
-
*
|
|
178
|
-
* Caller responsibility: the engine's tool-bridge invokes this AFTER
|
|
179
|
-
* the existing legacy `PostToolUseFailure` registry fire so legacy
|
|
180
|
-
* scripts and chain entries both run on the same failure (the legacy
|
|
181
|
-
* registry is the strict per-tool gate; chains are the fallback hook).
|
|
182
|
-
*/
|
|
183
|
-
export async function firePostToolUseFailureChain(workspaceRoot, payload, settingsOverride) {
|
|
184
|
-
const settings = settingsOverride ?? safeLoadSettings(workspaceRoot);
|
|
185
|
-
const config = resolveChain(settings, 'PostToolUseFailure');
|
|
186
|
-
if (!config.enabled) {
|
|
187
|
-
return { event: 'PostToolUseFailure', enabled: false, entries: [] };
|
|
188
|
-
}
|
|
189
|
-
const out = {
|
|
190
|
-
event: 'PostToolUseFailure',
|
|
191
|
-
enabled: true,
|
|
192
|
-
entries: [],
|
|
193
|
-
};
|
|
194
|
-
for (const entry of config.entries) {
|
|
195
|
-
const matched = matchesPostToolUseFailure(entry, payload);
|
|
196
|
-
if (!matched) {
|
|
197
|
-
out.entries.push({ matched: false, commands: [] });
|
|
198
|
-
continue;
|
|
199
|
-
}
|
|
200
|
-
const commands = await runChainEntry(entry, {
|
|
201
|
-
PUGI_HOOK_EVENT: 'PostToolUseFailure',
|
|
202
|
-
PUGI_HOOK_PAYLOAD: JSON.stringify(payload),
|
|
203
|
-
PUGI_HOOK_TOOL: payload.toolName,
|
|
204
|
-
PUGI_HOOK_EXIT_CODE: String(payload.exitCode),
|
|
205
|
-
});
|
|
206
|
-
out.entries.push({ matched: true, commands });
|
|
207
|
-
}
|
|
208
|
-
return out;
|
|
209
|
-
}
|
|
210
|
-
/**
|
|
211
|
-
* Fire the `TaskCompleted` chain. Same best-effort semantics as the
|
|
212
|
-
* PostToolUseFailure chain above. Caller invokes this at the dispatch
|
|
213
|
-
* exit in `native-pugi.ts` regardless of completion status (the
|
|
214
|
-
* payload carries `exitCode` so the hook can branch on success vs
|
|
215
|
-
* failure).
|
|
216
|
-
*/
|
|
217
|
-
export async function fireTaskCompletedChain(workspaceRoot, payload, settingsOverride) {
|
|
218
|
-
const settings = settingsOverride ?? safeLoadSettings(workspaceRoot);
|
|
219
|
-
const config = resolveChain(settings, 'TaskCompleted');
|
|
220
|
-
if (!config.enabled) {
|
|
221
|
-
return { event: 'TaskCompleted', enabled: false, entries: [] };
|
|
222
|
-
}
|
|
223
|
-
const out = {
|
|
224
|
-
event: 'TaskCompleted',
|
|
225
|
-
enabled: true,
|
|
226
|
-
entries: [],
|
|
227
|
-
};
|
|
228
|
-
for (const entry of config.entries) {
|
|
229
|
-
const matched = matchesTaskCompleted(entry, payload);
|
|
230
|
-
if (!matched) {
|
|
231
|
-
out.entries.push({ matched: false, commands: [] });
|
|
232
|
-
continue;
|
|
233
|
-
}
|
|
234
|
-
const commands = await runChainEntry(entry, {
|
|
235
|
-
PUGI_HOOK_EVENT: 'TaskCompleted',
|
|
236
|
-
PUGI_HOOK_PAYLOAD: JSON.stringify(payload),
|
|
237
|
-
PUGI_HOOK_COMMAND: payload.command,
|
|
238
|
-
PUGI_HOOK_EXIT_CODE: String(payload.exitCode),
|
|
239
|
-
PUGI_HOOK_DURATION_MS: String(payload.durationMs),
|
|
240
|
-
PUGI_HOOK_TOOL_CALLS: String(payload.toolCalls),
|
|
241
|
-
});
|
|
242
|
-
out.entries.push({ matched: true, commands });
|
|
243
|
-
}
|
|
244
|
-
return out;
|
|
245
|
-
}
|
|
246
|
-
/** Run every command in one chain entry sequentially. */
|
|
247
|
-
async function runChainEntry(entry, baseEnv) {
|
|
248
|
-
const timeoutMs = entry.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
249
|
-
const results = [];
|
|
250
|
-
for (const command of entry.run) {
|
|
251
|
-
try {
|
|
252
|
-
const result = await executeOne(command, timeoutMs, baseEnv);
|
|
253
|
-
results.push(result);
|
|
254
|
-
}
|
|
255
|
-
catch (error) {
|
|
256
|
-
// Spawn failure (binary missing, fork limit hit, etc). Swallow
|
|
257
|
-
// and record so the chain marches on to the next command.
|
|
258
|
-
results.push({
|
|
259
|
-
command,
|
|
260
|
-
exitCode: -1,
|
|
261
|
-
durationMs: 0,
|
|
262
|
-
stdout: '',
|
|
263
|
-
stderr: `chain spawn error: ${error.message}`,
|
|
264
|
-
timedOut: false,
|
|
265
|
-
});
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
return results;
|
|
269
|
-
}
|
|
270
|
-
/** Spawn ONE shell command and capture the result. */
|
|
271
|
-
function executeOne(command, timeoutMs, baseEnv) {
|
|
272
|
-
return new Promise((resolvePromise) => {
|
|
273
|
-
const startedAt = Date.now();
|
|
274
|
-
const child = spawn('/bin/sh', ['-c', command], {
|
|
275
|
-
env: { ...process.env, ...baseEnv },
|
|
276
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
277
|
-
});
|
|
278
|
-
let stdout = '';
|
|
279
|
-
let stderr = '';
|
|
280
|
-
let killedForTimeout = false;
|
|
281
|
-
let killedForStreamCap = false;
|
|
282
|
-
let sigKillTimer;
|
|
283
|
-
const enforceStreamCap = () => {
|
|
284
|
-
if (killedForStreamCap)
|
|
285
|
-
return;
|
|
286
|
-
if (stdout.length + stderr.length <= STREAM_CAP_BYTES)
|
|
287
|
-
return;
|
|
288
|
-
killedForStreamCap = true;
|
|
289
|
-
child.kill('SIGTERM');
|
|
290
|
-
if (!sigKillTimer) {
|
|
291
|
-
sigKillTimer = setTimeout(() => {
|
|
292
|
-
if (!child.killed)
|
|
293
|
-
child.kill('SIGKILL');
|
|
294
|
-
}, SIGKILL_GRACE_MS);
|
|
295
|
-
if (sigKillTimer.unref)
|
|
296
|
-
sigKillTimer.unref();
|
|
297
|
-
}
|
|
298
|
-
};
|
|
299
|
-
child.stdout?.on('data', (chunk) => {
|
|
300
|
-
if (killedForStreamCap)
|
|
301
|
-
return;
|
|
302
|
-
stdout += chunk.toString('utf8');
|
|
303
|
-
enforceStreamCap();
|
|
304
|
-
});
|
|
305
|
-
child.stderr?.on('data', (chunk) => {
|
|
306
|
-
if (killedForStreamCap)
|
|
307
|
-
return;
|
|
308
|
-
stderr += chunk.toString('utf8');
|
|
309
|
-
enforceStreamCap();
|
|
310
|
-
});
|
|
311
|
-
// Close stdin so commands that block on read (cat, jq) do not hang
|
|
312
|
-
// the chain. Errors are swallowed because the child may have
|
|
313
|
-
// already exited before we write.
|
|
314
|
-
if (child.stdin) {
|
|
315
|
-
child.stdin.on('error', () => {
|
|
316
|
-
/* ignore EPIPE */
|
|
317
|
-
});
|
|
318
|
-
child.stdin.end();
|
|
319
|
-
}
|
|
320
|
-
const timer = setTimeout(() => {
|
|
321
|
-
killedForTimeout = true;
|
|
322
|
-
child.kill('SIGTERM');
|
|
323
|
-
sigKillTimer = setTimeout(() => {
|
|
324
|
-
if (!child.killed)
|
|
325
|
-
child.kill('SIGKILL');
|
|
326
|
-
}, SIGKILL_GRACE_MS);
|
|
327
|
-
if (sigKillTimer.unref)
|
|
328
|
-
sigKillTimer.unref();
|
|
329
|
-
}, timeoutMs);
|
|
330
|
-
if (timer.unref)
|
|
331
|
-
timer.unref();
|
|
332
|
-
child.on('error', (error) => {
|
|
333
|
-
clearTimeout(timer);
|
|
334
|
-
if (sigKillTimer)
|
|
335
|
-
clearTimeout(sigKillTimer);
|
|
336
|
-
resolvePromise({
|
|
337
|
-
command,
|
|
338
|
-
exitCode: -1,
|
|
339
|
-
durationMs: Date.now() - startedAt,
|
|
340
|
-
stdout,
|
|
341
|
-
stderr: stderr || `hook spawn error: ${error.message}`,
|
|
342
|
-
timedOut: false,
|
|
343
|
-
});
|
|
344
|
-
});
|
|
345
|
-
child.on('close', (code, signal) => {
|
|
346
|
-
clearTimeout(timer);
|
|
347
|
-
if (sigKillTimer)
|
|
348
|
-
clearTimeout(sigKillTimer);
|
|
349
|
-
const durationMs = Date.now() - startedAt;
|
|
350
|
-
let exitCode;
|
|
351
|
-
if (code !== null) {
|
|
352
|
-
exitCode = code;
|
|
353
|
-
}
|
|
354
|
-
else if (signal === 'SIGTERM') {
|
|
355
|
-
exitCode = -15;
|
|
356
|
-
}
|
|
357
|
-
else if (signal === 'SIGKILL') {
|
|
358
|
-
exitCode = -9;
|
|
359
|
-
}
|
|
360
|
-
else {
|
|
361
|
-
exitCode = -1;
|
|
362
|
-
}
|
|
363
|
-
resolvePromise({
|
|
364
|
-
command,
|
|
365
|
-
exitCode,
|
|
366
|
-
durationMs,
|
|
367
|
-
stdout,
|
|
368
|
-
stderr,
|
|
369
|
-
timedOut: killedForTimeout || killedForStreamCap,
|
|
370
|
-
});
|
|
371
|
-
});
|
|
372
|
-
});
|
|
373
|
-
}
|
|
374
|
-
/**
|
|
375
|
-
* `loadSettings` throws on schema-invalid configs. Chains MUST NOT
|
|
376
|
-
* crash the dispatch — swallow + log to stderr so the caller proceeds
|
|
377
|
-
* with a default (no-hooks) settings object.
|
|
378
|
-
*/
|
|
379
|
-
function safeLoadSettings(root) {
|
|
380
|
-
try {
|
|
381
|
-
return loadSettings(root);
|
|
382
|
-
}
|
|
383
|
-
catch (error) {
|
|
384
|
-
process.stderr.write(`[pugi hook-chains] settings load failed: ${error.message}\n`);
|
|
385
|
-
// Build a minimal settings object that parses through the Zod
|
|
386
|
-
// schema. The shape is built by re-parsing an empty settings dir
|
|
387
|
-
// which `loadSettings` already does for missing files (returns
|
|
388
|
-
// the schema's default-applied object).
|
|
389
|
-
return loadSettings('/this-path-does-not-exist-fallback');
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
//# sourceMappingURL=hook-chains.js.map
|
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
import { promises as fs } from 'node:fs';
|
|
2
|
-
import * as path from 'node:path';
|
|
3
|
-
const DEFAULT_DESTRUCTIVE_RULES = [
|
|
4
|
-
{
|
|
5
|
-
pattern: /\bgit\s+push\s+(?:--force-with-lease|--force|-f)(?=\s|$)/i,
|
|
6
|
-
kind: 'force-push',
|
|
7
|
-
label: 'git push --force',
|
|
8
|
-
},
|
|
9
|
-
{
|
|
10
|
-
pattern: /\bgit\s+push\s+origin\s+(?:main|master)(?=\s|$)/i,
|
|
11
|
-
kind: 'origin-main-push',
|
|
12
|
-
label: 'git push origin main',
|
|
13
|
-
},
|
|
14
|
-
{ pattern: /\brm\s+-rf\b/i, kind: 'destructive-command', label: 'rm -rf' },
|
|
15
|
-
{ pattern: /\bsudo\s+rm\b/i, kind: 'destructive-command', label: 'sudo rm' },
|
|
16
|
-
{ pattern: /\bDROP\s+TABLE\b/i, kind: 'destructive-command', label: 'DROP TABLE' },
|
|
17
|
-
{ pattern: /\bDROP\s+DATABASE\b/i, kind: 'destructive-command', label: 'DROP DATABASE' },
|
|
18
|
-
];
|
|
19
|
-
const CITATION_RE = /\[([^\[\]\s:]+):(\d+)\]/g;
|
|
20
|
-
function lineColumnOf(text, index) {
|
|
21
|
-
let line = 1;
|
|
22
|
-
let column = 1;
|
|
23
|
-
for (let i = 0; i < index; i++) {
|
|
24
|
-
if (text.charCodeAt(i) === 10) {
|
|
25
|
-
line++;
|
|
26
|
-
column = 1;
|
|
27
|
-
}
|
|
28
|
-
else {
|
|
29
|
-
column++;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
return { line, column };
|
|
33
|
-
}
|
|
34
|
-
async function countLines(filePath) {
|
|
35
|
-
const data = await fs.readFile(filePath, 'utf8');
|
|
36
|
-
if (data.length === 0)
|
|
37
|
-
return 0;
|
|
38
|
-
const parts = data.split('\n');
|
|
39
|
-
if (parts[parts.length - 1] === '')
|
|
40
|
-
parts.pop();
|
|
41
|
-
return parts.length;
|
|
42
|
-
}
|
|
43
|
-
function withinWorkspace(workspaceRoot, candidate) {
|
|
44
|
-
if (path.isAbsolute(candidate))
|
|
45
|
-
return false;
|
|
46
|
-
const root = path.resolve(workspaceRoot);
|
|
47
|
-
const target = path.resolve(root, candidate);
|
|
48
|
-
const rel = path.relative(root, target);
|
|
49
|
-
if (rel === '')
|
|
50
|
-
return true;
|
|
51
|
-
return !rel.startsWith('..') && !path.isAbsolute(rel);
|
|
52
|
-
}
|
|
53
|
-
function ensureGlobal(pattern) {
|
|
54
|
-
if (pattern.flags.includes('g'))
|
|
55
|
-
return pattern;
|
|
56
|
-
return new RegExp(pattern.source, pattern.flags + 'g');
|
|
57
|
-
}
|
|
58
|
-
export async function verifyOutput(text, options) {
|
|
59
|
-
const signals = [];
|
|
60
|
-
const validateCitations = options.validateCitations !== false;
|
|
61
|
-
if (validateCitations) {
|
|
62
|
-
const citations = [];
|
|
63
|
-
for (const m of text.matchAll(CITATION_RE)) {
|
|
64
|
-
if (m.index === undefined)
|
|
65
|
-
continue;
|
|
66
|
-
citations.push({
|
|
67
|
-
raw: m[0],
|
|
68
|
-
filePath: m[1],
|
|
69
|
-
line: Number(m[2]),
|
|
70
|
-
index: m.index,
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
const invalidSignals = [];
|
|
74
|
-
for (const c of citations) {
|
|
75
|
-
const loc = lineColumnOf(text, c.index);
|
|
76
|
-
if (!withinWorkspace(options.workspaceRoot, c.filePath)) {
|
|
77
|
-
invalidSignals.push({
|
|
78
|
-
kind: 'invalid-citation',
|
|
79
|
-
detail: `${c.raw} resolves outside workspace`,
|
|
80
|
-
location: loc,
|
|
81
|
-
severity: 'warn',
|
|
82
|
-
});
|
|
83
|
-
continue;
|
|
84
|
-
}
|
|
85
|
-
const abs = path.resolve(options.workspaceRoot, c.filePath);
|
|
86
|
-
let lineCount;
|
|
87
|
-
try {
|
|
88
|
-
lineCount = await countLines(abs);
|
|
89
|
-
}
|
|
90
|
-
catch {
|
|
91
|
-
invalidSignals.push({
|
|
92
|
-
kind: 'invalid-citation',
|
|
93
|
-
detail: `${c.raw} file not found`,
|
|
94
|
-
location: loc,
|
|
95
|
-
severity: 'warn',
|
|
96
|
-
});
|
|
97
|
-
continue;
|
|
98
|
-
}
|
|
99
|
-
if (c.line < 1 || c.line > lineCount) {
|
|
100
|
-
invalidSignals.push({
|
|
101
|
-
kind: 'invalid-citation',
|
|
102
|
-
detail: `${c.raw} line ${c.line} out of range (file has ${lineCount} lines)`,
|
|
103
|
-
location: loc,
|
|
104
|
-
severity: 'warn',
|
|
105
|
-
});
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
if (citations.length > 0 && invalidSignals.length === citations.length) {
|
|
109
|
-
for (const s of invalidSignals)
|
|
110
|
-
s.severity = 'block';
|
|
111
|
-
}
|
|
112
|
-
signals.push(...invalidSignals);
|
|
113
|
-
}
|
|
114
|
-
const rules = options.allowedDestructivePatterns
|
|
115
|
-
? options.allowedDestructivePatterns.map((p) => ({
|
|
116
|
-
pattern: p,
|
|
117
|
-
kind: 'destructive-command',
|
|
118
|
-
label: p.source,
|
|
119
|
-
}))
|
|
120
|
-
: DEFAULT_DESTRUCTIVE_RULES;
|
|
121
|
-
for (const rule of rules) {
|
|
122
|
-
const re = ensureGlobal(rule.pattern);
|
|
123
|
-
for (const m of text.matchAll(re)) {
|
|
124
|
-
if (m.index === undefined)
|
|
125
|
-
continue;
|
|
126
|
-
const loc = lineColumnOf(text, m.index);
|
|
127
|
-
signals.push({
|
|
128
|
-
kind: rule.kind,
|
|
129
|
-
detail: `${rule.label}: ${m[0]}`,
|
|
130
|
-
location: loc,
|
|
131
|
-
severity: 'block',
|
|
132
|
-
});
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
const allowed = !signals.some((s) => s.severity === 'block');
|
|
136
|
-
return { allowed, signals };
|
|
137
|
-
}
|
|
138
|
-
//# sourceMappingURL=citation-verify-hook.js.map
|
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
import * as fs from 'node:fs/promises';
|
|
2
|
-
import * as path from 'node:path';
|
|
3
|
-
const CITATION_REGEX = /\[([^\]\s]+\.[a-zA-Z0-9]+):(\d+)(?:-(\d+))?(?:#([\w]+))?\]/g;
|
|
4
|
-
/**
|
|
5
|
-
* Parses citation-like strings from a block of text.
|
|
6
|
-
* @param text The text to parse.
|
|
7
|
-
* @returns An array of parsed CitationCheck objects.
|
|
8
|
-
*/
|
|
9
|
-
export function parseCitations(text) {
|
|
10
|
-
const matches = text.matchAll(CITATION_REGEX);
|
|
11
|
-
const citations = [];
|
|
12
|
-
for (const match of matches) {
|
|
13
|
-
const raw = match[0];
|
|
14
|
-
const path = match[1];
|
|
15
|
-
const lineStart = match[2];
|
|
16
|
-
const lineEnd = match[3];
|
|
17
|
-
const symbol = match[4];
|
|
18
|
-
if (!path || !lineStart)
|
|
19
|
-
continue;
|
|
20
|
-
citations.push({
|
|
21
|
-
raw,
|
|
22
|
-
path,
|
|
23
|
-
lineStart: parseInt(lineStart, 10),
|
|
24
|
-
lineEnd: lineEnd ? parseInt(lineEnd, 10) : undefined,
|
|
25
|
-
symbol: symbol || undefined,
|
|
26
|
-
status: 'valid',
|
|
27
|
-
});
|
|
28
|
-
}
|
|
29
|
-
return citations;
|
|
30
|
-
}
|
|
31
|
-
/**
|
|
32
|
-
* Parses citations from text and validates them against the filesystem.
|
|
33
|
-
* @param text The text containing citations.
|
|
34
|
-
* @param repoRoot The absolute path to the root of the repository.
|
|
35
|
-
* @returns A promise that resolves to an object containing valid and invalid citations,
|
|
36
|
-
* and the original text with all citation markers removed.
|
|
37
|
-
*/
|
|
38
|
-
export async function validateCitations(text, repoRoot) {
|
|
39
|
-
const citations = parseCitations(text);
|
|
40
|
-
const valid = [];
|
|
41
|
-
const invalid = [];
|
|
42
|
-
for (const citation of citations) {
|
|
43
|
-
const fullPath = path.join(repoRoot, citation.path);
|
|
44
|
-
let fileContent;
|
|
45
|
-
try {
|
|
46
|
-
await fs.stat(fullPath);
|
|
47
|
-
}
|
|
48
|
-
catch (error) {
|
|
49
|
-
if (error.code === 'ENOENT') {
|
|
50
|
-
citation.status = 'file_missing';
|
|
51
|
-
invalid.push(citation);
|
|
52
|
-
continue;
|
|
53
|
-
}
|
|
54
|
-
// Re-throw other errors
|
|
55
|
-
throw error;
|
|
56
|
-
}
|
|
57
|
-
try {
|
|
58
|
-
fileContent = await fs.readFile(fullPath, 'utf-8');
|
|
59
|
-
}
|
|
60
|
-
catch (e) {
|
|
61
|
-
citation.status = 'file_missing';
|
|
62
|
-
invalid.push(citation);
|
|
63
|
-
continue;
|
|
64
|
-
}
|
|
65
|
-
const lines = fileContent.split('\n');
|
|
66
|
-
const maxLines = lines.length;
|
|
67
|
-
if (citation.lineStart <= 0 || citation.lineStart > maxLines) {
|
|
68
|
-
citation.status = 'line_out_of_range';
|
|
69
|
-
invalid.push(citation);
|
|
70
|
-
continue;
|
|
71
|
-
}
|
|
72
|
-
if (citation.lineEnd && (citation.lineEnd > maxLines || citation.lineEnd < citation.lineStart)) {
|
|
73
|
-
citation.status = 'line_out_of_range';
|
|
74
|
-
invalid.push(citation);
|
|
75
|
-
continue;
|
|
76
|
-
}
|
|
77
|
-
// Symbol validation is not implemented in this version.
|
|
78
|
-
// We can add it here if needed by parsing the file content (e.g. with AST).
|
|
79
|
-
if (citation.symbol) {
|
|
80
|
-
citation.status = 'symbol_not_found'; // Placeholder for future implementation
|
|
81
|
-
}
|
|
82
|
-
// If we are here, file exists and line numbers are in range.
|
|
83
|
-
citation.status = 'valid';
|
|
84
|
-
valid.push(citation);
|
|
85
|
-
}
|
|
86
|
-
const cleaned = text.replace(CITATION_REGEX, '').trim();
|
|
87
|
-
return { valid, invalid, cleaned };
|
|
88
|
-
}
|
|
89
|
-
/**
|
|
90
|
-
* Checks if a given command string is potentially destructive.
|
|
91
|
-
* This is a safeguard and not exhaustive.
|
|
92
|
-
* @param cmd The command string to check.
|
|
93
|
-
* @returns An object indicating if the command is dangerous and why.
|
|
94
|
-
*/
|
|
95
|
-
export function isDestructiveGitCommand(cmd) {
|
|
96
|
-
const checks = [
|
|
97
|
-
{ re: /\bgit\s+push\s+(--force|-f)\b/, reason: '`git push --force` can overwrite remote history.' },
|
|
98
|
-
{ re: /\bgit\s+push\s+.*--force-with-lease.*\s(main|master)\b/, reason: '`git push --force-with-lease` on a main branch is risky.' },
|
|
99
|
-
{ re: /\bgit\s+reset\s+--hard\b/, reason: '`git reset --hard` discards all local changes and commits.' },
|
|
100
|
-
{ re: /\bgit\s+clean\s+-[dfx]+/, reason: '`git clean -fd` permanently deletes untracked files and directories.' },
|
|
101
|
-
{ re: /\brm\s+-rf\b/, reason: '`rm -rf` can recursively delete files and directories forcefully.' },
|
|
102
|
-
{ re: /\bDROP\s+DATABASE\b/i, reason: '`DROP DATABASE` permanently deletes an entire database.' },
|
|
103
|
-
{ re: /\bDROP\s+TABLE\b/i, reason: '`DROP TABLE` permanently deletes a table.' },
|
|
104
|
-
];
|
|
105
|
-
for (const check of checks) {
|
|
106
|
-
if (check.re.test(cmd)) {
|
|
107
|
-
return { dangerous: true, reason: check.reason };
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
return { dangerous: false };
|
|
111
|
-
}
|
|
112
|
-
//# sourceMappingURL=citation-verify.js.map
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Pugi hooks MVP — typed event payloads ().
|
|
3
|
-
*
|
|
4
|
-
* This module ships the MINIMAL FIRST PASS of the user-config hook
|
|
5
|
-
* matrix. Two lifecycle events out of the eventual 8 land here:
|
|
6
|
-
*
|
|
7
|
-
* - `SessionStart` — fired once when the REPL boots (`session.ts`).
|
|
8
|
-
* - `PreToolUse` — fired before each tool dispatch
|
|
9
|
-
* (`engine/tool-bridge.ts`). Non-zero exit from a
|
|
10
|
-
* hook with `blocking: true` aborts the dispatch.
|
|
11
|
-
*
|
|
12
|
-
* The remaining 6 events (`PostToolUse`, `UserPromptSubmit`, `Stop`,
|
|
13
|
-
* `SubagentStop`, `PreCompact`, `Notification`) are deferred to a
|
|
14
|
-
* fast-follow PR. The pattern established here — discriminated union
|
|
15
|
-
* payloads + registry-driven dispatch — is the reusable template.
|
|
16
|
-
*
|
|
17
|
-
* Design note (parallel surface): an older surface lives at
|
|
18
|
-
* `apps/pugi-cli/src/core/hooks.ts` and uses a flat-array `hooks: [{
|
|
19
|
-
* event, match, run }]` config shape with per-hook events. THIS module
|
|
20
|
-
* adopts the the upstream tool-style nested `hooks: { EventName: [{ matcher,
|
|
21
|
-
* command }] }` config shape. The two surfaces co-exist intentionally
|
|
22
|
-
* for the MVP — they read different files (`~/.pugi/hooks.json` vs.
|
|
23
|
-
* `~/.pugi/hooks-mvp.json`) so operator configs do not collide. The
|
|
24
|
-
* fast-follow PR consolidates the two readers.
|
|
25
|
-
*
|
|
26
|
-
* Brand voice: ASCII only, no emoji, no em-dashes, no marketing prose.
|
|
27
|
-
*/
|
|
28
|
-
/** Events the MVP actually fires. The remaining events live in the */
|
|
29
|
-
/** type but no integration point emits them yet. */
|
|
30
|
-
export const MVP_HOOK_EVENTS = [
|
|
31
|
-
'SessionStart',
|
|
32
|
-
'PreToolUse',
|
|
33
|
-
];
|
|
34
|
-
export const ALL_HOOK_EVENTS_V2 = [
|
|
35
|
-
'SessionStart',
|
|
36
|
-
'PreToolUse',
|
|
37
|
-
'PostToolUse',
|
|
38
|
-
'UserPromptSubmit',
|
|
39
|
-
'Stop',
|
|
40
|
-
'SubagentStop',
|
|
41
|
-
'PreCompact',
|
|
42
|
-
'Notification',
|
|
43
|
-
'WorktreeCreate',
|
|
44
|
-
'WorktreeRemove',
|
|
45
|
-
];
|
|
46
|
-
//# sourceMappingURL=events.js.map
|