@pugi/cli 0.1.0-beta.98 → 1.0.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +11 -191
- package/bin/pugi +8 -0
- package/package.json +15 -71
- package/postinstall.mjs +31 -0
- package/CHANGELOG.md +0 -132
- package/THIRD_PARTY_NOTICES.md +0 -40
- package/assets/pugi-mascot.ansi +0 -16
- package/assets/pugi-prozr2-mascot.ansi +0 -9
- package/bin/run.js +0 -34
- package/dist/commands/deploy.js +0 -439
- package/dist/commands/flatten.js +0 -191
- package/dist/commands/jobs-watch.js +0 -201
- package/dist/commands/jobs.js +0 -260
- package/dist/commands/retro.js +0 -210
- package/dist/commands/smoke.js +0 -133
- package/dist/core/agent-progress/cleanup.js +0 -134
- package/dist/core/agent-progress/schema.js +0 -144
- package/dist/core/agent-progress/writer.js +0 -101
- package/dist/core/agents/adaptive-router.js +0 -330
- package/dist/core/agents/loader.js +0 -104
- package/dist/core/agents/query-decomposer.js +0 -297
- package/dist/core/agents/registry.js +0 -69
- package/dist/core/approvals/shortcut-resolver.js +0 -98
- package/dist/core/artifact-chain/dispatcher.js +0 -148
- package/dist/core/artifact-chain/exporter.js +0 -164
- package/dist/core/artifact-chain/state.js +0 -243
- package/dist/core/artifact-chain/steps.js +0 -169
- package/dist/core/ask-user/question.js +0 -92
- package/dist/core/audit/audit-trail.js +0 -275
- package/dist/core/auth/ensure-authenticated.js +0 -129
- package/dist/core/auth/env-provider.js +0 -238
- package/dist/core/auto-open-browser.js +0 -128
- package/dist/core/auto-update/channels.js +0 -122
- package/dist/core/auto-update/checker.js +0 -241
- package/dist/core/auto-update/state.js +0 -235
- package/dist/core/bare-mode/index.js +0 -107
- package/dist/core/bash/redirect.js +0 -281
- package/dist/core/bash-classifier.js +0 -1397
- package/dist/core/checkpoint/resumer.js +0 -149
- package/dist/core/checkpoint/rewinder.js +0 -291
- package/dist/core/checkpoints/shadow-git.js +0 -670
- package/dist/core/citations/parser.js +0 -109
- package/dist/core/classifier/yolo-classifier.js +0 -88
- package/dist/core/clipboard.js +0 -70
- package/dist/core/codegraph/decision-store.js +0 -248
- package/dist/core/codegraph/detect-repo.js +0 -459
- package/dist/core/codegraph/install.js +0 -134
- package/dist/core/codegraph/offer-hook.js +0 -220
- package/dist/core/compact/auto-trigger.js +0 -96
- package/dist/core/compact/buffer-rewriter.js +0 -115
- package/dist/core/compact/summarizer.js +0 -208
- package/dist/core/compact/token-counter.js +0 -108
- package/dist/core/consensus/anvil-fanout.js +0 -276
- package/dist/core/consensus/diff-capture.js +0 -491
- package/dist/core/consensus/rubric.js +0 -233
- package/dist/core/context/builder.js +0 -114
- package/dist/core/context/compaction-events.js +0 -99
- package/dist/core/context/compaction.js +0 -602
- package/dist/core/context/index.js +0 -28
- package/dist/core/context/invariants.js +0 -250
- package/dist/core/context/markdown-loader.js +0 -288
- package/dist/core/context/markdown-traverse.js +0 -255
- package/dist/core/context/pugiignore.js +0 -316
- package/dist/core/context/repo-skeleton.js +0 -533
- package/dist/core/context/tool-eviction.js +0 -55
- package/dist/core/context/watcher.js +0 -342
- package/dist/core/context/working-set.js +0 -165
- package/dist/core/coordinator/agent-tools.js +0 -77
- package/dist/core/coordinator/agent-toolset.js +0 -65
- package/dist/core/coordinator/fsm.js +0 -73
- package/dist/core/coordinator/mode-fsm.js +0 -70
- package/dist/core/cost/rate-card.js +0 -129
- package/dist/core/cost/tracker.js +0 -221
- package/dist/core/credentials.js +0 -355
- package/dist/core/cron/scheduler.js +0 -138
- package/dist/core/denial-tracking/index.js +0 -8
- package/dist/core/denial-tracking/state.js +0 -264
- package/dist/core/diagnostics/probe-runner.js +0 -93
- package/dist/core/diagnostics/probes/api.js +0 -46
- package/dist/core/diagnostics/probes/auth.js +0 -93
- package/dist/core/diagnostics/probes/bare-mode.js +0 -42
- package/dist/core/diagnostics/probes/cli-version.js +0 -127
- package/dist/core/diagnostics/probes/config.js +0 -72
- package/dist/core/diagnostics/probes/denial-tracking.js +0 -57
- package/dist/core/diagnostics/probes/disk.js +0 -81
- package/dist/core/diagnostics/probes/engine-live.js +0 -46
- package/dist/core/diagnostics/probes/git.js +0 -65
- package/dist/core/diagnostics/probes/hooks.js +0 -118
- package/dist/core/diagnostics/probes/mcp.js +0 -75
- package/dist/core/diagnostics/probes/node.js +0 -59
- package/dist/core/diagnostics/probes/pnpm.js +0 -36
- package/dist/core/diagnostics/probes/pugi-md.js +0 -89
- package/dist/core/diagnostics/probes/sandbox.js +0 -72
- package/dist/core/diagnostics/probes/session.js +0 -74
- package/dist/core/diagnostics/probes/status-snapshot.js +0 -488
- package/dist/core/diagnostics/probes/workspace.js +0 -63
- package/dist/core/diagnostics/types.js +0 -70
- package/dist/core/dispatch/cache-cleanup.js +0 -197
- package/dist/core/dispatch/cache-handoff.js +0 -295
- package/dist/core/edits/apply-patch-layer-e.js +0 -189
- package/dist/core/edits/dispatch.js +0 -511
- package/dist/core/edits/format-detector.js +0 -260
- package/dist/core/edits/format-matrix.js +0 -26
- package/dist/core/edits/fuzzy-ladder.js +0 -650
- package/dist/core/edits/index.js +0 -19
- package/dist/core/edits/journal.js +0 -199
- package/dist/core/edits/layer-a-apply.js +0 -217
- package/dist/core/edits/layer-a-fuzzy-apply.js +0 -198
- package/dist/core/edits/layer-b-apply.js +0 -211
- package/dist/core/edits/layer-c-apply.js +0 -160
- package/dist/core/edits/layer-d-ast.js +0 -572
- package/dist/core/edits/marker-parser.js +0 -401
- package/dist/core/edits/security-gate.js +0 -223
- package/dist/core/edits/verify-hook.js +0 -273
- package/dist/core/edits/worktree.js +0 -322
- package/dist/core/engine/adapter-runner.js +0 -8
- package/dist/core/engine/anvil-client.js +0 -344
- package/dist/core/engine/auto-compact.js +0 -179
- package/dist/core/engine/budgets.js +0 -192
- package/dist/core/engine/context-prefix.js +0 -155
- package/dist/core/engine/index.js +0 -12
- package/dist/core/engine/intensity.js +0 -163
- package/dist/core/engine/intent.js +0 -260
- package/dist/core/engine/native-pugi.js +0 -1616
- package/dist/core/engine/noop.js +0 -27
- package/dist/core/engine/prompts.js +0 -236
- package/dist/core/engine/strip-internal-fields.js +0 -124
- package/dist/core/engine/tool-bridge.js +0 -2173
- package/dist/core/engine/verification-patterns.js +0 -195
- package/dist/core/evaluation/golden-dataset.js +0 -293
- package/dist/core/feedback/queue.js +0 -177
- package/dist/core/feedback/submitter.js +0 -145
- package/dist/core/file-cache.js +0 -141
- package/dist/core/flatten/flatten-repo.js +0 -439
- package/dist/core/format/osc8-link.js +0 -28
- package/dist/core/hook-chains.js +0 -392
- package/dist/core/hooks/citation-verify-hook.js +0 -138
- package/dist/core/hooks/citation-verify.js +0 -112
- package/dist/core/hooks/events.js +0 -46
- package/dist/core/hooks/index.js +0 -15
- package/dist/core/hooks/registry.js +0 -216
- package/dist/core/hooks/runner.js +0 -236
- package/dist/core/hooks/v2/event-emitter.js +0 -115
- package/dist/core/hooks/v2/executor.js +0 -282
- package/dist/core/hooks/v2/index.js +0 -25
- package/dist/core/hooks/v2/lifecycle.js +0 -104
- package/dist/core/hooks/v2/loader.js +0 -216
- package/dist/core/hooks/v2/matcher.js +0 -125
- package/dist/core/hooks/v2/trust.js +0 -143
- package/dist/core/hooks/v2/types.js +0 -86
- package/dist/core/hooks/worktree-events.js +0 -158
- package/dist/core/hooks.js +0 -415
- package/dist/core/image/renderer.js +0 -71
- package/dist/core/index-store.js +0 -260
- package/dist/core/init/detector.js +0 -582
- package/dist/core/init/template-renderer.js +0 -242
- package/dist/core/jobs/registry.js +0 -462
- package/dist/core/ledger/results-tsv.js +0 -142
- package/dist/core/log-discipline/stdout-redirect.js +0 -51
- package/dist/core/lsp/cache.js +0 -105
- package/dist/core/lsp/client.js +0 -1229
- package/dist/core/lsp/language-detect.js +0 -66
- package/dist/core/lsp/post-edit-diagnostics.js +0 -171
- package/dist/core/lsp/server-detect.js +0 -173
- package/dist/core/lsp/symbol-cache.js +0 -162
- package/dist/core/lsp/symbol-tools.js +0 -664
- package/dist/core/mcp/client.js +0 -385
- package/dist/core/mcp/http-server.js +0 -553
- package/dist/core/mcp/orchestrator-config.js +0 -192
- package/dist/core/mcp/orchestrator-tools.js +0 -806
- package/dist/core/mcp/permission.js +0 -190
- package/dist/core/mcp/registry.js +0 -193
- package/dist/core/mcp/server-tools.js +0 -219
- package/dist/core/mcp/server.js +0 -397
- package/dist/core/mcp/trust.js +0 -91
- package/dist/core/memory/dual-write.js +0 -416
- package/dist/core/memory/passive-extract.js +0 -130
- package/dist/core/memory/phase1-kinds.js +0 -20
- package/dist/core/memory/secret-scanner.js +0 -304
- package/dist/core/memory-sync/queue.js +0 -170
- package/dist/core/metrics/extract.js +0 -113
- package/dist/core/modes/roo-modes.js +0 -68
- package/dist/core/onboarding/ensure-initialized.js +0 -133
- package/dist/core/onboarding/marker.js +0 -111
- package/dist/core/onboarding/telemetry-state.js +0 -108
- package/dist/core/output-style/presets.js +0 -176
- package/dist/core/output-style/state.js +0 -185
- package/dist/core/path-security.js +0 -345
- package/dist/core/permission.js +0 -369
- package/dist/core/permissions/auto-classifier.js +0 -124
- package/dist/core/permissions/bash-parser.js +0 -371
- package/dist/core/permissions/circuit-breaker.js +0 -83
- package/dist/core/permissions/constrained-edit.js +0 -91
- package/dist/core/permissions/gate.js +0 -278
- package/dist/core/permissions/index.js +0 -20
- package/dist/core/permissions/mode.js +0 -174
- package/dist/core/permissions/network-egress.js +0 -137
- package/dist/core/permissions/state.js +0 -241
- package/dist/core/permissions/tool-class.js +0 -107
- package/dist/core/plan-mode/ui-state.js +0 -51
- package/dist/core/plans/plan-artifact.js +0 -721
- package/dist/core/policy-limits/etag-store.js +0 -122
- package/dist/core/prd-check/parser.js +0 -215
- package/dist/core/prd-check/reporter.js +0 -127
- package/dist/core/prd-check/session-review.js +0 -557
- package/dist/core/prd-check/verifiers.js +0 -223
- package/dist/core/prompt-cache/client-cache.js +0 -99
- package/dist/core/prompts/assembly.js +0 -29
- package/dist/core/prompts/registry.js +0 -364
- package/dist/core/pugi-gitignore.js +0 -52
- package/dist/core/pugi-md/cc-compat-rules.js +0 -735
- package/dist/core/pugi-md/context-injector.js +0 -76
- package/dist/core/pugi-md/walk-up.js +0 -207
- package/dist/core/python/uv-installer.js +0 -270
- package/dist/core/python/uv-resolver.js +0 -83
- package/dist/core/rate-limit/narrator.js +0 -146
- package/dist/core/recipes/cli-types.js +0 -20
- package/dist/core/recipes/loader.js +0 -103
- package/dist/core/recipes/runner.js +0 -345
- package/dist/core/recipes/schema.js +0 -587
- package/dist/core/release-notes/parser.js +0 -241
- package/dist/core/release-notes/state.js +0 -116
- package/dist/core/repl/ask.js +0 -512
- package/dist/core/repl/cancellation.js +0 -98
- package/dist/core/repl/cap-warning.js +0 -91
- package/dist/core/repl/clipboard-read.js +0 -174
- package/dist/core/repl/dispatch-fsm.js +0 -220
- package/dist/core/repl/engine-bridge.js +0 -303
- package/dist/core/repl/history-search.js +0 -175
- package/dist/core/repl/history.js +0 -182
- package/dist/core/repl/kill-ring.js +0 -138
- package/dist/core/repl/model-pricing.js +0 -135
- package/dist/core/repl/privacy-banner.js +0 -71
- package/dist/core/repl/session.js +0 -4962
- package/dist/core/repl/slash-commands.js +0 -747
- package/dist/core/repl/store/index.js +0 -12
- package/dist/core/repl/store/jsonl-log.js +0 -321
- package/dist/core/repl/store/lockfile.js +0 -155
- package/dist/core/repl/store/session-store.js +0 -821
- package/dist/core/repl/store/types.js +0 -44
- package/dist/core/repl/store/uuid-v7.js +0 -68
- package/dist/core/repl/tool-route.js +0 -382
- package/dist/core/repl/workspace-context.js +0 -206
- package/dist/core/repo-map/build.js +0 -125
- package/dist/core/repo-map/cache.js +0 -185
- package/dist/core/repo-map/extractor.js +0 -254
- package/dist/core/repo-map/formatter.js +0 -145
- package/dist/core/repo-map/page-rank.js +0 -105
- package/dist/core/repo-map/scanner.js +0 -211
- package/dist/core/retro/git-collector.js +0 -251
- package/dist/core/retro/health-card.js +0 -25
- package/dist/core/retro/metrics.js +0 -342
- package/dist/core/retro/narrative.js +0 -249
- package/dist/core/retro/plane-collector.js +0 -274
- package/dist/core/retro/pr-issue-link.js +0 -65
- package/dist/core/retro/types.js +0 -16
- package/dist/core/retry-budget/budget.js +0 -284
- package/dist/core/retry-budget/index.js +0 -5
- package/dist/core/retry-budget/retry-cap.js +0 -74
- package/dist/core/routing/lead-worker.js +0 -43
- package/dist/core/routing/pre-flight-estimator.js +0 -108
- package/dist/core/runs/run-tree.js +0 -103
- package/dist/core/sandboxing/adapter.js +0 -29
- package/dist/core/sandboxing/index.js +0 -49
- package/dist/core/sandboxing/none.js +0 -19
- package/dist/core/sandboxing/seatbelt.js +0 -183
- package/dist/core/security/injection-scanner.js +0 -367
- package/dist/core/security/output-filter.js +0 -418
- package/dist/core/session/env-file.js +0 -105
- package/dist/core/session/section-budgets.js +0 -140
- package/dist/core/session.js +0 -377
- package/dist/core/settings.js +0 -400
- package/dist/core/share/formatter.js +0 -271
- package/dist/core/share/redactor.js +0 -221
- package/dist/core/share/uploader.js +0 -267
- package/dist/core/skills/defaults.js +0 -457
- package/dist/core/skills/loader.js +0 -454
- package/dist/core/skills/sources.js +0 -480
- package/dist/core/skills/trust.js +0 -172
- package/dist/core/smoke/headless-driver.js +0 -174
- package/dist/core/smoke/orchestrator.js +0 -194
- package/dist/core/smoke/runner.js +0 -238
- package/dist/core/smoke/scenario-parser.js +0 -316
- package/dist/core/statusline.js +0 -99
- package/dist/core/subagents/dispatcher-real.js +0 -600
- package/dist/core/subagents/dispatcher.js +0 -352
- package/dist/core/subagents/index.js +0 -39
- package/dist/core/subagents/isolation-matrix.js +0 -213
- package/dist/core/subagents/spawn.js +0 -101
- package/dist/core/telemetry/emitter.js +0 -229
- package/dist/core/telemetry/queue.js +0 -251
- package/dist/core/theme/context.js +0 -91
- package/dist/core/theme/presets.js +0 -228
- package/dist/core/theme/state.js +0 -181
- package/dist/core/todos/invariant.js +0 -10
- package/dist/core/todos/state.js +0 -177
- package/dist/core/tool-schema/compressor.js +0 -89
- package/dist/core/transport/version-interceptor.js +0 -166
- package/dist/core/trust.js +0 -109
- package/dist/core/tui/thinking-block.js +0 -64
- package/dist/core/vim/keymap.js +0 -288
- package/dist/core/vim/state.js +0 -92
- package/dist/core/watch-markers/marker-watcher.js +0 -133
- package/dist/core/worktree/include-parser.js +0 -249
- package/dist/core/worktree-manager/cleanup.js +0 -123
- package/dist/core/worktree-manager/manager.js +0 -303
- package/dist/index.js +0 -44
- package/dist/runtime/bootstrap.js +0 -190
- package/dist/runtime/cli.js +0 -8121
- package/dist/runtime/commands/agents.js +0 -385
- package/dist/runtime/commands/budget.js +0 -192
- package/dist/runtime/commands/cancel.js +0 -231
- package/dist/runtime/commands/chain.js +0 -489
- package/dist/runtime/commands/codegraph-status.js +0 -227
- package/dist/runtime/commands/compact.js +0 -297
- package/dist/runtime/commands/config.js +0 -595
- package/dist/runtime/commands/cost.js +0 -199
- package/dist/runtime/commands/delegate.js +0 -312
- package/dist/runtime/commands/dispatch.js +0 -126
- package/dist/runtime/commands/doctor.js +0 -579
- package/dist/runtime/commands/feedback.js +0 -184
- package/dist/runtime/commands/hooks.js +0 -187
- package/dist/runtime/commands/init.js +0 -254
- package/dist/runtime/commands/lsp.js +0 -368
- package/dist/runtime/commands/mcp.js +0 -935
- package/dist/runtime/commands/memory.js +0 -582
- package/dist/runtime/commands/model.js +0 -237
- package/dist/runtime/commands/onboarding.js +0 -275
- package/dist/runtime/commands/patch.js +0 -128
- package/dist/runtime/commands/permissions.js +0 -112
- package/dist/runtime/commands/plan.js +0 -143
- package/dist/runtime/commands/prd-check.js +0 -285
- package/dist/runtime/commands/privacy.js +0 -107
- package/dist/runtime/commands/recipe.js +0 -325
- package/dist/runtime/commands/redo-blob-store.js +0 -92
- package/dist/runtime/commands/redo.js +0 -361
- package/dist/runtime/commands/release-notes.js +0 -229
- package/dist/runtime/commands/repo-map.js +0 -95
- package/dist/runtime/commands/report.js +0 -299
- package/dist/runtime/commands/resume.js +0 -118
- package/dist/runtime/commands/review-consensus.js +0 -414
- package/dist/runtime/commands/rewind.js +0 -333
- package/dist/runtime/commands/roster.js +0 -117
- package/dist/runtime/commands/sessions.js +0 -163
- package/dist/runtime/commands/share.js +0 -316
- package/dist/runtime/commands/skills.js +0 -401
- package/dist/runtime/commands/status.js +0 -186
- package/dist/runtime/commands/stickers.js +0 -82
- package/dist/runtime/commands/style.js +0 -194
- package/dist/runtime/commands/theme.js +0 -196
- package/dist/runtime/commands/undo.js +0 -361
- package/dist/runtime/commands/update.js +0 -289
- package/dist/runtime/commands/vim.js +0 -140
- package/dist/runtime/commands/worktree.js +0 -177
- package/dist/runtime/commands/worktrees.js +0 -155
- package/dist/runtime/deprecation-warning.js +0 -69
- package/dist/runtime/engine-exit-code.js +0 -50
- package/dist/runtime/headless-repl.js +0 -195
- package/dist/runtime/headless.js +0 -548
- package/dist/runtime/load-hooks-or-exit.js +0 -71
- package/dist/runtime/plan-decompose.js +0 -531
- package/dist/runtime/sigint-guard.js +0 -272
- package/dist/runtime/stream-renderer.js +0 -195
- package/dist/runtime/update-check.js +0 -294
- package/dist/runtime/version.js +0 -65
- package/dist/runtime/worktree-bootstrap.js +0 -579
- package/dist/skills/bundled/batch.js +0 -617
- package/dist/skills/bundled/index.js +0 -45
- package/dist/skills/bundled/loop.js +0 -358
- package/dist/skills/bundled/remember.js +0 -383
- package/dist/skills/bundled/simplify.js +0 -289
- package/dist/skills/bundled/skillify.js +0 -373
- package/dist/skills/bundled/stuck.js +0 -558
- package/dist/skills/bundled/verify.js +0 -439
- package/dist/testing/vcr.js +0 -486
- package/dist/tools/agent-tool.js +0 -229
- package/dist/tools/apply-patch.js +0 -556
- package/dist/tools/ask-user-question.js +0 -337
- package/dist/tools/ask-user.js +0 -115
- package/dist/tools/bash.js +0 -1238
- package/dist/tools/brief.js +0 -224
- package/dist/tools/cron.js +0 -433
- package/dist/tools/enter-worktree.js +0 -250
- package/dist/tools/exit-worktree.js +0 -147
- package/dist/tools/file-tools.js +0 -553
- package/dist/tools/http-request.js +0 -336
- package/dist/tools/lsp-tools.js +0 -565
- package/dist/tools/mcp-tool.js +0 -260
- package/dist/tools/multi-edit.js +0 -361
- package/dist/tools/powershell.js +0 -268
- package/dist/tools/registry.js +0 -166
- package/dist/tools/server-tools.js +0 -892
- package/dist/tools/skill-tool.js +0 -96
- package/dist/tools/sleep.js +0 -99
- package/dist/tools/synthetic-output.js +0 -133
- package/dist/tools/tasks.js +0 -208
- package/dist/tools/todo-write.js +0 -184
- package/dist/tools/verify-plan-execution.js +0 -295
- package/dist/tools/web-fetch-injection-scanner.js +0 -207
- package/dist/tools/web-fetch.js +0 -720
- package/dist/tools/web-search.js +0 -458
- package/dist/tui/agent-progress-card.js +0 -111
- package/dist/tui/agent-tree-pane.js +0 -9
- package/dist/tui/agent-tree.js +0 -87
- package/dist/tui/ask-cli.js +0 -52
- package/dist/tui/ask-modal.js +0 -211
- package/dist/tui/ask-user-question-chips.js +0 -315
- package/dist/tui/ask-user-question-prompt.js +0 -203
- package/dist/tui/compact-banner.js +0 -81
- package/dist/tui/conversation-pane.js +0 -164
- package/dist/tui/cost-table.js +0 -111
- package/dist/tui/device-flow.js +0 -142
- package/dist/tui/doctor-table.js +0 -46
- package/dist/tui/feedback-prompt.js +0 -156
- package/dist/tui/input-box.js +0 -732
- package/dist/tui/login-picker.js +0 -69
- package/dist/tui/markdown-render.js +0 -266
- package/dist/tui/multi-file-diff-approval.js +0 -375
- package/dist/tui/onboarding-wizard.js +0 -240
- package/dist/tui/permissions-picker.js +0 -86
- package/dist/tui/render.js +0 -160
- package/dist/tui/repl-render.js +0 -770
- package/dist/tui/repl-splash-art.js +0 -64
- package/dist/tui/repl-splash-mascot.js +0 -154
- package/dist/tui/repl-splash.js +0 -117
- package/dist/tui/repl.js +0 -378
- package/dist/tui/slash-palette.js +0 -106
- package/dist/tui/splash-data.js +0 -61
- package/dist/tui/splash.js +0 -31
- package/dist/tui/status-bar.js +0 -209
- package/dist/tui/status-table.js +0 -7
- package/dist/tui/stickers-art.js +0 -136
- package/dist/tui/style-table.js +0 -28
- package/dist/tui/theme-table.js +0 -29
- package/dist/tui/thinking-spinner.js +0 -123
- package/dist/tui/tool-stream-pane.js +0 -140
- package/dist/tui/update-banner.js +0 -33
- package/dist/tui/vim-input.js +0 -267
- package/dist/tui/welcome-banner.js +0 -107
- package/dist/tui/welcome-data.js +0 -293
- package/dist/tui/workspace-context.js +0 -105
- package/docs/examples/codegraph.mcp.json +0 -10
- package/test/scenarios/codegen-create-file.scenario.txt +0 -13
- package/test/scenarios/compact-force.scenario.txt +0 -12
- package/test/scenarios/identity.scenario.txt +0 -11
- package/test/scenarios/persona-handoff.scenario.txt +0 -12
- package/test/scenarios/walkback.scenario.txt +0 -12
package/dist/tui/repl-render.js
DELETED
|
@@ -1,770 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Production REPL mount + transport - Sprint .
|
|
3
|
-
*
|
|
4
|
-
* Owns the Ink mount lifecycle for `<Repl />` and wires the real
|
|
5
|
-
* fetch + SSE transport. The CLI dispatcher in `runtime/cli.ts` calls
|
|
6
|
-
* `renderRepl` on the bare-`pugi` path when stdin / stdout are TTYs.
|
|
7
|
-
*
|
|
8
|
-
* The transport speaks to admin-api:
|
|
9
|
-
* POST /api/pugi/sessions → { sessionId }
|
|
10
|
-
* POST /api/pugi/sessions/:id/brief → { dispatchId }
|
|
11
|
-
* POST /api/pugi/sessions/:id/stop → { stopped }
|
|
12
|
-
* GET /api/pugi/sessions/:id/stream → text/event-stream
|
|
13
|
-
*
|
|
14
|
-
* SSE is parsed client-side from a streaming fetch response - Node 22
|
|
15
|
-
* native fetch returns a WHATWG `ReadableStream` which we feed through
|
|
16
|
-
* a tiny `event:`/`data:`/`id:` parser. This keeps the dependency
|
|
17
|
-
* graph at zero new packages.
|
|
18
|
-
*/
|
|
19
|
-
import { existsSync } from 'node:fs';
|
|
20
|
-
import { resolve } from 'node:path';
|
|
21
|
-
import React from 'react';
|
|
22
|
-
import { render } from 'ink';
|
|
23
|
-
import { Repl } from './repl.js';
|
|
24
|
-
import { printPugMascotPreInk } from './repl-splash-mascot.js';
|
|
25
|
-
import { collectWelcomeData } from './welcome-data.js';
|
|
26
|
-
import { ThemeProvider } from '../core/theme/context.js';
|
|
27
|
-
import { resolveTheme } from '../core/theme/state.js';
|
|
28
|
-
import { ReplSession, } from '../core/repl/session.js';
|
|
29
|
-
import { resolveWorkspaceContext } from '../core/repl/workspace-context.js';
|
|
30
|
-
import { createEngineBridge } from '../core/repl/engine-bridge.js';
|
|
31
|
-
import { profileFor, resolveIntensity } from '../core/engine/intensity.js';
|
|
32
|
-
import { SqliteSessionStore } from '../core/repl/store/index.js';
|
|
33
|
-
import { slugForCwd } from '../core/repl/history.js';
|
|
34
|
-
import { loadSettings } from '../core/settings.js';
|
|
35
|
-
import { buildRuntimeConfig } from '@pugi/sdk';
|
|
36
|
-
import { WorkingSet, buildRepoSkeleton, loadPugiIgnore, PugiWatcher, } from '../core/context/index.js';
|
|
37
|
-
/**
|
|
38
|
-
* Mount the REPL and resolve when the user exits via Ctrl+C × 2 or
|
|
39
|
-
* `/quit`. The session is closed (server-side stays alive; resume via
|
|
40
|
-
* `pugi resume <sessionId>` once that command exists).
|
|
41
|
-
*/
|
|
42
|
-
export async function renderRepl(options) {
|
|
43
|
-
// beta.9 CEO dogfood: claim stdin raw mode + alt-screen
|
|
44
|
-
// BEFORE any async bootstrap step so keystrokes typed during the
|
|
45
|
-
// [launch -> Ink mount] window cannot echo into the terminal in
|
|
46
|
-
// cooked mode. Previously openLocalStore (SQLite open) +
|
|
47
|
-
// bootstrapContext (chokidar start) could take hundreds of ms to
|
|
48
|
-
// multiple seconds on a fresh install / large repo; during that
|
|
49
|
-
// window stdin stayed in cooked mode and the terminal echoed
|
|
50
|
-
// every typed character literally onto the screen below the
|
|
51
|
-
// pre-printed mascot/header. The visible result was the operator's
|
|
52
|
-
// "ssssss" landing on the rendered status-bar bottom row (CEO
|
|
53
|
-
// screenshot: beta.8 REPL bug 2).
|
|
54
|
-
//
|
|
55
|
-
// The claim is idempotent with Ink's own raw-mode enable: Ink
|
|
56
|
-
// ref-counts setRawMode calls, and Node's stdin.setRawMode is
|
|
57
|
-
// safe to call twice with the same value. The pre-Ink claim acts
|
|
58
|
-
// as a "raw-mode floor" - whatever Ink does after mount layers on
|
|
59
|
-
// top, and our finally{} restore drops the floor only after Ink
|
|
60
|
-
// has cleanly torn down (or never mounted on a bootstrap crash).
|
|
61
|
-
const bootstrap = claimTerminalForRepl();
|
|
62
|
-
// beta.13 auto-init wire (CEO dogfood): scaffold the
|
|
63
|
-
// `.pugi/` workspace silently on REPL boot so launching `pugi` in a
|
|
64
|
-
// fresh cwd no longer demands an explicit `pugi init` round-trip.
|
|
65
|
-
// Idempotent — every helper inside scaffoldPugiWorkspace is a
|
|
66
|
-
// `*_IfMissing` write, so re-running over an existing workspace is
|
|
67
|
-
// a no-op. Fail-safe: any FS / perms error never blocks REPL launch.
|
|
68
|
-
// Operator escape hatch: PUGI_NO_AUTO_INIT=1.
|
|
69
|
-
//
|
|
70
|
-
// Beta.13 P2 fix: gate the scaffold on project-root markers
|
|
71
|
-
// so launching `pugi` from `$HOME` / `/tmp` / arbitrary dirs does NOT
|
|
72
|
-
// sprinkle `.pugi/` directories all over the filesystem. The gate
|
|
73
|
-
// mirrors `isBoundWorkspace` from workspace-context.ts but also
|
|
74
|
-
// accepts non-JS roots (Cargo / pyproject / go.mod) because the CLI
|
|
75
|
-
// is language-agnostic and an operator working in a Rust repo deserves
|
|
76
|
-
// the same auto-init UX as a Node operator. Already-bound `.pugi/`
|
|
77
|
-
// dirs also opt back in so the scaffold can fill any missing
|
|
78
|
-
// sub-artifacts the operator deleted.
|
|
79
|
-
// `--bare` (PUGI_BARE=1) ALSO suppresses the
|
|
80
|
-
// auto-init scaffold. Bare mode is the deterministic "fresh install
|
|
81
|
-
// anywhere" path — no `.pugi/` writes, no PUGI.md scaffold, no
|
|
82
|
-
// settings.json seed. The pre-existing PUGI_NO_AUTO_INIT escape
|
|
83
|
-
// hatch stays — bare mode just unions with it.
|
|
84
|
-
const { isBareMode } = await import('../core/bare-mode/index.js');
|
|
85
|
-
if (process.env.PUGI_NO_AUTO_INIT !== '1' &&
|
|
86
|
-
!isBareMode() &&
|
|
87
|
-
isProjectRoot(process.cwd())) {
|
|
88
|
-
try {
|
|
89
|
-
const { scaffoldPugiWorkspace } = await import('../runtime/cli.js');
|
|
90
|
-
await scaffoldPugiWorkspace({
|
|
91
|
-
cwd: process.cwd(),
|
|
92
|
-
noDefaults: true,
|
|
93
|
-
log: () => {
|
|
94
|
-
/* silent — never leak scaffold progress into the REPL alt-screen */
|
|
95
|
-
},
|
|
96
|
-
});
|
|
97
|
-
}
|
|
98
|
-
catch (err) {
|
|
99
|
-
// Fail-safe: read-only FS or perms error never blocks REPL launch.
|
|
100
|
-
// Beta.13 P2 fix: bare-catch swallowed the diagnostic;
|
|
101
|
-
// surface it on stderr under PUGI_DEBUG=1 so operator-triage on
|
|
102
|
-
// "why isn't .pugi/ being created?" has a starting point without
|
|
103
|
-
// having to re-instrument the bootstrap.
|
|
104
|
-
if (process.env.PUGI_DEBUG === '1') {
|
|
105
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
106
|
-
process.stderr.write(`[pugi-debug] auto-init failed: ${msg}\n`);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
const transport = createProductionTransport();
|
|
111
|
-
// Auto-bind the workspace context from process.cwd() so Pugi knows
|
|
112
|
-
// which repo the operator launched the CLI in. The resolver is
|
|
113
|
-
// best-effort — any FS error falls back to a basename-only summary,
|
|
114
|
-
// never blocks REPL launch. fix.
|
|
115
|
-
const workspace = options.workspace ?? resolveWorkspaceContext(process.cwd());
|
|
116
|
-
// Beta.13 P1 fix: read `ui.cyberZoo` from
|
|
117
|
-
// `.pugi/settings.json` so the operator's splash posture flows to
|
|
118
|
-
// admin-api on session open. Without this, the renderer's `cyberZoo`
|
|
119
|
-
// parameter (added beta.13) was always defaulted to 'on' regardless
|
|
120
|
-
// of the operator's actual setting.
|
|
121
|
-
const cyberZoo = readCyberZooSetting(process.cwd());
|
|
122
|
-
// : open the local SessionStore for `/resume` persistence. The
|
|
123
|
-
// store lives under `~/.pugi/projects/<slug>/`; failure is fail-safe
|
|
124
|
-
// — we log a one-line warning to stderr and continue with the REPL
|
|
125
|
-
// in memory-only mode. Lock-busy errors get the friendliest message
|
|
126
|
-
// so an operator running two REPLs in the same project understands
|
|
127
|
-
// the constraint.
|
|
128
|
-
const projectSlug = slugForCwd(process.cwd());
|
|
129
|
-
const { store, openedSessionId } = await openLocalStore({
|
|
130
|
-
projectSlug,
|
|
131
|
-
workspaceRoot: process.cwd(),
|
|
132
|
-
resumeLocalSessionId: options.resumeLocalSessionId,
|
|
133
|
-
});
|
|
134
|
-
// three-tier context bootstrap. The skeleton + working set
|
|
135
|
-
// + watcher are local-first and best-effort: every step is wrapped
|
|
136
|
-
// in try/catch so an unreadable workspace never blocks REPL launch.
|
|
137
|
-
// Opt-out via PUGI_DISABLE_CONTEXT=1 for hermetic test runs.
|
|
138
|
-
const { skeleton, workingSet, watcher } = await bootstrapContext({
|
|
139
|
-
cwd: process.cwd(),
|
|
140
|
-
env: process.env,
|
|
141
|
-
});
|
|
142
|
-
// PUGI-538c () -- wire the production engine bridge so
|
|
143
|
-
// `<pugi-tool-route>` envelopes emitted by Pugi's coordinator
|
|
144
|
-
// actually drive a local `NativePugiEngineAdapter` run instead of
|
|
145
|
-
// surfacing "Engine bridge not configured" on the system line. The
|
|
146
|
-
// factory closure resolves `cwd` per invocation so an operator who
|
|
147
|
-
// `cd`-ed mid-session writes files into the new directory (matches
|
|
148
|
-
// `pugi code` direct-path behaviour).
|
|
149
|
-
//
|
|
150
|
-
// The bridge runtime config reuses the REPL transport credentials so
|
|
151
|
-
// a single token authenticates both the coordinator brief (via
|
|
152
|
-
// admin-api /api/pugi/sessions) AND the bridged engine call (via
|
|
153
|
-
// /api/pugi/engine). No new credential surface, no double login.
|
|
154
|
-
//
|
|
155
|
-
// Fail-safe construction: `buildRuntimeConfig` validates apiUrl via
|
|
156
|
-
// `z.string().url()` and throws on a malformed value. We must not
|
|
157
|
-
// crash REPL launch on that path -- every other bootstrap step in
|
|
158
|
-
// this file (auto-init, openLocalStore, bootstrapContext, watcher)
|
|
159
|
-
// is fail-safe. On construction failure we surface a one-line stderr
|
|
160
|
-
// diagnostic under PUGI_DEBUG=1 and pass `engineBridge: undefined`
|
|
161
|
-
// so the REPL launches; the operator sees the legacy "Engine bridge
|
|
162
|
-
// not configured" system line on the first routed brief, which is
|
|
163
|
-
// the same UX as a pre-PUGI-538c CLI build.
|
|
164
|
-
let engineBridge;
|
|
165
|
-
try {
|
|
166
|
-
// PR A (2026-06-05): resolve intensity profile once at bridge
|
|
167
|
-
// construction time. The bridge stays pure and re-uses the resolved
|
|
168
|
-
// profile across every routed brief. `resolveIntensity` reads env
|
|
169
|
-
// (`PUGI_INTENSITY`) и settings.json; the REPL launch lifecycle is
|
|
170
|
-
// the natural binding point. A future `/intensity` REPL command
|
|
171
|
-
// will rebuild the bridge to swap the profile mid-session.
|
|
172
|
-
const intensityLevel = resolveIntensity({});
|
|
173
|
-
const intensityProfile = profileFor(intensityLevel);
|
|
174
|
-
engineBridge = createEngineBridge({
|
|
175
|
-
config: buildRuntimeConfig({
|
|
176
|
-
apiUrl: options.apiUrl,
|
|
177
|
-
apiKey: options.apiKey,
|
|
178
|
-
}),
|
|
179
|
-
cwd: () => process.cwd(),
|
|
180
|
-
intensityProfile,
|
|
181
|
-
});
|
|
182
|
-
}
|
|
183
|
-
catch (err) {
|
|
184
|
-
if (process.env.PUGI_DEBUG === '1') {
|
|
185
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
186
|
-
process.stderr.write(`[pugi-debug] engine bridge bootstrap failed: ${msg}\n`);
|
|
187
|
-
}
|
|
188
|
-
engineBridge = undefined;
|
|
189
|
-
}
|
|
190
|
-
const session = new ReplSession({
|
|
191
|
-
apiUrl: options.apiUrl,
|
|
192
|
-
apiKey: options.apiKey,
|
|
193
|
-
workspaceLabel: options.workspaceLabel,
|
|
194
|
-
cliVersion: options.cliVersion,
|
|
195
|
-
transport,
|
|
196
|
-
...(engineBridge !== undefined ? { engineBridge } : {}),
|
|
197
|
-
workspace,
|
|
198
|
-
cyberZoo,
|
|
199
|
-
store,
|
|
200
|
-
localSessionId: openedSessionId,
|
|
201
|
-
repoSkeleton: skeleton,
|
|
202
|
-
workingSet,
|
|
203
|
-
watcher,
|
|
204
|
-
});
|
|
205
|
-
// Restore the transcript from the JSONL log if we resumed an
|
|
206
|
-
// existing session. The restore is idempotent and bypasses persist
|
|
207
|
-
// (no double-write of replayed rows).
|
|
208
|
-
if (store && openedSessionId && options.resumeLocalSessionId) {
|
|
209
|
-
try {
|
|
210
|
-
const events = await store.loadEvents(openedSessionId, { limit: 500 });
|
|
211
|
-
session.restoreTranscript(events);
|
|
212
|
-
}
|
|
213
|
-
catch (error) {
|
|
214
|
-
const msg = error instanceof Error ? error.message : String(error);
|
|
215
|
-
process.stderr.write(`[pugi] Could not restore session ${openedSessionId.slice(0, 13)}: ${msg}\n`);
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
// Kick off the connect; the Repl renders the connecting state until
|
|
219
|
-
// the session pushes `connection: 'on_watch'` from the SSE onOpen.
|
|
220
|
-
void session.start();
|
|
221
|
-
// beta.9: drain any keystrokes that landed in stdin between the
|
|
222
|
-
// pre-Ink raw-mode claim and now. Without this, the queued bytes
|
|
223
|
-
// would feed Ink's first useInput tick as a flood of "stale"
|
|
224
|
-
// characters once the InputBox mounts - the operator would see
|
|
225
|
-
// their pre-typed input materialise in the prompt as if they had
|
|
226
|
-
// typed it after the REPL became interactive. Idempotent: no-op
|
|
227
|
-
// when stdin is not a TTY or no bytes were buffered.
|
|
228
|
-
drainBufferedStdin(process.stdin);
|
|
229
|
-
// wave 5: paint the chafa-baked brand-pug ANSI render to
|
|
230
|
-
// stdout BEFORE Ink mounts (but AFTER alt-screen enter). Ink's
|
|
231
|
-
// layout engine would mis-measure the truecolor escape sequences,
|
|
232
|
-
// so the pug must land verbatim. The flag is passed into <Repl />
|
|
233
|
-
// so the splash component knows to skip its own hand-crafted
|
|
234
|
-
// PUG_MASCOT column - otherwise the operator sees both the chafa
|
|
235
|
-
// pug AND the ASCII fallback stacked. When skipSplash is true
|
|
236
|
-
// (operator opted out via --no-splash), we suppress the pre-print
|
|
237
|
-
// too so the boot stays silent.
|
|
238
|
-
const mascotPrePrinted = options.skipSplash === true ? false : printPugMascotPreInk(process.stdout);
|
|
239
|
-
// resolve the active theme ONCE at mount
|
|
240
|
-
// and wrap `<Repl />` in `<ThemeProvider>` so every Ink consumer
|
|
241
|
-
// (`<Header>`, `<DoctorTable>`, `<StyleTable>`, `<ThemeTable>`,
|
|
242
|
-
// …) picks up the same color tokens. The provider is stable for
|
|
243
|
-
// the lifetime of the REPL — operator `/theme <name>` writes to
|
|
244
|
-
// disk + appends a system line, and the next `pugi` launch re-
|
|
245
|
-
// mounts with the new slug. Re-mounting mid-session would race
|
|
246
|
-
// against Ink's raw-mode handler so we deliberately keep the
|
|
247
|
-
// session-lifetime contract instead of polling the config file.
|
|
248
|
-
const resolvedTheme = resolveTheme({
|
|
249
|
-
workspaceRoot: process.cwd(),
|
|
250
|
-
env: process.env,
|
|
251
|
-
});
|
|
252
|
-
// CEO P0 #2 : collect welcome banner data BEFORE Ink
|
|
253
|
-
// mounts so the banner paints on the first frame instead of swapping
|
|
254
|
-
// in mid-render. The collector swallows every IO error so а missing
|
|
255
|
-
// CHANGELOG / unreadable credential / malformed settings never
|
|
256
|
-
// blocks the boot.
|
|
257
|
-
let welcomeData;
|
|
258
|
-
if (options.skipSplash !== true) {
|
|
259
|
-
try {
|
|
260
|
-
welcomeData = collectWelcomeData({
|
|
261
|
-
cliVersion: options.cliVersion,
|
|
262
|
-
cwd: process.cwd(),
|
|
263
|
-
});
|
|
264
|
-
}
|
|
265
|
-
catch {
|
|
266
|
-
welcomeData = undefined;
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
const instance = render(React.createElement(ThemeProvider, { slug: resolvedTheme.slug }, React.createElement(Repl, {
|
|
270
|
-
session,
|
|
271
|
-
updateBanner: options.updateBanner ?? null,
|
|
272
|
-
skipSplash: options.skipSplash === true,
|
|
273
|
-
hideToolStream: options.hideToolStream === true,
|
|
274
|
-
mascotPrePrinted,
|
|
275
|
-
welcomeData,
|
|
276
|
-
autoInitStatus: options.autoInitStatus ?? null,
|
|
277
|
-
})), {
|
|
278
|
-
// PUGI-534 — Ink kills the process on the first raw Ctrl+C by
|
|
279
|
-
// default, which beat the InputBox useInput double-tap timer to
|
|
280
|
-
// the punch (operator reported single ^C exits the REPL). Disable
|
|
281
|
-
// Ink's built-in handler so the InputBox `lastCtrlCAt` window owns
|
|
282
|
-
// the exit gesture: first press → cancel + toast, second press
|
|
283
|
-
// within 1s → `handleExit()` → `useApp().exit()`. SIGINT handlers
|
|
284
|
-
// on the process object (sigint-guard + per-engine-task in
|
|
285
|
-
// runtime/cli.ts) still fire as defence-in-depth on the rare
|
|
286
|
-
// non-raw fallback path.
|
|
287
|
-
exitOnCtrlC: false,
|
|
288
|
-
});
|
|
289
|
-
// Make sure we leave the alt screen on abrupt exits too. Without
|
|
290
|
-
// this the operator's shell stays "frozen" on the Pugi splash.
|
|
291
|
-
process.once('exit', bootstrap.restore);
|
|
292
|
-
process.once('SIGINT', bootstrap.restore);
|
|
293
|
-
process.once('SIGTERM', bootstrap.restore);
|
|
294
|
-
try {
|
|
295
|
-
await instance.waitUntilExit();
|
|
296
|
-
}
|
|
297
|
-
finally {
|
|
298
|
-
bootstrap.restore();
|
|
299
|
-
session.close();
|
|
300
|
-
if (store) {
|
|
301
|
-
try {
|
|
302
|
-
await store.close();
|
|
303
|
-
}
|
|
304
|
-
catch {
|
|
305
|
-
/* idempotent — already closed */
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
if (watcher) {
|
|
309
|
-
try {
|
|
310
|
-
await watcher.close();
|
|
311
|
-
}
|
|
312
|
-
catch {
|
|
313
|
-
/* idempotent — chokidar may already be torn down */
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
export function claimTerminalForRepl(stdin = process.stdin, stdout = process.stdout) {
|
|
319
|
-
const isStdoutTty = stdout.isTTY === true;
|
|
320
|
-
const isStdinTty = stdin.isTTY === true && typeof stdin.setRawMode === 'function';
|
|
321
|
-
let altScreenEntered = false;
|
|
322
|
-
if (isStdoutTty) {
|
|
323
|
-
try {
|
|
324
|
-
stdout.write('\x1b[?1049h');
|
|
325
|
-
stdout.write('\x1b[H');
|
|
326
|
-
altScreenEntered = true;
|
|
327
|
-
}
|
|
328
|
-
catch {
|
|
329
|
-
/* terminal already detached */
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
let rawModeClaimed = false;
|
|
333
|
-
if (isStdinTty) {
|
|
334
|
-
try {
|
|
335
|
-
stdin.setEncoding('utf8');
|
|
336
|
-
stdin.setRawMode(true);
|
|
337
|
-
// Resume so the kernel actually delivers bytes to Node's event
|
|
338
|
-
// loop. Without resume, raw mode is set but data does not flow
|
|
339
|
-
// until something else (e.g. Ink) attaches a 'data' listener.
|
|
340
|
-
stdin.resume();
|
|
341
|
-
rawModeClaimed = true;
|
|
342
|
-
}
|
|
343
|
-
catch {
|
|
344
|
-
/* raw mode unsupported - the operator's shell still works */
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
let restored = false;
|
|
348
|
-
const restore = () => {
|
|
349
|
-
if (restored)
|
|
350
|
-
return;
|
|
351
|
-
restored = true;
|
|
352
|
-
if (rawModeClaimed && isStdinTty) {
|
|
353
|
-
try {
|
|
354
|
-
stdin.setRawMode(false);
|
|
355
|
-
}
|
|
356
|
-
catch {
|
|
357
|
-
/* terminal already detached */
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
if (altScreenEntered) {
|
|
361
|
-
try {
|
|
362
|
-
stdout.write('\x1b[?1049l');
|
|
363
|
-
}
|
|
364
|
-
catch {
|
|
365
|
-
/* shutdown race - terminal already detached */
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
};
|
|
369
|
-
return { altScreenEntered, rawModeClaimed, restore };
|
|
370
|
-
}
|
|
371
|
-
/**
|
|
372
|
-
* Read and discard any bytes buffered in stdin between
|
|
373
|
-
* `claimTerminalForRepl()` and the Ink mount. Returns the number of
|
|
374
|
-
* bytes drained so tests can assert the behaviour without intercepting
|
|
375
|
-
* the side effect.
|
|
376
|
-
*
|
|
377
|
-
* `stdin.read()` is a no-op when no data is buffered, so this is safe
|
|
378
|
-
* to call whether or not the operator actually typed during bootstrap.
|
|
379
|
-
* Wrapped in try/catch because a closed / piped stdin will throw on
|
|
380
|
-
* read in some Node versions.
|
|
381
|
-
*/
|
|
382
|
-
export function drainBufferedStdin(stdin = process.stdin) {
|
|
383
|
-
if (stdin.isTTY !== true)
|
|
384
|
-
return 0;
|
|
385
|
-
try {
|
|
386
|
-
let bytesDrained = 0;
|
|
387
|
-
// Loop until read() returns null - readable streams may chunk
|
|
388
|
-
// buffered bytes across multiple read() calls when the operator
|
|
389
|
-
// typed faster than the kernel could deliver to Node's loop.
|
|
390
|
-
for (;;) {
|
|
391
|
-
const chunk = stdin.read();
|
|
392
|
-
if (chunk === null)
|
|
393
|
-
return bytesDrained;
|
|
394
|
-
bytesDrained += typeof chunk === 'string' ? chunk.length : chunk.byteLength;
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
catch {
|
|
398
|
-
return 0;
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
/**
|
|
402
|
-
* Project-root probe — beta.13 P2 fix.
|
|
403
|
-
*
|
|
404
|
-
* Beta.13 auto-init was unconditional and silently created `.pugi/` in
|
|
405
|
-
* every cwd the REPL was launched from, including `$HOME` and `/tmp`.
|
|
406
|
-
* Operators who ran `pugi` to ask a quick question outside of any
|
|
407
|
-
* project ended up with stray `.pugi/` directories polluting their
|
|
408
|
-
* filesystem. The gate looks for any of six project-root markers
|
|
409
|
-
* before scaffolding:
|
|
410
|
-
*
|
|
411
|
-
* - `package.json` — JS / TS workspaces
|
|
412
|
-
* - `.git` — any cloned repo regardless of language
|
|
413
|
-
* - `.pugi` — already-bound Pugi workspace (re-scaffold
|
|
414
|
-
* fills any missing artifacts the operator
|
|
415
|
-
* deleted, idempotent over existing files)
|
|
416
|
-
* - `Cargo.toml` — Rust crates
|
|
417
|
-
* - `pyproject.toml` — Python projects (PEP 518)
|
|
418
|
-
* - `go.mod` — Go modules
|
|
419
|
-
*
|
|
420
|
-
* The probe is six cheap `existsSync` calls; the cost is negligible
|
|
421
|
-
* compared with the alt-screen + Ink mount that follows. Exported so a
|
|
422
|
-
* future unit spec can lock the contract.
|
|
423
|
-
*/
|
|
424
|
-
export function isProjectRoot(cwd) {
|
|
425
|
-
// ESM static imports — `require()` is not defined in a `"type": "module"`
|
|
426
|
-
// bundle and would throw `ReferenceError: require is not defined` the
|
|
427
|
-
// moment the REPL bootstrap calls this gate. Beta.16 P0 fix.
|
|
428
|
-
return (existsSync(resolve(cwd, 'package.json')) ||
|
|
429
|
-
existsSync(resolve(cwd, '.git')) ||
|
|
430
|
-
existsSync(resolve(cwd, '.pugi')) ||
|
|
431
|
-
existsSync(resolve(cwd, 'Cargo.toml')) ||
|
|
432
|
-
existsSync(resolve(cwd, 'pyproject.toml')) ||
|
|
433
|
-
existsSync(resolve(cwd, 'go.mod')));
|
|
434
|
-
}
|
|
435
|
-
/**
|
|
436
|
-
* Read the operator's cyber-zoo posture from `.pugi/settings.json`.
|
|
437
|
-
* Best-effort: when the file is missing / malformed, fall through to
|
|
438
|
-
* the historical 'on' default so the REPL never refuses to launch on
|
|
439
|
-
* a settings error. Beta.13 P1 fix.
|
|
440
|
-
*/
|
|
441
|
-
function readCyberZooSetting(cwd) {
|
|
442
|
-
try {
|
|
443
|
-
const settings = loadSettings(cwd);
|
|
444
|
-
return settings.ui?.cyberZoo ?? 'on';
|
|
445
|
-
}
|
|
446
|
-
catch {
|
|
447
|
-
return 'on';
|
|
448
|
-
}
|
|
449
|
-
}
|
|
450
|
-
/**
|
|
451
|
-
* Open the local SessionStore for the REPL bootstrap. Returns
|
|
452
|
-
* `{ store: null, openedSessionId: undefined }` on any error so the
|
|
453
|
-
* caller falls through to memory-only mode rather than failing the
|
|
454
|
-
* launch. The one error we surface verbatim is the lock-busy case —
|
|
455
|
-
* that one is operator-actionable.
|
|
456
|
-
*/
|
|
457
|
-
async function openLocalStore(input) {
|
|
458
|
-
// Honour an explicit opt-out for offline-strict environments / CI.
|
|
459
|
-
// PUGI_DISABLE_SESSION_STORE=1 wipes the integration to zero. Useful
|
|
460
|
-
// for hermetic test runs and for operators who do not want any
|
|
461
|
-
// persistence under $HOME.
|
|
462
|
-
if (process.env.PUGI_DISABLE_SESSION_STORE === '1') {
|
|
463
|
-
return { store: null, openedSessionId: undefined };
|
|
464
|
-
}
|
|
465
|
-
try {
|
|
466
|
-
const store = new SqliteSessionStore({ projectSlug: input.projectSlug });
|
|
467
|
-
const row = await store.open({
|
|
468
|
-
id: input.resumeLocalSessionId,
|
|
469
|
-
workspaceRoot: input.workspaceRoot,
|
|
470
|
-
projectSlug: input.projectSlug,
|
|
471
|
-
});
|
|
472
|
-
return { store, openedSessionId: row.id };
|
|
473
|
-
}
|
|
474
|
-
catch (error) {
|
|
475
|
-
const code = error?.code;
|
|
476
|
-
const msg = error instanceof Error ? error.message : String(error);
|
|
477
|
-
if (code === 'EBUSY_SESSION_LOCK') {
|
|
478
|
-
process.stderr.write(`[pugi] ${msg} Continuing without local session persistence.\n`);
|
|
479
|
-
}
|
|
480
|
-
else {
|
|
481
|
-
process.stderr.write(`[pugi] Local session store unavailable (${msg}). Continuing in memory-only mode.\n`);
|
|
482
|
-
}
|
|
483
|
-
return { store: null, openedSessionId: undefined };
|
|
484
|
-
}
|
|
485
|
-
}
|
|
486
|
-
/**
|
|
487
|
-
* Bootstrap the three-tier context primitives:
|
|
488
|
-
*
|
|
489
|
-
* - Tier 0: `RepoSkeleton` (~5KB ASCII tree + meta) for prompt injection.
|
|
490
|
-
* - Tier 1: `WorkingSet` LRU bounded at 50 entries.
|
|
491
|
-
* - Filewatch: chokidar started against cwd, ignore-filtered.
|
|
492
|
-
*
|
|
493
|
-
* The bootstrap is fail-safe: every primitive is wrapped so the REPL
|
|
494
|
-
* still launches when (e.g.) chokidar refuses to start on a
|
|
495
|
-
* permission-blocked dir. The PUGI_DISABLE_CONTEXT=1 env var skips
|
|
496
|
-
* the bootstrap entirely for hermetic test runs and for operators
|
|
497
|
-
* who want a zero-touch REPL.
|
|
498
|
-
*/
|
|
499
|
-
async function bootstrapContext(input) {
|
|
500
|
-
if (input.env.PUGI_DISABLE_CONTEXT === '1') {
|
|
501
|
-
return { skeleton: null, workingSet: null, watcher: null };
|
|
502
|
-
}
|
|
503
|
-
let ignore;
|
|
504
|
-
try {
|
|
505
|
-
ignore = loadPugiIgnore(input.cwd);
|
|
506
|
-
}
|
|
507
|
-
catch (error) {
|
|
508
|
-
const msg = error instanceof Error ? error.message : String(error);
|
|
509
|
-
process.stderr.write(`[pugi] Three-tier context bootstrap skipped (ignore matcher failed: ${msg}).\n`);
|
|
510
|
-
return { skeleton: null, workingSet: null, watcher: null };
|
|
511
|
-
}
|
|
512
|
-
let skeleton = null;
|
|
513
|
-
try {
|
|
514
|
-
skeleton = buildRepoSkeleton(input.cwd, { ignore });
|
|
515
|
-
}
|
|
516
|
-
catch (error) {
|
|
517
|
-
const msg = error instanceof Error ? error.message : String(error);
|
|
518
|
-
process.stderr.write(`[pugi] Repo skeleton bootstrap failed (${msg}). Continuing without Tier 0.\n`);
|
|
519
|
-
}
|
|
520
|
-
const workingSet = new WorkingSet();
|
|
521
|
-
let watcher = null;
|
|
522
|
-
// chokidar opt-out: PUGI_DISABLE_FILEWATCH=1 keeps Tier 0/1 wired
|
|
523
|
-
// but skips the live-update channel. Useful on CI runners and on
|
|
524
|
-
// network mounts where fsevents misbehaves.
|
|
525
|
-
if (input.env.PUGI_DISABLE_FILEWATCH !== '1') {
|
|
526
|
-
try {
|
|
527
|
-
const w = new PugiWatcher({ cwd: input.cwd, ignore });
|
|
528
|
-
await w.start();
|
|
529
|
-
watcher = w;
|
|
530
|
-
}
|
|
531
|
-
catch (error) {
|
|
532
|
-
const msg = error instanceof Error ? error.message : String(error);
|
|
533
|
-
process.stderr.write(`[pugi] Filewatch bootstrap failed (${msg}). Continuing without live updates.\n`);
|
|
534
|
-
}
|
|
535
|
-
}
|
|
536
|
-
return { skeleton, workingSet, watcher };
|
|
537
|
-
}
|
|
538
|
-
/* ------------------------------------------------------------------ */
|
|
539
|
-
/* Production transport */
|
|
540
|
-
/* ------------------------------------------------------------------ */
|
|
541
|
-
export function createProductionTransport() {
|
|
542
|
-
return {
|
|
543
|
-
async createSession({ apiUrl, apiKey, workspace, cyberZoo }) {
|
|
544
|
-
// Forward the workspace bundle in the POST body so admin-api can
|
|
545
|
-
// surface `<workspace-context>` in Pugi's prompt. Older admin-api
|
|
546
|
-
// builds ignore unknown fields, so this stays forward-compatible.
|
|
547
|
-
// fix.
|
|
548
|
-
//
|
|
549
|
-
// Beta.13 P1 fix: also forward `cyberZoo` so admin-api
|
|
550
|
-
// can render Pugi's `<cyber-zoo>` marker matching the operator's
|
|
551
|
-
// `.pugi/settings.json::ui.cyberZoo` toggle instead of the
|
|
552
|
-
// historical 'on' default. Only included on the wire when set
|
|
553
|
-
// explicitly so a missing setting still survives older admin-api
|
|
554
|
-
// builds that do not declare the DTO field.
|
|
555
|
-
const body = {};
|
|
556
|
-
if (workspace?.workspaceCwd)
|
|
557
|
-
body.workspaceCwd = workspace.workspaceCwd;
|
|
558
|
-
if (workspace?.workspaceSlug)
|
|
559
|
-
body.workspaceSlug = workspace.workspaceSlug;
|
|
560
|
-
if (workspace?.workspaceSummary)
|
|
561
|
-
body.workspaceSummary = workspace.workspaceSummary;
|
|
562
|
-
if (cyberZoo === 'on' || cyberZoo === 'off')
|
|
563
|
-
body.cyberZoo = cyberZoo;
|
|
564
|
-
const response = await fetch(joinUrl(apiUrl, '/api/pugi/sessions'), {
|
|
565
|
-
method: 'POST',
|
|
566
|
-
headers: jsonHeaders(apiKey),
|
|
567
|
-
body: JSON.stringify(body),
|
|
568
|
-
});
|
|
569
|
-
const json = await readJson(response);
|
|
570
|
-
const sessionId = json.sessionId;
|
|
571
|
-
if (typeof sessionId !== 'string' || sessionId.length === 0) {
|
|
572
|
-
throw new Error('admin-api did not return a sessionId');
|
|
573
|
-
}
|
|
574
|
-
return { sessionId };
|
|
575
|
-
},
|
|
576
|
-
async postBrief({ apiUrl, apiKey, sessionId, brief }) {
|
|
577
|
-
const response = await fetch(joinUrl(apiUrl, `/api/pugi/sessions/${encodeURIComponent(sessionId)}/brief`), {
|
|
578
|
-
method: 'POST',
|
|
579
|
-
headers: jsonHeaders(apiKey),
|
|
580
|
-
body: JSON.stringify({ brief }),
|
|
581
|
-
});
|
|
582
|
-
const json = await readJson(response);
|
|
583
|
-
const dispatchId = json.dispatchId;
|
|
584
|
-
if (typeof dispatchId !== 'string' || dispatchId.length === 0) {
|
|
585
|
-
throw new Error('admin-api did not return a dispatchId');
|
|
586
|
-
}
|
|
587
|
-
return { dispatchId };
|
|
588
|
-
},
|
|
589
|
-
async postStop({ apiUrl, apiKey, sessionId, persona }) {
|
|
590
|
-
const response = await fetch(joinUrl(apiUrl, `/api/pugi/sessions/${encodeURIComponent(sessionId)}/stop`), {
|
|
591
|
-
method: 'POST',
|
|
592
|
-
headers: jsonHeaders(apiKey),
|
|
593
|
-
body: JSON.stringify({ persona }),
|
|
594
|
-
});
|
|
595
|
-
const json = await readJson(response);
|
|
596
|
-
const stopped = Boolean(json.stopped);
|
|
597
|
-
return { stopped };
|
|
598
|
-
},
|
|
599
|
-
subscribe({ apiUrl, apiKey, sessionId, lastEventId, onEvent, onError, onOpen }) {
|
|
600
|
-
const controller = new AbortController();
|
|
601
|
-
const url = joinUrl(apiUrl, `/api/pugi/sessions/${encodeURIComponent(sessionId)}/stream`);
|
|
602
|
-
const headers = {
|
|
603
|
-
Accept: 'text/event-stream',
|
|
604
|
-
Authorization: `Bearer ${apiKey}`,
|
|
605
|
-
};
|
|
606
|
-
if (lastEventId) {
|
|
607
|
-
headers['Last-Event-ID'] = lastEventId;
|
|
608
|
-
}
|
|
609
|
-
// beta.9 CEO dogfood: hard timeout on the SSE
|
|
610
|
-
// handshake so a CDN/proxy that buffers the response (or an
|
|
611
|
-
// admin-api that accepted the route but never flushed headers)
|
|
612
|
-
// cannot freeze the REPL in `connecting` forever. The 5s budget
|
|
613
|
-
// is generous - admin-api routinely responds in <500ms when
|
|
614
|
-
// healthy - but tight enough that an operator who launched
|
|
615
|
-
// `pugi` and is staring at the screen will see the status flip
|
|
616
|
-
// to `reconnecting` instead of an indefinite hang. The
|
|
617
|
-
// AbortController bound to the fetch aborts the in-flight
|
|
618
|
-
// request when the timer fires, which surfaces as an
|
|
619
|
-
// `AbortError` and routes through the existing onError handler
|
|
620
|
-
// (which calls scheduleReconnect via the session). The timer
|
|
621
|
-
// is cleared the moment onOpen fires so a slow-but-eventually-
|
|
622
|
-
// successful handshake still works.
|
|
623
|
-
const handshakeDeadlineMs = 5_000;
|
|
624
|
-
const handshakeTimer = setTimeout(() => {
|
|
625
|
-
controller.abort();
|
|
626
|
-
// onError is called from the catch block below (the abort
|
|
627
|
-
// synthesises an AbortError that consumeSseStream / fetch
|
|
628
|
-
// will throw). No explicit onError call here - we let the
|
|
629
|
-
// catch path normalise the error message so the operator
|
|
630
|
-
// sees the consistent "SSE handshake timed out (5s)" prose
|
|
631
|
-
// through the same plumbing that surfaces every other
|
|
632
|
-
// transport failure.
|
|
633
|
-
}, handshakeDeadlineMs);
|
|
634
|
-
void (async () => {
|
|
635
|
-
try {
|
|
636
|
-
const response = await fetch(url, {
|
|
637
|
-
method: 'GET',
|
|
638
|
-
headers,
|
|
639
|
-
signal: controller.signal,
|
|
640
|
-
});
|
|
641
|
-
if (!response.ok) {
|
|
642
|
-
throw new Error(`HTTP ${response.status} on SSE stream`);
|
|
643
|
-
}
|
|
644
|
-
if (!response.body) {
|
|
645
|
-
throw new Error('SSE response has no body');
|
|
646
|
-
}
|
|
647
|
-
// Handshake survived; cancel the deadline so a slow
|
|
648
|
-
// first-event stream does not get aborted later.
|
|
649
|
-
clearTimeout(handshakeTimer);
|
|
650
|
-
onOpen();
|
|
651
|
-
await consumeSseStream(response.body, onEvent);
|
|
652
|
-
// Server closed the stream cleanly. Treat as an error so
|
|
653
|
-
// the session reconnects (the spec says "transient
|
|
654
|
-
// disconnect" - a clean close from the server side is also
|
|
655
|
-
// transient because the operator may have toggled wifi).
|
|
656
|
-
onError(new Error('SSE stream ended'));
|
|
657
|
-
}
|
|
658
|
-
catch (error) {
|
|
659
|
-
clearTimeout(handshakeTimer);
|
|
660
|
-
if (controller.signal.aborted) {
|
|
661
|
-
// Distinguish operator-driven close (session.close())
|
|
662
|
-
// from the handshake-deadline abort. The session sets a
|
|
663
|
-
// `closed` flag before calling controller.abort(); the
|
|
664
|
-
// handshake-deadline abort fires while the session is
|
|
665
|
-
// still expecting onOpen. We cannot read session state
|
|
666
|
-
// from here, so we surface a single error class with a
|
|
667
|
-
// clear message - the session-side onError handler
|
|
668
|
-
// already short-circuits when `closed=true`.
|
|
669
|
-
onError(new Error(`SSE handshake timed out after ${handshakeDeadlineMs}ms`));
|
|
670
|
-
return;
|
|
671
|
-
}
|
|
672
|
-
onError(error instanceof Error ? error : new Error(String(error)));
|
|
673
|
-
}
|
|
674
|
-
})();
|
|
675
|
-
return {
|
|
676
|
-
close: () => {
|
|
677
|
-
clearTimeout(handshakeTimer);
|
|
678
|
-
controller.abort();
|
|
679
|
-
},
|
|
680
|
-
};
|
|
681
|
-
},
|
|
682
|
-
};
|
|
683
|
-
}
|
|
684
|
-
/* ------------------------------------------------------------------ */
|
|
685
|
-
/* SSE parser */
|
|
686
|
-
/* ------------------------------------------------------------------ */
|
|
687
|
-
/**
|
|
688
|
-
* Minimal SSE parser. Reads a UTF-8 stream of `id:` / `event:` / `data:`
|
|
689
|
-
* lines separated by blank lines. We only need the `data` payload (a
|
|
690
|
-
* JSON object) and the `id` field (so we can replay on reconnect via
|
|
691
|
-
* Last-Event-ID).
|
|
692
|
-
*/
|
|
693
|
-
async function consumeSseStream(body, onEvent) {
|
|
694
|
-
const reader = body.getReader();
|
|
695
|
-
const decoder = new TextDecoder('utf-8');
|
|
696
|
-
let buffer = '';
|
|
697
|
-
let currentId = '';
|
|
698
|
-
let currentData = '';
|
|
699
|
-
while (true) {
|
|
700
|
-
const { value, done } = await reader.read();
|
|
701
|
-
if (done)
|
|
702
|
-
break;
|
|
703
|
-
buffer += decoder.decode(value, { stream: true });
|
|
704
|
-
let newlineIndex;
|
|
705
|
-
while ((newlineIndex = buffer.indexOf('\n')) !== -1) {
|
|
706
|
-
const rawLine = buffer.slice(0, newlineIndex).replace(/\r$/, '');
|
|
707
|
-
buffer = buffer.slice(newlineIndex + 1);
|
|
708
|
-
if (rawLine.length === 0) {
|
|
709
|
-
// Dispatch - only if we have a data payload.
|
|
710
|
-
if (currentData.length > 0) {
|
|
711
|
-
try {
|
|
712
|
-
const parsed = JSON.parse(currentData);
|
|
713
|
-
onEvent(parsed, currentId);
|
|
714
|
-
}
|
|
715
|
-
catch {
|
|
716
|
-
// Drop malformed frames silently - protocol-level
|
|
717
|
-
// robustness is the controller's job, not the client's.
|
|
718
|
-
}
|
|
719
|
-
}
|
|
720
|
-
currentData = '';
|
|
721
|
-
currentId = '';
|
|
722
|
-
continue;
|
|
723
|
-
}
|
|
724
|
-
if (rawLine.startsWith(':'))
|
|
725
|
-
continue; // Comment / keepalive.
|
|
726
|
-
const colonIndex = rawLine.indexOf(':');
|
|
727
|
-
const field = colonIndex === -1 ? rawLine : rawLine.slice(0, colonIndex);
|
|
728
|
-
const value = colonIndex === -1 ? '' : rawLine.slice(colonIndex + 1).replace(/^ /, '');
|
|
729
|
-
switch (field) {
|
|
730
|
-
case 'id':
|
|
731
|
-
currentId = value;
|
|
732
|
-
break;
|
|
733
|
-
case 'data':
|
|
734
|
-
currentData = currentData.length === 0 ? value : `${currentData}\n${value}`;
|
|
735
|
-
break;
|
|
736
|
-
case 'event':
|
|
737
|
-
case 'retry':
|
|
738
|
-
default:
|
|
739
|
-
// We do not surface the `event:` name to the consumer - the
|
|
740
|
-
// payload itself carries `type`. Future events that need
|
|
741
|
-
// dispatcher-side routing without parsing JSON can wire
|
|
742
|
-
// through this branch.
|
|
743
|
-
break;
|
|
744
|
-
}
|
|
745
|
-
}
|
|
746
|
-
}
|
|
747
|
-
}
|
|
748
|
-
/* ------------------------------------------------------------------ */
|
|
749
|
-
/* Small helpers */
|
|
750
|
-
/* ------------------------------------------------------------------ */
|
|
751
|
-
function jsonHeaders(apiKey) {
|
|
752
|
-
return {
|
|
753
|
-
'Content-Type': 'application/json',
|
|
754
|
-
Accept: 'application/json',
|
|
755
|
-
Authorization: `Bearer ${apiKey}`,
|
|
756
|
-
};
|
|
757
|
-
}
|
|
758
|
-
async function readJson(response) {
|
|
759
|
-
if (!response.ok) {
|
|
760
|
-
const detail = await response.text().catch(() => '');
|
|
761
|
-
throw new Error(`HTTP ${response.status}${detail ? `: ${detail.slice(0, 200)}` : ''}`);
|
|
762
|
-
}
|
|
763
|
-
return response.json();
|
|
764
|
-
}
|
|
765
|
-
function joinUrl(base, path) {
|
|
766
|
-
const trimmedBase = base.endsWith('/') ? base.slice(0, -1) : base;
|
|
767
|
-
const trimmedPath = path.startsWith('/') ? path : `/${path}`;
|
|
768
|
-
return `${trimmedBase}${trimmedPath}`;
|
|
769
|
-
}
|
|
770
|
-
//# sourceMappingURL=repl-render.js.map
|