@pugi/cli 0.1.0-beta.99 → 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 -195
- package/dist/core/engine/context-prefix.js +0 -155
- package/dist/core/engine/index.js +0 -12
- package/dist/core/engine/intensity.js +0 -163
- package/dist/core/engine/intent.js +0 -260
- package/dist/core/engine/native-pugi.js +0 -1616
- package/dist/core/engine/noop.js +0 -27
- package/dist/core/engine/prompts.js +0 -236
- package/dist/core/engine/strip-internal-fields.js +0 -124
- package/dist/core/engine/tool-bridge.js +0 -2173
- package/dist/core/engine/verification-patterns.js +0 -195
- package/dist/core/evaluation/golden-dataset.js +0 -293
- package/dist/core/feedback/queue.js +0 -177
- package/dist/core/feedback/submitter.js +0 -145
- package/dist/core/file-cache.js +0 -141
- package/dist/core/flatten/flatten-repo.js +0 -439
- package/dist/core/format/osc8-link.js +0 -28
- package/dist/core/hook-chains.js +0 -392
- package/dist/core/hooks/citation-verify-hook.js +0 -138
- package/dist/core/hooks/citation-verify.js +0 -112
- package/dist/core/hooks/events.js +0 -46
- package/dist/core/hooks/index.js +0 -15
- package/dist/core/hooks/registry.js +0 -216
- package/dist/core/hooks/runner.js +0 -236
- package/dist/core/hooks/v2/event-emitter.js +0 -115
- package/dist/core/hooks/v2/executor.js +0 -282
- package/dist/core/hooks/v2/index.js +0 -25
- package/dist/core/hooks/v2/lifecycle.js +0 -104
- package/dist/core/hooks/v2/loader.js +0 -216
- package/dist/core/hooks/v2/matcher.js +0 -125
- package/dist/core/hooks/v2/trust.js +0 -143
- package/dist/core/hooks/v2/types.js +0 -86
- package/dist/core/hooks/worktree-events.js +0 -158
- package/dist/core/hooks.js +0 -415
- package/dist/core/image/renderer.js +0 -71
- package/dist/core/index-store.js +0 -260
- package/dist/core/init/detector.js +0 -582
- package/dist/core/init/template-renderer.js +0 -242
- package/dist/core/jobs/registry.js +0 -462
- package/dist/core/ledger/results-tsv.js +0 -142
- package/dist/core/log-discipline/stdout-redirect.js +0 -51
- package/dist/core/lsp/cache.js +0 -105
- package/dist/core/lsp/client.js +0 -1229
- package/dist/core/lsp/language-detect.js +0 -66
- package/dist/core/lsp/post-edit-diagnostics.js +0 -171
- package/dist/core/lsp/server-detect.js +0 -173
- package/dist/core/lsp/symbol-cache.js +0 -162
- package/dist/core/lsp/symbol-tools.js +0 -664
- package/dist/core/mcp/client.js +0 -385
- package/dist/core/mcp/http-server.js +0 -553
- package/dist/core/mcp/orchestrator-config.js +0 -192
- package/dist/core/mcp/orchestrator-tools.js +0 -806
- package/dist/core/mcp/permission.js +0 -190
- package/dist/core/mcp/registry.js +0 -193
- package/dist/core/mcp/server-tools.js +0 -219
- package/dist/core/mcp/server.js +0 -397
- package/dist/core/mcp/trust.js +0 -91
- package/dist/core/memory/dual-write.js +0 -416
- package/dist/core/memory/passive-extract.js +0 -130
- package/dist/core/memory/phase1-kinds.js +0 -20
- package/dist/core/memory/secret-scanner.js +0 -304
- package/dist/core/memory-sync/queue.js +0 -170
- package/dist/core/metrics/extract.js +0 -113
- package/dist/core/modes/roo-modes.js +0 -68
- package/dist/core/onboarding/ensure-initialized.js +0 -133
- package/dist/core/onboarding/marker.js +0 -111
- package/dist/core/onboarding/telemetry-state.js +0 -108
- package/dist/core/output-style/presets.js +0 -176
- package/dist/core/output-style/state.js +0 -185
- package/dist/core/path-security.js +0 -345
- package/dist/core/permission.js +0 -369
- package/dist/core/permissions/auto-classifier.js +0 -124
- package/dist/core/permissions/bash-parser.js +0 -371
- package/dist/core/permissions/circuit-breaker.js +0 -83
- package/dist/core/permissions/constrained-edit.js +0 -91
- package/dist/core/permissions/gate.js +0 -278
- package/dist/core/permissions/index.js +0 -20
- package/dist/core/permissions/mode.js +0 -174
- package/dist/core/permissions/network-egress.js +0 -137
- package/dist/core/permissions/state.js +0 -241
- package/dist/core/permissions/tool-class.js +0 -107
- package/dist/core/plan-mode/ui-state.js +0 -51
- package/dist/core/plans/plan-artifact.js +0 -721
- package/dist/core/policy-limits/etag-store.js +0 -122
- package/dist/core/prd-check/parser.js +0 -215
- package/dist/core/prd-check/reporter.js +0 -127
- package/dist/core/prd-check/session-review.js +0 -557
- package/dist/core/prd-check/verifiers.js +0 -223
- package/dist/core/prompt-cache/client-cache.js +0 -99
- package/dist/core/prompts/assembly.js +0 -29
- package/dist/core/prompts/registry.js +0 -364
- package/dist/core/pugi-gitignore.js +0 -52
- package/dist/core/pugi-md/cc-compat-rules.js +0 -735
- package/dist/core/pugi-md/context-injector.js +0 -76
- package/dist/core/pugi-md/walk-up.js +0 -207
- package/dist/core/python/uv-installer.js +0 -270
- package/dist/core/python/uv-resolver.js +0 -83
- package/dist/core/rate-limit/narrator.js +0 -146
- package/dist/core/recipes/cli-types.js +0 -20
- package/dist/core/recipes/loader.js +0 -103
- package/dist/core/recipes/runner.js +0 -345
- package/dist/core/recipes/schema.js +0 -587
- package/dist/core/release-notes/parser.js +0 -241
- package/dist/core/release-notes/state.js +0 -116
- package/dist/core/repl/ask.js +0 -512
- package/dist/core/repl/cancellation.js +0 -98
- package/dist/core/repl/cap-warning.js +0 -91
- package/dist/core/repl/clipboard-read.js +0 -174
- package/dist/core/repl/dispatch-fsm.js +0 -220
- package/dist/core/repl/engine-bridge.js +0 -303
- package/dist/core/repl/history-search.js +0 -175
- package/dist/core/repl/history.js +0 -182
- package/dist/core/repl/kill-ring.js +0 -138
- package/dist/core/repl/model-pricing.js +0 -135
- package/dist/core/repl/privacy-banner.js +0 -71
- package/dist/core/repl/session.js +0 -4962
- package/dist/core/repl/slash-commands.js +0 -747
- package/dist/core/repl/store/index.js +0 -12
- package/dist/core/repl/store/jsonl-log.js +0 -321
- package/dist/core/repl/store/lockfile.js +0 -155
- package/dist/core/repl/store/session-store.js +0 -821
- package/dist/core/repl/store/types.js +0 -44
- package/dist/core/repl/store/uuid-v7.js +0 -68
- package/dist/core/repl/tool-route.js +0 -382
- package/dist/core/repl/workspace-context.js +0 -206
- package/dist/core/repo-map/build.js +0 -125
- package/dist/core/repo-map/cache.js +0 -185
- package/dist/core/repo-map/extractor.js +0 -254
- package/dist/core/repo-map/formatter.js +0 -145
- package/dist/core/repo-map/page-rank.js +0 -105
- package/dist/core/repo-map/scanner.js +0 -211
- package/dist/core/retro/git-collector.js +0 -251
- package/dist/core/retro/health-card.js +0 -25
- package/dist/core/retro/metrics.js +0 -342
- package/dist/core/retro/narrative.js +0 -249
- package/dist/core/retro/plane-collector.js +0 -274
- package/dist/core/retro/pr-issue-link.js +0 -65
- package/dist/core/retro/types.js +0 -16
- package/dist/core/retry-budget/budget.js +0 -284
- package/dist/core/retry-budget/index.js +0 -5
- package/dist/core/retry-budget/retry-cap.js +0 -74
- package/dist/core/routing/lead-worker.js +0 -43
- package/dist/core/routing/pre-flight-estimator.js +0 -108
- package/dist/core/runs/run-tree.js +0 -103
- package/dist/core/sandboxing/adapter.js +0 -29
- package/dist/core/sandboxing/index.js +0 -49
- package/dist/core/sandboxing/none.js +0 -19
- package/dist/core/sandboxing/seatbelt.js +0 -183
- package/dist/core/security/injection-scanner.js +0 -367
- package/dist/core/security/output-filter.js +0 -418
- package/dist/core/session/env-file.js +0 -105
- package/dist/core/session/section-budgets.js +0 -140
- package/dist/core/session.js +0 -377
- package/dist/core/settings.js +0 -400
- package/dist/core/share/formatter.js +0 -271
- package/dist/core/share/redactor.js +0 -221
- package/dist/core/share/uploader.js +0 -267
- package/dist/core/skills/defaults.js +0 -457
- package/dist/core/skills/loader.js +0 -454
- package/dist/core/skills/sources.js +0 -480
- package/dist/core/skills/trust.js +0 -172
- package/dist/core/smoke/headless-driver.js +0 -174
- package/dist/core/smoke/orchestrator.js +0 -194
- package/dist/core/smoke/runner.js +0 -238
- package/dist/core/smoke/scenario-parser.js +0 -316
- package/dist/core/statusline.js +0 -99
- package/dist/core/subagents/dispatcher-real.js +0 -600
- package/dist/core/subagents/dispatcher.js +0 -352
- package/dist/core/subagents/index.js +0 -39
- package/dist/core/subagents/isolation-matrix.js +0 -213
- package/dist/core/subagents/spawn.js +0 -101
- package/dist/core/telemetry/emitter.js +0 -229
- package/dist/core/telemetry/queue.js +0 -251
- package/dist/core/theme/context.js +0 -91
- package/dist/core/theme/presets.js +0 -228
- package/dist/core/theme/state.js +0 -181
- package/dist/core/todos/invariant.js +0 -10
- package/dist/core/todos/state.js +0 -177
- package/dist/core/tool-schema/compressor.js +0 -89
- package/dist/core/transport/version-interceptor.js +0 -166
- package/dist/core/trust.js +0 -109
- package/dist/core/tui/thinking-block.js +0 -64
- package/dist/core/vim/keymap.js +0 -288
- package/dist/core/vim/state.js +0 -92
- package/dist/core/watch-markers/marker-watcher.js +0 -133
- package/dist/core/worktree/include-parser.js +0 -249
- package/dist/core/worktree-manager/cleanup.js +0 -123
- package/dist/core/worktree-manager/manager.js +0 -303
- package/dist/index.js +0 -44
- package/dist/runtime/bootstrap.js +0 -190
- package/dist/runtime/cli.js +0 -8121
- package/dist/runtime/commands/agents.js +0 -385
- package/dist/runtime/commands/budget.js +0 -192
- package/dist/runtime/commands/cancel.js +0 -231
- package/dist/runtime/commands/chain.js +0 -489
- package/dist/runtime/commands/codegraph-status.js +0 -227
- package/dist/runtime/commands/compact.js +0 -297
- package/dist/runtime/commands/config.js +0 -595
- package/dist/runtime/commands/cost.js +0 -199
- package/dist/runtime/commands/delegate.js +0 -312
- package/dist/runtime/commands/dispatch.js +0 -126
- package/dist/runtime/commands/doctor.js +0 -579
- package/dist/runtime/commands/feedback.js +0 -184
- package/dist/runtime/commands/hooks.js +0 -187
- package/dist/runtime/commands/init.js +0 -254
- package/dist/runtime/commands/lsp.js +0 -368
- package/dist/runtime/commands/mcp.js +0 -935
- package/dist/runtime/commands/memory.js +0 -582
- package/dist/runtime/commands/model.js +0 -237
- package/dist/runtime/commands/onboarding.js +0 -275
- package/dist/runtime/commands/patch.js +0 -128
- package/dist/runtime/commands/permissions.js +0 -112
- package/dist/runtime/commands/plan.js +0 -143
- package/dist/runtime/commands/prd-check.js +0 -285
- package/dist/runtime/commands/privacy.js +0 -107
- package/dist/runtime/commands/recipe.js +0 -325
- package/dist/runtime/commands/redo-blob-store.js +0 -92
- package/dist/runtime/commands/redo.js +0 -361
- package/dist/runtime/commands/release-notes.js +0 -229
- package/dist/runtime/commands/repo-map.js +0 -95
- package/dist/runtime/commands/report.js +0 -299
- package/dist/runtime/commands/resume.js +0 -118
- package/dist/runtime/commands/review-consensus.js +0 -414
- package/dist/runtime/commands/rewind.js +0 -333
- package/dist/runtime/commands/roster.js +0 -117
- package/dist/runtime/commands/sessions.js +0 -163
- package/dist/runtime/commands/share.js +0 -316
- package/dist/runtime/commands/skills.js +0 -401
- package/dist/runtime/commands/status.js +0 -186
- package/dist/runtime/commands/stickers.js +0 -82
- package/dist/runtime/commands/style.js +0 -194
- package/dist/runtime/commands/theme.js +0 -196
- package/dist/runtime/commands/undo.js +0 -361
- package/dist/runtime/commands/update.js +0 -289
- package/dist/runtime/commands/vim.js +0 -140
- package/dist/runtime/commands/worktree.js +0 -177
- package/dist/runtime/commands/worktrees.js +0 -155
- package/dist/runtime/deprecation-warning.js +0 -69
- package/dist/runtime/engine-exit-code.js +0 -50
- package/dist/runtime/headless-repl.js +0 -195
- package/dist/runtime/headless.js +0 -548
- package/dist/runtime/load-hooks-or-exit.js +0 -71
- package/dist/runtime/plan-decompose.js +0 -531
- package/dist/runtime/sigint-guard.js +0 -272
- package/dist/runtime/stream-renderer.js +0 -195
- package/dist/runtime/update-check.js +0 -294
- package/dist/runtime/version.js +0 -65
- package/dist/runtime/worktree-bootstrap.js +0 -579
- package/dist/skills/bundled/batch.js +0 -617
- package/dist/skills/bundled/index.js +0 -45
- package/dist/skills/bundled/loop.js +0 -358
- package/dist/skills/bundled/remember.js +0 -383
- package/dist/skills/bundled/simplify.js +0 -289
- package/dist/skills/bundled/skillify.js +0 -373
- package/dist/skills/bundled/stuck.js +0 -558
- package/dist/skills/bundled/verify.js +0 -439
- package/dist/testing/vcr.js +0 -486
- package/dist/tools/agent-tool.js +0 -229
- package/dist/tools/apply-patch.js +0 -556
- package/dist/tools/ask-user-question.js +0 -337
- package/dist/tools/ask-user.js +0 -115
- package/dist/tools/bash.js +0 -1238
- package/dist/tools/brief.js +0 -224
- package/dist/tools/cron.js +0 -433
- package/dist/tools/enter-worktree.js +0 -250
- package/dist/tools/exit-worktree.js +0 -147
- package/dist/tools/file-tools.js +0 -553
- package/dist/tools/http-request.js +0 -336
- package/dist/tools/lsp-tools.js +0 -565
- package/dist/tools/mcp-tool.js +0 -260
- package/dist/tools/multi-edit.js +0 -361
- package/dist/tools/powershell.js +0 -268
- package/dist/tools/registry.js +0 -166
- package/dist/tools/server-tools.js +0 -892
- package/dist/tools/skill-tool.js +0 -96
- package/dist/tools/sleep.js +0 -99
- package/dist/tools/synthetic-output.js +0 -133
- package/dist/tools/tasks.js +0 -208
- package/dist/tools/todo-write.js +0 -184
- package/dist/tools/verify-plan-execution.js +0 -295
- package/dist/tools/web-fetch-injection-scanner.js +0 -207
- package/dist/tools/web-fetch.js +0 -720
- package/dist/tools/web-search.js +0 -458
- package/dist/tui/agent-progress-card.js +0 -111
- package/dist/tui/agent-tree-pane.js +0 -9
- package/dist/tui/agent-tree.js +0 -87
- package/dist/tui/ask-cli.js +0 -52
- package/dist/tui/ask-modal.js +0 -211
- package/dist/tui/ask-user-question-chips.js +0 -315
- package/dist/tui/ask-user-question-prompt.js +0 -203
- package/dist/tui/compact-banner.js +0 -81
- package/dist/tui/conversation-pane.js +0 -164
- package/dist/tui/cost-table.js +0 -111
- package/dist/tui/device-flow.js +0 -142
- package/dist/tui/doctor-table.js +0 -46
- package/dist/tui/feedback-prompt.js +0 -156
- package/dist/tui/input-box.js +0 -732
- package/dist/tui/login-picker.js +0 -69
- package/dist/tui/markdown-render.js +0 -266
- package/dist/tui/multi-file-diff-approval.js +0 -375
- package/dist/tui/onboarding-wizard.js +0 -240
- package/dist/tui/permissions-picker.js +0 -86
- package/dist/tui/render.js +0 -160
- package/dist/tui/repl-render.js +0 -770
- package/dist/tui/repl-splash-art.js +0 -64
- package/dist/tui/repl-splash-mascot.js +0 -154
- package/dist/tui/repl-splash.js +0 -117
- package/dist/tui/repl.js +0 -378
- package/dist/tui/slash-palette.js +0 -106
- package/dist/tui/splash-data.js +0 -61
- package/dist/tui/splash.js +0 -31
- package/dist/tui/status-bar.js +0 -209
- package/dist/tui/status-table.js +0 -7
- package/dist/tui/stickers-art.js +0 -136
- package/dist/tui/style-table.js +0 -28
- package/dist/tui/theme-table.js +0 -29
- package/dist/tui/thinking-spinner.js +0 -123
- package/dist/tui/tool-stream-pane.js +0 -140
- package/dist/tui/update-banner.js +0 -33
- package/dist/tui/vim-input.js +0 -267
- package/dist/tui/welcome-banner.js +0 -107
- package/dist/tui/welcome-data.js +0 -293
- package/dist/tui/workspace-context.js +0 -105
- package/docs/examples/codegraph.mcp.json +0 -10
- package/test/scenarios/codegen-create-file.scenario.txt +0 -13
- package/test/scenarios/compact-force.scenario.txt +0 -12
- package/test/scenarios/identity.scenario.txt +0 -11
- package/test/scenarios/persona-handoff.scenario.txt +0 -12
- package/test/scenarios/walkback.scenario.txt +0 -12
|
@@ -1,617 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `pugi batch` — fan-out multi-step parallel execution scaffold.
|
|
3
|
-
*
|
|
4
|
-
* Bundled skill, batch 2 (backlog). The operator describes N
|
|
5
|
-
* independent engine tasks in a YAML recipe; this skill spawns up to
|
|
6
|
-
* `--concurrency=N` subprocesses, each running `pugi --print "<prompt>"`
|
|
7
|
-
* with the configured model / max-turns / timeout, captures their
|
|
8
|
-
* stdout to per-task log files under `<repo>/.pugi/batch/<batch-id>/`,
|
|
9
|
-
* and aggregates the verdicts into a JSONL ledger plus a summary
|
|
10
|
-
* table.
|
|
11
|
-
*
|
|
12
|
-
* # Recipe schema (YAML)
|
|
13
|
-
*
|
|
14
|
-
* ```yaml
|
|
15
|
-
* concurrency: 5 # optional, defaults to 5, hard cap 30
|
|
16
|
-
* tasks:
|
|
17
|
-
* - id: scaffold-button
|
|
18
|
-
* prompt: "Scaffold a shadcn button at apps/console-web/src/components/button.tsx"
|
|
19
|
-
* model: claude-sonnet-4-5
|
|
20
|
-
* maxTurns: 8
|
|
21
|
-
* timeoutMs: 600000
|
|
22
|
-
* - id: scaffold-input
|
|
23
|
-
* prompt: "Scaffold a shadcn input at apps/console-web/src/components/input.tsx"
|
|
24
|
-
* ```
|
|
25
|
-
*
|
|
26
|
-
* # Hard cap on concurrency
|
|
27
|
-
*
|
|
28
|
-
* The CEO memory `feedback_max_3_parallel_agents_mac_safety.md` caps
|
|
29
|
-
* unscoped parallel agent spawns at 3 because the Mac kernel-paniced
|
|
30
|
-
* during a 5+ run on. The operator explicitly OK'd higher
|
|
31
|
-
* caps for worktree-isolated batches — this skill enforces a HARD
|
|
32
|
-
* upper bound of 30 to stay well below the regression threshold while
|
|
33
|
-
* letting power-users fan out.
|
|
34
|
-
*
|
|
35
|
-
* # Worktree isolation (deferred)
|
|
36
|
-
*
|
|
37
|
-
* True per-task worktrees are backlog. Today every subprocess
|
|
38
|
-
* runs in the operator's current working directory and relies on its
|
|
39
|
-
* own scratch files to avoid stepping on siblings. The TODO marker is
|
|
40
|
-
* preserved so the upgrade path is unambiguous.
|
|
41
|
-
*
|
|
42
|
-
* # Provenance
|
|
43
|
-
*
|
|
44
|
-
* Inspired by the external bundled-skills pattern (intel from
|
|
45
|
-
* leak-research memos, independent implementation TS). No upstream code reused.
|
|
46
|
-
*/
|
|
47
|
-
import { spawn } from 'node:child_process';
|
|
48
|
-
import { existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync, } from 'node:fs';
|
|
49
|
-
import { dirname, isAbsolute, join, resolve } from 'node:path';
|
|
50
|
-
/**
|
|
51
|
-
* Default subprocess concurrency. Five is the sweet spot on a 16 GB
|
|
52
|
-
* Mac with the Anvil-routed CLI — each task averages ~600 MB resident
|
|
53
|
-
* during the engine turn, leaving headroom for the operator's REPL.
|
|
54
|
-
*/
|
|
55
|
-
const DEFAULT_CONCURRENCY = 5;
|
|
56
|
-
/**
|
|
57
|
-
* Hard upper bound on concurrency. Operator override stops at 30 per
|
|
58
|
-
* the worktree-isolated batch carve-out in the CEO memory. Beyond 30
|
|
59
|
-
* we have observed kernel-level pressure on Apple Silicon — even with
|
|
60
|
-
* worktree isolation, the model gateway becomes the bottleneck and
|
|
61
|
-
* the marginal value vanishes.
|
|
62
|
-
*/
|
|
63
|
-
const MAX_CONCURRENCY = 30;
|
|
64
|
-
/**
|
|
65
|
-
* Default per-task wall-clock ceiling. Ten minutes is enough for a
|
|
66
|
-
* non-trivial engine turn (multi-tool, several file edits) while
|
|
67
|
-
* keeping a wedged subprocess from hanging the whole batch forever.
|
|
68
|
-
*/
|
|
69
|
-
const DEFAULT_TASK_TIMEOUT_MS = 10 * 60 * 1000;
|
|
70
|
-
function parseFlags(args) {
|
|
71
|
-
const flags = {
|
|
72
|
-
json: false,
|
|
73
|
-
concurrency: null,
|
|
74
|
-
recipePath: null,
|
|
75
|
-
dryRun: false,
|
|
76
|
-
};
|
|
77
|
-
for (let i = 0; i < args.length; i += 1) {
|
|
78
|
-
const arg = args[i];
|
|
79
|
-
if (arg === undefined)
|
|
80
|
-
continue;
|
|
81
|
-
if (arg === '--json') {
|
|
82
|
-
flags.json = true;
|
|
83
|
-
continue;
|
|
84
|
-
}
|
|
85
|
-
if (arg === '--dry-run') {
|
|
86
|
-
flags.dryRun = true;
|
|
87
|
-
continue;
|
|
88
|
-
}
|
|
89
|
-
if (arg === '--concurrency') {
|
|
90
|
-
const next = args[i + 1];
|
|
91
|
-
if (next === undefined)
|
|
92
|
-
return { flags, error: '--concurrency requires a value' };
|
|
93
|
-
const parsed = Number.parseInt(next, 10);
|
|
94
|
-
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
95
|
-
return { flags, error: '--concurrency must be a positive integer' };
|
|
96
|
-
}
|
|
97
|
-
if (parsed > MAX_CONCURRENCY) {
|
|
98
|
-
return {
|
|
99
|
-
flags,
|
|
100
|
-
error: `--concurrency must be <= ${MAX_CONCURRENCY} (Mac safety cap)`,
|
|
101
|
-
};
|
|
102
|
-
}
|
|
103
|
-
flags.concurrency = parsed;
|
|
104
|
-
i += 1;
|
|
105
|
-
continue;
|
|
106
|
-
}
|
|
107
|
-
if (arg === '--help' || arg === '-h') {
|
|
108
|
-
return { flags, error: 'help' };
|
|
109
|
-
}
|
|
110
|
-
if (arg.startsWith('--')) {
|
|
111
|
-
return { flags, error: `unknown flag: ${arg}` };
|
|
112
|
-
}
|
|
113
|
-
if (flags.recipePath === null) {
|
|
114
|
-
flags.recipePath = arg;
|
|
115
|
-
continue;
|
|
116
|
-
}
|
|
117
|
-
return { flags, error: `unexpected positional argument: ${arg}` };
|
|
118
|
-
}
|
|
119
|
-
return { flags, error: null };
|
|
120
|
-
}
|
|
121
|
-
/**
|
|
122
|
-
* Minimal YAML parser tuned for the batch recipe shape. We deliberately
|
|
123
|
-
* do NOT pull in a real YAML dep — the recipe surface is a tightly
|
|
124
|
-
* constrained subset (top-level `concurrency:` scalar + `tasks:` list of
|
|
125
|
-
* mappings with scalar fields), and a 60-line parser keeps the bundled
|
|
126
|
-
* skill dep-free. Anything fancier raises a parse error so the operator
|
|
127
|
-
* is forced into the supported shape.
|
|
128
|
-
*
|
|
129
|
-
* Public for direct test coverage.
|
|
130
|
-
*/
|
|
131
|
-
export function parseBatchRecipe(text) {
|
|
132
|
-
const lines = text.split('\n');
|
|
133
|
-
let concurrency = DEFAULT_CONCURRENCY;
|
|
134
|
-
const tasks = [];
|
|
135
|
-
let i = 0;
|
|
136
|
-
let inTasks = false;
|
|
137
|
-
// current task accumulator
|
|
138
|
-
let cur = null;
|
|
139
|
-
const flushTask = () => {
|
|
140
|
-
if (cur === null)
|
|
141
|
-
return null;
|
|
142
|
-
if (cur.id === undefined || cur.id === '') {
|
|
143
|
-
return 'task missing required `id`';
|
|
144
|
-
}
|
|
145
|
-
if (!/^[a-zA-Z0-9_-]+$/.test(cur.id)) {
|
|
146
|
-
return `task id '${cur.id}' must match [a-zA-Z0-9_-]+`;
|
|
147
|
-
}
|
|
148
|
-
if (cur.prompt === undefined || cur.prompt === '') {
|
|
149
|
-
return `task '${cur.id}' missing required \`prompt\``;
|
|
150
|
-
}
|
|
151
|
-
const spec = {
|
|
152
|
-
id: cur.id,
|
|
153
|
-
prompt: cur.prompt,
|
|
154
|
-
...(cur.model !== undefined ? { model: cur.model } : {}),
|
|
155
|
-
...(cur.maxTurns !== undefined ? { maxTurns: cur.maxTurns } : {}),
|
|
156
|
-
...(cur.timeoutMs !== undefined ? { timeoutMs: cur.timeoutMs } : {}),
|
|
157
|
-
};
|
|
158
|
-
tasks.push(spec);
|
|
159
|
-
cur = null;
|
|
160
|
-
return null;
|
|
161
|
-
};
|
|
162
|
-
while (i < lines.length) {
|
|
163
|
-
const raw = lines[i] ?? '';
|
|
164
|
-
i += 1;
|
|
165
|
-
// strip trailing CR + trailing inline comment (only when preceded by space)
|
|
166
|
-
const stripped = raw.replace(/\r$/, '').replace(/\s+#.*$/, '');
|
|
167
|
-
if (stripped.trim() === '')
|
|
168
|
-
continue;
|
|
169
|
-
// Top-level `concurrency: N`
|
|
170
|
-
const concMatch = /^concurrency:\s*(\d+)\s*$/.exec(stripped);
|
|
171
|
-
if (concMatch && !inTasks) {
|
|
172
|
-
const parsed = Number.parseInt(concMatch[1] ?? '', 10);
|
|
173
|
-
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
174
|
-
return { ok: false, error: 'concurrency must be a positive integer' };
|
|
175
|
-
}
|
|
176
|
-
if (parsed > MAX_CONCURRENCY) {
|
|
177
|
-
return {
|
|
178
|
-
ok: false,
|
|
179
|
-
error: `concurrency must be <= ${MAX_CONCURRENCY}`,
|
|
180
|
-
};
|
|
181
|
-
}
|
|
182
|
-
concurrency = parsed;
|
|
183
|
-
continue;
|
|
184
|
-
}
|
|
185
|
-
if (/^tasks:\s*$/.test(stripped)) {
|
|
186
|
-
inTasks = true;
|
|
187
|
-
continue;
|
|
188
|
-
}
|
|
189
|
-
if (!inTasks) {
|
|
190
|
-
return { ok: false, error: `unexpected top-level line: ${stripped}` };
|
|
191
|
-
}
|
|
192
|
-
// task entry start: " - id: <value>" or " - <field>: <value>" on the
|
|
193
|
-
// same line as the dash.
|
|
194
|
-
const dashMatch = /^(\s*)-\s+(.*)$/.exec(stripped);
|
|
195
|
-
if (dashMatch) {
|
|
196
|
-
const flushErr = flushTask();
|
|
197
|
-
if (flushErr)
|
|
198
|
-
return { ok: false, error: flushErr };
|
|
199
|
-
cur = {};
|
|
200
|
-
// The dash line itself may already carry a field.
|
|
201
|
-
const tail = dashMatch[2] ?? '';
|
|
202
|
-
const fieldOnDash = /^([a-zA-Z]+):\s*(.*)$/.exec(tail);
|
|
203
|
-
if (fieldOnDash) {
|
|
204
|
-
const field = fieldOnDash[1] ?? '';
|
|
205
|
-
const value = fieldOnDash[2] ?? '';
|
|
206
|
-
const assignErr = assignField(cur, field, value);
|
|
207
|
-
if (assignErr)
|
|
208
|
-
return { ok: false, error: assignErr };
|
|
209
|
-
}
|
|
210
|
-
else if (tail.trim() !== '') {
|
|
211
|
-
return { ok: false, error: `unexpected task-list entry: ${stripped}` };
|
|
212
|
-
}
|
|
213
|
-
continue;
|
|
214
|
-
}
|
|
215
|
-
// Continuation field inside the current task: " <field>: <value>"
|
|
216
|
-
const fieldMatch = /^(\s+)([a-zA-Z]+):\s*(.*)$/.exec(stripped);
|
|
217
|
-
if (fieldMatch && cur !== null) {
|
|
218
|
-
const field = fieldMatch[2] ?? '';
|
|
219
|
-
const value = fieldMatch[3] ?? '';
|
|
220
|
-
const assignErr = assignField(cur, field, value);
|
|
221
|
-
if (assignErr)
|
|
222
|
-
return { ok: false, error: assignErr };
|
|
223
|
-
continue;
|
|
224
|
-
}
|
|
225
|
-
return { ok: false, error: `cannot parse line: ${stripped}` };
|
|
226
|
-
}
|
|
227
|
-
const flushErr = flushTask();
|
|
228
|
-
if (flushErr)
|
|
229
|
-
return { ok: false, error: flushErr };
|
|
230
|
-
if (tasks.length === 0) {
|
|
231
|
-
return { ok: false, error: 'recipe declares zero tasks' };
|
|
232
|
-
}
|
|
233
|
-
return {
|
|
234
|
-
ok: true,
|
|
235
|
-
recipe: { concurrency, tasks },
|
|
236
|
-
};
|
|
237
|
-
}
|
|
238
|
-
function assignField(target, field, rawValue) {
|
|
239
|
-
// strip surrounding quotes for scalar string values
|
|
240
|
-
const stripQuotes = (s) => {
|
|
241
|
-
const trimmed = s.trim();
|
|
242
|
-
if ((trimmed.startsWith('"') && trimmed.endsWith('"')) ||
|
|
243
|
-
(trimmed.startsWith("'") && trimmed.endsWith("'"))) {
|
|
244
|
-
return trimmed.slice(1, -1);
|
|
245
|
-
}
|
|
246
|
-
return trimmed;
|
|
247
|
-
};
|
|
248
|
-
switch (field) {
|
|
249
|
-
case 'id':
|
|
250
|
-
target.id = stripQuotes(rawValue);
|
|
251
|
-
return null;
|
|
252
|
-
case 'prompt':
|
|
253
|
-
target.prompt = stripQuotes(rawValue);
|
|
254
|
-
return null;
|
|
255
|
-
case 'model':
|
|
256
|
-
target.model = stripQuotes(rawValue);
|
|
257
|
-
return null;
|
|
258
|
-
case 'maxTurns': {
|
|
259
|
-
const parsed = Number.parseInt(rawValue.trim(), 10);
|
|
260
|
-
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
261
|
-
return `maxTurns must be a positive integer (got '${rawValue}')`;
|
|
262
|
-
}
|
|
263
|
-
target.maxTurns = parsed;
|
|
264
|
-
return null;
|
|
265
|
-
}
|
|
266
|
-
case 'timeoutMs': {
|
|
267
|
-
const parsed = Number.parseInt(rawValue.trim(), 10);
|
|
268
|
-
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
269
|
-
return `timeoutMs must be a positive integer (got '${rawValue}')`;
|
|
270
|
-
}
|
|
271
|
-
target.timeoutMs = parsed;
|
|
272
|
-
return null;
|
|
273
|
-
}
|
|
274
|
-
default:
|
|
275
|
-
return `unknown task field '${field}'`;
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
/**
|
|
279
|
-
* Default subprocess runner — shells out to the same `pugi` binary that
|
|
280
|
-
* invoked us via `--print "<prompt>"`. Tests inject their own runner so
|
|
281
|
-
* the spec suite never spawns real subprocesses.
|
|
282
|
-
*
|
|
283
|
-
* Public so the dispatcher can pass a custom invocation when a session
|
|
284
|
-
* wants to point at a wrapper binary (e.g. the dev-mode `tsx src/index.ts`).
|
|
285
|
-
*/
|
|
286
|
-
export function spawnPugiSubprocess(task, ctx) {
|
|
287
|
-
return new Promise((resolveTask) => {
|
|
288
|
-
const startedAt = Date.now();
|
|
289
|
-
const timeoutMs = task.timeoutMs ?? DEFAULT_TASK_TIMEOUT_MS;
|
|
290
|
-
const args = ['--print', task.prompt];
|
|
291
|
-
if (task.maxTurns !== undefined) {
|
|
292
|
-
args.unshift('--max-turns', String(task.maxTurns));
|
|
293
|
-
}
|
|
294
|
-
if (task.model !== undefined) {
|
|
295
|
-
args.unshift('--model', task.model);
|
|
296
|
-
}
|
|
297
|
-
const stdoutChunks = [];
|
|
298
|
-
const stderrChunks = [];
|
|
299
|
-
// TODO worktree-isolate per — currently every task spawns in
|
|
300
|
-
// the operator cwd; full per-task worktree comes when the worktree
|
|
301
|
-
// manager exposes a public batch-friendly factory.
|
|
302
|
-
const child = spawn('pugi', args, {
|
|
303
|
-
cwd: ctx.cwd,
|
|
304
|
-
env: ctx.env,
|
|
305
|
-
stdio: ['ignore', 'pipe', 'pipe'],
|
|
306
|
-
});
|
|
307
|
-
let timer = setTimeout(() => {
|
|
308
|
-
timer = null;
|
|
309
|
-
try {
|
|
310
|
-
child.kill('SIGKILL');
|
|
311
|
-
}
|
|
312
|
-
catch {
|
|
313
|
-
// best-effort
|
|
314
|
-
}
|
|
315
|
-
}, timeoutMs);
|
|
316
|
-
child.stdout.setEncoding('utf8');
|
|
317
|
-
child.stdout.on('data', (chunk) => stdoutChunks.push(chunk));
|
|
318
|
-
child.stderr.setEncoding('utf8');
|
|
319
|
-
child.stderr.on('data', (chunk) => stderrChunks.push(chunk));
|
|
320
|
-
child.on('error', (err) => {
|
|
321
|
-
if (timer !== null) {
|
|
322
|
-
clearTimeout(timer);
|
|
323
|
-
timer = null;
|
|
324
|
-
}
|
|
325
|
-
resolveTask({
|
|
326
|
-
status: 'failed',
|
|
327
|
-
exitCode: null,
|
|
328
|
-
stdout: stdoutChunks.join(''),
|
|
329
|
-
stderr: `spawn error: ${err.message}\n${stderrChunks.join('')}`,
|
|
330
|
-
durationMs: Date.now() - startedAt,
|
|
331
|
-
});
|
|
332
|
-
});
|
|
333
|
-
child.on('exit', (code, signal) => {
|
|
334
|
-
const killedByTimeout = timer === null;
|
|
335
|
-
if (timer !== null) {
|
|
336
|
-
clearTimeout(timer);
|
|
337
|
-
timer = null;
|
|
338
|
-
}
|
|
339
|
-
const durationMs = Date.now() - startedAt;
|
|
340
|
-
let status = 'ok';
|
|
341
|
-
if (killedByTimeout || signal === 'SIGKILL') {
|
|
342
|
-
status = 'timeout';
|
|
343
|
-
}
|
|
344
|
-
else if (code !== 0) {
|
|
345
|
-
status = 'failed';
|
|
346
|
-
}
|
|
347
|
-
resolveTask({
|
|
348
|
-
status,
|
|
349
|
-
exitCode: code,
|
|
350
|
-
stdout: stdoutChunks.join(''),
|
|
351
|
-
stderr: stderrChunks.join(''),
|
|
352
|
-
durationMs,
|
|
353
|
-
});
|
|
354
|
-
});
|
|
355
|
-
});
|
|
356
|
-
}
|
|
357
|
-
/**
|
|
358
|
-
* Atomic write helper — same tmp + rename pattern the agent-progress
|
|
359
|
-
* writer uses. Public so the test suite can exercise the path.
|
|
360
|
-
*/
|
|
361
|
-
export function atomicWriteFile(targetPath, body) {
|
|
362
|
-
const parent = dirname(targetPath);
|
|
363
|
-
if (!existsSync(parent)) {
|
|
364
|
-
mkdirSync(parent, { recursive: true });
|
|
365
|
-
}
|
|
366
|
-
const tmpPath = `${targetPath}.tmp-${process.pid}-${Date.now()}-${Math.floor(Math.random() * 1_000_000)}`;
|
|
367
|
-
try {
|
|
368
|
-
writeFileSync(tmpPath, body, 'utf8');
|
|
369
|
-
renameSync(tmpPath, targetPath);
|
|
370
|
-
}
|
|
371
|
-
catch (err) {
|
|
372
|
-
try {
|
|
373
|
-
rmSync(tmpPath, { force: true });
|
|
374
|
-
}
|
|
375
|
-
catch {
|
|
376
|
-
// ignore cleanup failure
|
|
377
|
-
}
|
|
378
|
-
throw err;
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
function renderSummary(result) {
|
|
382
|
-
const lines = [];
|
|
383
|
-
lines.push(`pugi batch — id=${result.batchId}`);
|
|
384
|
-
lines.push(`recipe: ${result.recipePath}`);
|
|
385
|
-
lines.push(`results: ${result.resultsPath}`);
|
|
386
|
-
lines.push('');
|
|
387
|
-
const okCount = result.results.filter((r) => r.status === 'ok').length;
|
|
388
|
-
const failed = result.results.filter((r) => r.status === 'failed').length;
|
|
389
|
-
const timeouts = result.results.filter((r) => r.status === 'timeout').length;
|
|
390
|
-
const skipped = result.results.filter((r) => r.status === 'skipped').length;
|
|
391
|
-
lines.push(`tasks: ${result.results.length} ok=${okCount} failed=${failed} timeout=${timeouts} skipped=${skipped}`);
|
|
392
|
-
lines.push('');
|
|
393
|
-
// ASCII summary table — no external dep required.
|
|
394
|
-
const header = 'id'.padEnd(30) + ' | status | exit | duration';
|
|
395
|
-
lines.push(header);
|
|
396
|
-
lines.push('-'.repeat(header.length));
|
|
397
|
-
for (const r of result.results) {
|
|
398
|
-
const idCell = (r.id.length > 30 ? `${r.id.slice(0, 27)}...` : r.id).padEnd(30);
|
|
399
|
-
const statusCell = r.status.padEnd(8);
|
|
400
|
-
const exitCell = (r.exitCode === null ? '-' : String(r.exitCode)).padStart(4);
|
|
401
|
-
const dur = `${(r.durationMs / 1000).toFixed(1)}s`;
|
|
402
|
-
lines.push(`${idCell} | ${statusCell} | ${exitCell} | ${dur}`);
|
|
403
|
-
}
|
|
404
|
-
return lines.join('\n');
|
|
405
|
-
}
|
|
406
|
-
/**
|
|
407
|
-
* Bounded concurrent map. Exposed so tests can drive it directly when
|
|
408
|
-
* the runner is async-deterministic and we want to assert that the
|
|
409
|
-
* pool never exceeds the requested concurrency.
|
|
410
|
-
*/
|
|
411
|
-
export async function runWithConcurrency(items, concurrency, worker) {
|
|
412
|
-
if (concurrency <= 0) {
|
|
413
|
-
throw new Error('runWithConcurrency: concurrency must be > 0');
|
|
414
|
-
}
|
|
415
|
-
const cap = Math.min(concurrency, items.length);
|
|
416
|
-
const results = new Array(items.length);
|
|
417
|
-
let cursor = 0;
|
|
418
|
-
const launchOne = async () => {
|
|
419
|
-
while (true) {
|
|
420
|
-
const idx = cursor;
|
|
421
|
-
cursor += 1;
|
|
422
|
-
if (idx >= items.length)
|
|
423
|
-
return;
|
|
424
|
-
const item = items[idx];
|
|
425
|
-
if (item === undefined)
|
|
426
|
-
continue;
|
|
427
|
-
const out = await worker(item);
|
|
428
|
-
results[idx] = out;
|
|
429
|
-
}
|
|
430
|
-
};
|
|
431
|
-
const lanes = [];
|
|
432
|
-
for (let i = 0; i < cap; i += 1)
|
|
433
|
-
lanes.push(launchOne());
|
|
434
|
-
await Promise.all(lanes);
|
|
435
|
-
return results;
|
|
436
|
-
}
|
|
437
|
-
function makeBatchId(now) {
|
|
438
|
-
const iso = now.toISOString().replace(/[:.]/g, '-');
|
|
439
|
-
const suffix = Math.floor(Math.random() * 0xfffff)
|
|
440
|
-
.toString(16)
|
|
441
|
-
.padStart(5, '0');
|
|
442
|
-
return `batch-${iso}-${suffix}`;
|
|
443
|
-
}
|
|
444
|
-
function summarizeError(stdout, stderr) {
|
|
445
|
-
const candidate = (stderr || stdout).trim();
|
|
446
|
-
if (candidate === '')
|
|
447
|
-
return null;
|
|
448
|
-
// Keep summaries short so the printed table stays readable; the
|
|
449
|
-
// full text is on disk via the per-task log files.
|
|
450
|
-
const firstLine = candidate.split('\n', 1)[0] ?? '';
|
|
451
|
-
return firstLine.slice(0, 200);
|
|
452
|
-
}
|
|
453
|
-
export async function runBatchCommand(args, ctx) {
|
|
454
|
-
const { flags, error } = parseFlags(args);
|
|
455
|
-
if (error === 'help') {
|
|
456
|
-
ctx.writeOutput({ ok: true, command: 'batch', usage: BATCH_USAGE }, BATCH_USAGE);
|
|
457
|
-
return emptyResult('help', '');
|
|
458
|
-
}
|
|
459
|
-
if (error !== null) {
|
|
460
|
-
ctx.writeOutput({ ok: false, command: 'batch', error }, `pugi batch: ${error}`);
|
|
461
|
-
return { ...emptyResult('args-error', ''), exitCode: 2 };
|
|
462
|
-
}
|
|
463
|
-
if (flags.recipePath === null) {
|
|
464
|
-
const msg = 'recipe path is required';
|
|
465
|
-
ctx.writeOutput({ ok: false, command: 'batch', error: msg }, `pugi batch: ${msg}\n\n${BATCH_USAGE}`);
|
|
466
|
-
return { ...emptyResult('args-error', ''), exitCode: 2 };
|
|
467
|
-
}
|
|
468
|
-
const recipePath = isAbsolute(flags.recipePath)
|
|
469
|
-
? flags.recipePath
|
|
470
|
-
: resolve(ctx.cwd, flags.recipePath);
|
|
471
|
-
if (!existsSync(recipePath)) {
|
|
472
|
-
const msg = `recipe not found: ${recipePath}`;
|
|
473
|
-
ctx.writeOutput({ ok: false, command: 'batch', error: msg }, `pugi batch: ${msg}`);
|
|
474
|
-
return { ...emptyResult('args-error', recipePath), exitCode: 2 };
|
|
475
|
-
}
|
|
476
|
-
let recipeText;
|
|
477
|
-
try {
|
|
478
|
-
recipeText = readFileSync(recipePath, 'utf8');
|
|
479
|
-
}
|
|
480
|
-
catch (err) {
|
|
481
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
482
|
-
ctx.writeOutput({ ok: false, command: 'batch', error: message }, `pugi batch: cannot read ${recipePath}: ${message}`);
|
|
483
|
-
return { ...emptyResult('args-error', recipePath), exitCode: 2 };
|
|
484
|
-
}
|
|
485
|
-
const parsed = parseBatchRecipe(recipeText);
|
|
486
|
-
if (!parsed.ok) {
|
|
487
|
-
ctx.writeOutput({ ok: false, command: 'batch', error: parsed.error }, `pugi batch: recipe parse error — ${parsed.error}`);
|
|
488
|
-
return { ...emptyResult('args-error', recipePath), exitCode: 2 };
|
|
489
|
-
}
|
|
490
|
-
const recipe = parsed.recipe;
|
|
491
|
-
const concurrency = flags.concurrency !== null ? flags.concurrency : recipe.concurrency;
|
|
492
|
-
const batchId = ctx.newBatchId ? ctx.newBatchId() : makeBatchId(ctx.now());
|
|
493
|
-
const batchDir = join(ctx.cwd, '.pugi', 'batch', batchId);
|
|
494
|
-
mkdirSync(batchDir, { recursive: true });
|
|
495
|
-
const resultsPath = join(batchDir, 'results.jsonl');
|
|
496
|
-
if (flags.dryRun) {
|
|
497
|
-
const result = {
|
|
498
|
-
batchId,
|
|
499
|
-
recipePath,
|
|
500
|
-
resultsPath,
|
|
501
|
-
results: recipe.tasks.map((t) => ({
|
|
502
|
-
id: t.id,
|
|
503
|
-
status: 'skipped',
|
|
504
|
-
exitCode: null,
|
|
505
|
-
durationMs: 0,
|
|
506
|
-
outputPath: join(batchDir, `${t.id}.stdout.log`),
|
|
507
|
-
errorPath: join(batchDir, `${t.id}.stderr.log`),
|
|
508
|
-
errorSummary: null,
|
|
509
|
-
})),
|
|
510
|
-
exitCode: 0,
|
|
511
|
-
};
|
|
512
|
-
ctx.writeOutput({ ok: true, command: 'batch', dryRun: true, result }, `pugi batch: dry-run, ${recipe.tasks.length} tasks would run with concurrency=${concurrency}`);
|
|
513
|
-
return result;
|
|
514
|
-
}
|
|
515
|
-
const runner = ctx.runner ?? spawnPugiSubprocess;
|
|
516
|
-
const results = [];
|
|
517
|
-
await runWithConcurrency(recipe.tasks, concurrency, async (task) => {
|
|
518
|
-
const outputPath = join(batchDir, `${task.id}.stdout.log`);
|
|
519
|
-
const errorPath = join(batchDir, `${task.id}.stderr.log`);
|
|
520
|
-
let out;
|
|
521
|
-
try {
|
|
522
|
-
out = await runner(task, { batchDir, cwd: ctx.cwd, env: ctx.env });
|
|
523
|
-
}
|
|
524
|
-
catch (err) {
|
|
525
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
526
|
-
out = {
|
|
527
|
-
status: 'failed',
|
|
528
|
-
exitCode: null,
|
|
529
|
-
stdout: '',
|
|
530
|
-
stderr: `runner threw: ${message}`,
|
|
531
|
-
durationMs: 0,
|
|
532
|
-
};
|
|
533
|
-
}
|
|
534
|
-
try {
|
|
535
|
-
atomicWriteFile(outputPath, out.stdout);
|
|
536
|
-
atomicWriteFile(errorPath, out.stderr);
|
|
537
|
-
}
|
|
538
|
-
catch {
|
|
539
|
-
// best-effort; we still record the in-memory result so the
|
|
540
|
-
// operator can tell what happened from the summary table.
|
|
541
|
-
}
|
|
542
|
-
const taskResult = {
|
|
543
|
-
id: task.id,
|
|
544
|
-
status: out.status,
|
|
545
|
-
exitCode: out.exitCode,
|
|
546
|
-
durationMs: out.durationMs,
|
|
547
|
-
outputPath,
|
|
548
|
-
errorPath,
|
|
549
|
-
errorSummary: out.status === 'ok' ? null : summarizeError(out.stdout, out.stderr),
|
|
550
|
-
};
|
|
551
|
-
results.push(taskResult);
|
|
552
|
-
});
|
|
553
|
-
// Preserve recipe order for idempotent re-runs and stable JSONL.
|
|
554
|
-
results.sort((a, b) => {
|
|
555
|
-
const idxA = recipe.tasks.findIndex((t) => t.id === a.id);
|
|
556
|
-
const idxB = recipe.tasks.findIndex((t) => t.id === b.id);
|
|
557
|
-
return idxA - idxB;
|
|
558
|
-
});
|
|
559
|
-
const jsonl = results.map((r) => JSON.stringify(r)).join('\n');
|
|
560
|
-
try {
|
|
561
|
-
atomicWriteFile(resultsPath, jsonl === '' ? '' : `${jsonl}\n`);
|
|
562
|
-
}
|
|
563
|
-
catch (err) {
|
|
564
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
565
|
-
ctx.writeOutput({ ok: false, command: 'batch', error: `failed to persist results: ${message}` }, `pugi batch: failed to persist results — ${message}`);
|
|
566
|
-
return {
|
|
567
|
-
batchId,
|
|
568
|
-
recipePath,
|
|
569
|
-
resultsPath,
|
|
570
|
-
results,
|
|
571
|
-
exitCode: 1,
|
|
572
|
-
};
|
|
573
|
-
}
|
|
574
|
-
const failedCount = results.filter((r) => r.status === 'failed' || r.status === 'timeout').length;
|
|
575
|
-
const exitCode = failedCount === 0 ? 0 : 1;
|
|
576
|
-
const result = {
|
|
577
|
-
batchId,
|
|
578
|
-
recipePath,
|
|
579
|
-
resultsPath,
|
|
580
|
-
results,
|
|
581
|
-
exitCode,
|
|
582
|
-
};
|
|
583
|
-
ctx.writeOutput({ ok: true, command: 'batch', result }, renderSummary(result));
|
|
584
|
-
return result;
|
|
585
|
-
}
|
|
586
|
-
function emptyResult(reason, recipePath) {
|
|
587
|
-
return {
|
|
588
|
-
batchId: reason,
|
|
589
|
-
recipePath,
|
|
590
|
-
resultsPath: '',
|
|
591
|
-
results: [],
|
|
592
|
-
exitCode: 0,
|
|
593
|
-
};
|
|
594
|
-
}
|
|
595
|
-
const BATCH_USAGE = [
|
|
596
|
-
'pugi batch — fan-out multi-task parallel execution scaffold.',
|
|
597
|
-
'',
|
|
598
|
-
'Usage:',
|
|
599
|
-
' pugi batch <recipe.yml> [--concurrency <N>] [--json] [--dry-run]',
|
|
600
|
-
'',
|
|
601
|
-
'Flags:',
|
|
602
|
-
' --concurrency <N> Override recipe concurrency (1..30). Default: 5.',
|
|
603
|
-
' --json Emit a JSON envelope instead of human text.',
|
|
604
|
-
' --dry-run Validate the recipe + plan paths; do not spawn anything.',
|
|
605
|
-
'',
|
|
606
|
-
'Recipe shape (YAML):',
|
|
607
|
-
' concurrency: 5 # optional, defaults to 5, hard cap 30',
|
|
608
|
-
' tasks:',
|
|
609
|
-
' - id: <kebab-id>',
|
|
610
|
-
' prompt: "<text>"',
|
|
611
|
-
' model: <slug> # optional',
|
|
612
|
-
' maxTurns: <int> # optional',
|
|
613
|
-
' timeoutMs: <int> # optional, default 600000',
|
|
614
|
-
'',
|
|
615
|
-
'Results land in .pugi/batch/<batch-id>/results.jsonl (one line per task).',
|
|
616
|
-
].join('\n');
|
|
617
|
-
//# sourceMappingURL=batch.js.map
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Bundled-skills registry — batch 1 (backlog) + batch 2 (backlog).
|
|
3
|
-
*
|
|
4
|
-
* Bundled skills ship as in-binary commands rather than user
|
|
5
|
-
* `.pugi/skills/` markdown:
|
|
6
|
-
*
|
|
7
|
-
* - Batch 1 (`#168`):
|
|
8
|
-
* - `pugi stuck` — non-invasive self-diagnosis (no process kill).
|
|
9
|
-
* - `pugi simplify` — local 3-agent parallel review + auto-fix.
|
|
10
|
-
* - `pugi remember` — proposal-first memory curator.
|
|
11
|
-
*
|
|
12
|
-
* - Batch 2 (`#169`, external independent implementation):
|
|
13
|
-
* - `pugi batch` — fan-out multi-task parallel execution scaffold.
|
|
14
|
-
* - `pugi verify` — post-execution claim verification.
|
|
15
|
-
* - `pugi loop` — drive a prompt against the engine to a stop condition.
|
|
16
|
-
* - `pugi skillify` — codify a freeform description into a skill scaffold.
|
|
17
|
-
*
|
|
18
|
-
* Each skill exports its runner from this index so the CLI dispatcher
|
|
19
|
-
* in `runtime/cli.ts` (and any future REPL slash wrapper) imports
|
|
20
|
-
* through one barrel. New batches append to the union types here.
|
|
21
|
-
*
|
|
22
|
-
* Inspired by the upstream tool / external bundled-skills patterns (intel from
|
|
23
|
-
* leak-research memos, independent implementation TS).
|
|
24
|
-
*/
|
|
25
|
-
export { runStuckCommand, classifyPeers, classifyChildren, readDebugTail, captureSampleStack, postSnapshotToWebhook, readProcessTable, } from './stuck.js';
|
|
26
|
-
export { runSimplifyCommand, aggregateReviewerResults, parseReviewerFindings, defaultCaptureDiff, } from './simplify.js';
|
|
27
|
-
export { runRememberCommand, classifyMemoryCandidate, } from './remember.js';
|
|
28
|
-
export { runBatchCommand, parseBatchRecipe, spawnPugiSubprocess, runWithConcurrency, atomicWriteFile as atomicWriteFileBatch, } from './batch.js';
|
|
29
|
-
export { runVerifyCommand, parseClaimsDocument, parseExpectedPattern, resolveTargetPath, tokenizeCommand, } from './verify.js';
|
|
30
|
-
export { runLoopCommand, parseStopRegex, atomicWriteFile as atomicWriteFileLoop, } from './loop.js';
|
|
31
|
-
export { runSkillifyCommand, parseScaffoldEnvelope, extractJsonBlock, renderSkillMarkdown, } from './skillify.js';
|
|
32
|
-
/**
|
|
33
|
-
* Names of every bundled skill. Kept as a frozen tuple so the help
|
|
34
|
-
* renderer and the dispatcher reuse a single source of truth.
|
|
35
|
-
*/
|
|
36
|
-
export const BUNDLED_SKILL_NAMES = Object.freeze([
|
|
37
|
-
'stuck',
|
|
38
|
-
'simplify',
|
|
39
|
-
'remember',
|
|
40
|
-
'batch',
|
|
41
|
-
'verify',
|
|
42
|
-
'loop',
|
|
43
|
-
'skillify',
|
|
44
|
-
]);
|
|
45
|
-
//# sourceMappingURL=index.js.map
|