@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
|
@@ -1,242 +0,0 @@
|
|
|
1
|
-
export const STYLE_WORD_CAPS = Object.freeze({
|
|
2
|
-
minimal: 500,
|
|
3
|
-
standard: 1500,
|
|
4
|
-
detailed: 3500,
|
|
5
|
-
});
|
|
6
|
-
/** Render PUGI.md content for the requested tier. */
|
|
7
|
-
export function renderPugiMd(detection, options) {
|
|
8
|
-
const sections = [];
|
|
9
|
-
const style = options.style;
|
|
10
|
-
const now = options.now ?? (() => new Date());
|
|
11
|
-
sections.push(renderHeader(detection, now()));
|
|
12
|
-
sections.push(renderStack(detection));
|
|
13
|
-
sections.push(renderFrameworks(detection, style));
|
|
14
|
-
if (style !== 'minimal') {
|
|
15
|
-
sections.push(renderTesting(detection));
|
|
16
|
-
sections.push(renderCommitConvention(detection));
|
|
17
|
-
sections.push(renderMonorepo(detection));
|
|
18
|
-
sections.push(renderAiConfigs(detection, style));
|
|
19
|
-
sections.push(renderCommands(detection));
|
|
20
|
-
sections.push(renderProjectLayout(detection));
|
|
21
|
-
}
|
|
22
|
-
if (style === 'detailed') {
|
|
23
|
-
sections.push(renderContributorChecklist());
|
|
24
|
-
}
|
|
25
|
-
sections.push(renderFooter());
|
|
26
|
-
const body = sections.filter((s) => s.length > 0).join('\n\n');
|
|
27
|
-
const trimmed = enforceWordCap(body, STYLE_WORD_CAPS[style]);
|
|
28
|
-
return Object.freeze({
|
|
29
|
-
content: trimmed.endsWith('\n') ? trimmed : `${trimmed}\n`,
|
|
30
|
-
wordCount: countWords(trimmed),
|
|
31
|
-
style,
|
|
32
|
-
});
|
|
33
|
-
}
|
|
34
|
-
/* ------------------------------------------------------------------ */
|
|
35
|
-
/* Section renderers */
|
|
36
|
-
/* ------------------------------------------------------------------ */
|
|
37
|
-
function renderHeader(detection, now) {
|
|
38
|
-
const name = detection.stack.nodeName ?? deriveName(detection.cwd);
|
|
39
|
-
const date = now.toISOString().slice(0, 10);
|
|
40
|
-
const lines = [
|
|
41
|
-
`# ${name} — AI Context (PUGI.md)`,
|
|
42
|
-
'',
|
|
43
|
-
`Generated by \`pugi init\` on ${date}.`,
|
|
44
|
-
'Pugi reads this file on every session start. Keep it short and current —',
|
|
45
|
-
'if a section conflicts with the code, the code wins. Edit freely.',
|
|
46
|
-
];
|
|
47
|
-
return lines.join('\n');
|
|
48
|
-
}
|
|
49
|
-
function renderStack(detection) {
|
|
50
|
-
const lines = ['## Stack', ''];
|
|
51
|
-
const stack = detection.stack;
|
|
52
|
-
if (stack.kind === 'unknown') {
|
|
53
|
-
lines.push('No language manifest detected at the workspace root.');
|
|
54
|
-
if (detection.languages.length > 0) {
|
|
55
|
-
lines.push('');
|
|
56
|
-
lines.push(`Primary file extensions: ${detection.languages.join(', ')}.`);
|
|
57
|
-
}
|
|
58
|
-
return lines.join('\n');
|
|
59
|
-
}
|
|
60
|
-
const labelMap = {
|
|
61
|
-
node: 'Node.js / JavaScript / TypeScript',
|
|
62
|
-
python: 'Python',
|
|
63
|
-
rust: 'Rust',
|
|
64
|
-
go: 'Go',
|
|
65
|
-
ruby: 'Ruby',
|
|
66
|
-
php: 'PHP',
|
|
67
|
-
java: 'Java / Kotlin (JVM)',
|
|
68
|
-
};
|
|
69
|
-
lines.push(`- Primary: ${labelMap[stack.kind] ?? stack.kind}`);
|
|
70
|
-
if (stack.packageManager) {
|
|
71
|
-
lines.push(`- Package manager: ${stack.packageManager}`);
|
|
72
|
-
}
|
|
73
|
-
if (stack.manifests.length > 0) {
|
|
74
|
-
lines.push(`- Manifests: ${stack.manifests.join(', ')}`);
|
|
75
|
-
}
|
|
76
|
-
if (detection.languages.length > 0) {
|
|
77
|
-
lines.push(`- Languages observed: ${detection.languages.join(', ')}`);
|
|
78
|
-
}
|
|
79
|
-
return lines.join('\n');
|
|
80
|
-
}
|
|
81
|
-
function renderFrameworks(detection, style) {
|
|
82
|
-
if (detection.frameworks.length === 0)
|
|
83
|
-
return '';
|
|
84
|
-
const lines = ['## Frameworks', ''];
|
|
85
|
-
for (const fw of detection.frameworks) {
|
|
86
|
-
if (style === 'detailed') {
|
|
87
|
-
lines.push(`- **${fw.name}** — ${fw.evidence}`);
|
|
88
|
-
}
|
|
89
|
-
else {
|
|
90
|
-
lines.push(`- ${fw.name}`);
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
return lines.join('\n');
|
|
94
|
-
}
|
|
95
|
-
function renderTesting(detection) {
|
|
96
|
-
if (detection.testFrameworks.length === 0)
|
|
97
|
-
return '';
|
|
98
|
-
const lines = ['## Testing', ''];
|
|
99
|
-
lines.push(`Detected: ${detection.testFrameworks.join(', ')}.`);
|
|
100
|
-
return lines.join('\n');
|
|
101
|
-
}
|
|
102
|
-
function renderCommitConvention(detection) {
|
|
103
|
-
const lines = ['## Commit conventions', ''];
|
|
104
|
-
switch (detection.commitConvention) {
|
|
105
|
-
case 'conventional':
|
|
106
|
-
lines.push('Recent history follows Conventional Commits (`feat:`, `fix:`, `chore:`...).');
|
|
107
|
-
lines.push('Keep new commits in that style — auto-changelog tooling parses the prefix.');
|
|
108
|
-
break;
|
|
109
|
-
case 'gitmoji':
|
|
110
|
-
lines.push('Recent history uses gitmoji prefixes (`:sparkles:`, `:bug:`...).');
|
|
111
|
-
lines.push('Keep new commits in that style for visual consistency.');
|
|
112
|
-
break;
|
|
113
|
-
case 'freeform':
|
|
114
|
-
lines.push('Commits are freeform — no detected prefix convention.');
|
|
115
|
-
lines.push('Pick a short imperative subject line and keep body wrapped at 72 cols.');
|
|
116
|
-
break;
|
|
117
|
-
case 'unknown':
|
|
118
|
-
default:
|
|
119
|
-
lines.push('No commit history detected yet. Pick a convention (recommended: Conventional Commits).');
|
|
120
|
-
break;
|
|
121
|
-
}
|
|
122
|
-
return lines.join('\n');
|
|
123
|
-
}
|
|
124
|
-
function renderMonorepo(detection) {
|
|
125
|
-
if (!detection.monorepo)
|
|
126
|
-
return '';
|
|
127
|
-
const lines = ['## Monorepo', ''];
|
|
128
|
-
const kindLabel = {
|
|
129
|
-
pnpm: 'pnpm workspaces',
|
|
130
|
-
lerna: 'Lerna',
|
|
131
|
-
turborepo: 'Turborepo',
|
|
132
|
-
nx: 'Nx',
|
|
133
|
-
'yarn-workspaces': 'Yarn / npm workspaces',
|
|
134
|
-
};
|
|
135
|
-
lines.push(`- Layout: ${kindLabel[detection.monorepo.kind] ?? detection.monorepo.kind} (${detection.monorepo.file})`);
|
|
136
|
-
return lines.join('\n');
|
|
137
|
-
}
|
|
138
|
-
function renderAiConfigs(detection, style) {
|
|
139
|
-
if (detection.aiConfigs.length === 0)
|
|
140
|
-
return '';
|
|
141
|
-
const lines = ['## Existing AI configs', ''];
|
|
142
|
-
for (const cfg of detection.aiConfigs) {
|
|
143
|
-
lines.push(`- \`${cfg.path}\``);
|
|
144
|
-
}
|
|
145
|
-
if (style === 'detailed') {
|
|
146
|
-
for (const cfg of detection.aiConfigs) {
|
|
147
|
-
if (!cfg.excerpt)
|
|
148
|
-
continue;
|
|
149
|
-
lines.push('');
|
|
150
|
-
lines.push(`### ${cfg.name} excerpt`);
|
|
151
|
-
lines.push('');
|
|
152
|
-
lines.push('```');
|
|
153
|
-
const trimmed = cfg.excerpt.split('\n').slice(0, 20).join('\n');
|
|
154
|
-
lines.push(trimmed);
|
|
155
|
-
lines.push('```');
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
return lines.join('\n');
|
|
159
|
-
}
|
|
160
|
-
function renderCommands(detection) {
|
|
161
|
-
if (detection.scripts.length === 0)
|
|
162
|
-
return '';
|
|
163
|
-
const lines = ['## Commands', ''];
|
|
164
|
-
const pm = detection.stack.packageManager ?? 'npm';
|
|
165
|
-
lines.push('```bash');
|
|
166
|
-
for (const entry of detection.scripts) {
|
|
167
|
-
lines.push(commandLine(entry, pm));
|
|
168
|
-
}
|
|
169
|
-
lines.push('```');
|
|
170
|
-
return lines.join('\n');
|
|
171
|
-
}
|
|
172
|
-
function commandLine(entry, pm) {
|
|
173
|
-
const runner = pm === 'pnpm' || pm === 'yarn' || pm === 'bun' ? pm : 'npm run';
|
|
174
|
-
if (runner === 'npm run')
|
|
175
|
-
return `npm run ${entry.name}`;
|
|
176
|
-
return `${runner} ${entry.name}`;
|
|
177
|
-
}
|
|
178
|
-
function renderProjectLayout(detection) {
|
|
179
|
-
if (detection.topLevelDirs.length === 0)
|
|
180
|
-
return '';
|
|
181
|
-
const lines = ['## Project layout', ''];
|
|
182
|
-
for (const dir of detection.topLevelDirs) {
|
|
183
|
-
lines.push(`- \`${dir}/\``);
|
|
184
|
-
}
|
|
185
|
-
return lines.join('\n');
|
|
186
|
-
}
|
|
187
|
-
function renderContributorChecklist() {
|
|
188
|
-
return [
|
|
189
|
-
'## Contributor checklist',
|
|
190
|
-
'',
|
|
191
|
-
'- Read this file + any in-repo ARCHITECTURE / DESIGN docs before code changes.',
|
|
192
|
-
'- Run the project test suite before opening a PR.',
|
|
193
|
-
'- Keep secrets in `.env` (never commit).',
|
|
194
|
-
'- Update this file when stack / framework / commands change.',
|
|
195
|
-
].join('\n');
|
|
196
|
-
}
|
|
197
|
-
function renderFooter() {
|
|
198
|
-
return [
|
|
199
|
-
'## How Pugi uses this file',
|
|
200
|
-
'',
|
|
201
|
-
'On every session, Pugi loads this file as ambient context.',
|
|
202
|
-
'Edit it like a README aimed at an AI engineer joining your team.',
|
|
203
|
-
].join('\n');
|
|
204
|
-
}
|
|
205
|
-
/* ------------------------------------------------------------------ */
|
|
206
|
-
/* Word-cap enforcement */
|
|
207
|
-
/* ------------------------------------------------------------------ */
|
|
208
|
-
function countWords(text) {
|
|
209
|
-
const trimmed = text.trim();
|
|
210
|
-
if (trimmed.length === 0)
|
|
211
|
-
return 0;
|
|
212
|
-
return trimmed.split(/\s+/).length;
|
|
213
|
-
}
|
|
214
|
-
function enforceWordCap(body, cap) {
|
|
215
|
-
const current = countWords(body);
|
|
216
|
-
if (current <= cap)
|
|
217
|
-
return body;
|
|
218
|
-
// Greedy truncation: keep paragraphs in order until cap is reached,
|
|
219
|
-
// then append a truncation marker so the reader understands the file
|
|
220
|
-
// was clipped (deterministic).
|
|
221
|
-
const paragraphs = body.split(/\n{2,}/);
|
|
222
|
-
const kept = [];
|
|
223
|
-
let used = 0;
|
|
224
|
-
for (const paragraph of paragraphs) {
|
|
225
|
-
const words = countWords(paragraph);
|
|
226
|
-
if (used + words > cap)
|
|
227
|
-
break;
|
|
228
|
-
kept.push(paragraph);
|
|
229
|
-
used += words;
|
|
230
|
-
}
|
|
231
|
-
kept.push(`<!-- truncated to fit ${cap}-word cap -->`);
|
|
232
|
-
return kept.join('\n\n');
|
|
233
|
-
}
|
|
234
|
-
/* ------------------------------------------------------------------ */
|
|
235
|
-
/* Misc */
|
|
236
|
-
/* ------------------------------------------------------------------ */
|
|
237
|
-
function deriveName(cwd) {
|
|
238
|
-
const trimmed = cwd.replace(/\/+$/, '');
|
|
239
|
-
const last = trimmed.split('/').pop();
|
|
240
|
-
return last && last.length > 0 ? last : 'project';
|
|
241
|
-
}
|
|
242
|
-
//# sourceMappingURL=template-renderer.js.map
|
|
@@ -1,462 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* JobRegistry — Sprint .
|
|
3
|
-
*
|
|
4
|
-
* First-class persistent registry for background bash jobs. Lifts the
|
|
5
|
-
* inline `~/.pugi/jobs.json` management from `src/tools/bash.ts` and
|
|
6
|
-
* extends it with:
|
|
7
|
-
*
|
|
8
|
-
* - Atomic write (tempfile + rename) so concurrent CLI invocations
|
|
9
|
-
* cannot corrupt the ledger (Code Reviewer P2 retro on PR).
|
|
10
|
-
* - Stale-job reaping on every `list()` call: entries whose pid no
|
|
11
|
-
* longer corresponds to a live process are marked `abandoned`.
|
|
12
|
-
* - 24h retention for finished / killed / abandoned entries; older
|
|
13
|
-
* records are dropped silently.
|
|
14
|
-
* - Lifecycle status field (`running` / `finished` / `killed` /
|
|
15
|
-
* `failed` / `abandoned`) so the `pugi jobs` CLI can render an
|
|
16
|
-
* accurate snapshot regardless of which CLI invocation observed
|
|
17
|
-
* the exit.
|
|
18
|
-
*
|
|
19
|
-
* Storage stays at `~/.pugi/jobs.json` as a JSON array (not JSONL) so
|
|
20
|
-
* readers continue to work. The schema is additive: legacy
|
|
21
|
-
* entries lacking `id` / `status` / etc. are upgraded on read.
|
|
22
|
-
*/
|
|
23
|
-
import { randomUUID } from 'node:crypto';
|
|
24
|
-
import { closeSync, existsSync, fsyncSync, mkdirSync, openSync, readFileSync, renameSync, unlinkSync, writeSync, } from 'node:fs';
|
|
25
|
-
import { homedir } from 'node:os';
|
|
26
|
-
import { dirname, join } from 'node:path';
|
|
27
|
-
/**
|
|
28
|
-
* Retention window for non-`running` entries. 24 hours mirrors the
|
|
29
|
-
* default session window in `core/session.ts`; older jobs are dropped
|
|
30
|
-
* to keep the ledger bounded.
|
|
31
|
-
*/
|
|
32
|
-
export const JOB_RETENTION_MS = 24 * 60 * 60 * 1000;
|
|
33
|
-
/**
|
|
34
|
-
* Default grace period between SIGTERM and SIGKILL. Matches the bash
|
|
35
|
-
* tool's BASH_SIGKILL_GRACE_MS so foreground / background paths share
|
|
36
|
-
* the same shutdown contract.
|
|
37
|
-
*/
|
|
38
|
-
export const JOB_DEFAULT_GRACEFUL_MS = 5_000;
|
|
39
|
-
const DEFAULT_JOBS_PATH = join(homedir(), '.pugi', 'jobs.json');
|
|
40
|
-
/**
|
|
41
|
-
* Indirection point so tests can isolate the registry to a tmpdir
|
|
42
|
-
* without touching the user's real `~/.pugi/jobs.json`. The override
|
|
43
|
-
* lives at the module scope (single global) because the registry
|
|
44
|
-
* itself is a singleton — tests reset between cases with
|
|
45
|
-
* `resetJobRegistryForTesting`.
|
|
46
|
-
*/
|
|
47
|
-
let jobsPathOverride;
|
|
48
|
-
let singleton;
|
|
49
|
-
export function getJobRegistry() {
|
|
50
|
-
if (!singleton) {
|
|
51
|
-
singleton = new FileJobRegistry(resolveJobsPath());
|
|
52
|
-
}
|
|
53
|
-
return singleton;
|
|
54
|
-
}
|
|
55
|
-
/**
|
|
56
|
-
* Test-only hook. Resets the singleton so the next `getJobRegistry()`
|
|
57
|
-
* call binds to the (possibly overridden) jobs path again.
|
|
58
|
-
*/
|
|
59
|
-
export function resetJobRegistryForTesting(jobsPath) {
|
|
60
|
-
jobsPathOverride = jobsPath;
|
|
61
|
-
singleton = undefined;
|
|
62
|
-
}
|
|
63
|
-
function resolveJobsPath() {
|
|
64
|
-
return jobsPathOverride ?? DEFAULT_JOBS_PATH;
|
|
65
|
-
}
|
|
66
|
-
class FileJobRegistry {
|
|
67
|
-
path;
|
|
68
|
-
constructor(path) {
|
|
69
|
-
this.path = path;
|
|
70
|
-
}
|
|
71
|
-
async list() {
|
|
72
|
-
return this.listSync();
|
|
73
|
-
}
|
|
74
|
-
listSync() {
|
|
75
|
-
const entries = this.readAll();
|
|
76
|
-
const reaped = this.reapInMemory(entries);
|
|
77
|
-
const pruned = this.pruneOld(reaped);
|
|
78
|
-
if (mutated(entries, pruned)) {
|
|
79
|
-
this.writeAll(pruned);
|
|
80
|
-
}
|
|
81
|
-
return pruned;
|
|
82
|
-
}
|
|
83
|
-
async get(id) {
|
|
84
|
-
const entries = await this.list();
|
|
85
|
-
return entries.find((entry) => entry.id === id);
|
|
86
|
-
}
|
|
87
|
-
async add(input) {
|
|
88
|
-
const entry = {
|
|
89
|
-
...input,
|
|
90
|
-
startedAt: new Date().toISOString(),
|
|
91
|
-
status: 'running',
|
|
92
|
-
};
|
|
93
|
-
const entries = this.readAll();
|
|
94
|
-
entries.push(entry);
|
|
95
|
-
this.writeAll(entries);
|
|
96
|
-
return entry;
|
|
97
|
-
}
|
|
98
|
-
async kill(id, opts = {}) {
|
|
99
|
-
const entries = this.readAll();
|
|
100
|
-
const target = entries.find((entry) => entry.id === id);
|
|
101
|
-
if (!target) {
|
|
102
|
-
return { killed: false, method: 'noop' };
|
|
103
|
-
}
|
|
104
|
-
if (target.status !== 'running') {
|
|
105
|
-
return { killed: false, method: 'noop' };
|
|
106
|
-
}
|
|
107
|
-
const grace = opts.gracefulMs ?? JOB_DEFAULT_GRACEFUL_MS;
|
|
108
|
-
if (!processAlive(target.pid)) {
|
|
109
|
-
this.markStatus(id, 'abandoned');
|
|
110
|
-
return { killed: false, method: 'noop' };
|
|
111
|
-
}
|
|
112
|
-
try {
|
|
113
|
-
process.kill(target.pid, 'SIGTERM');
|
|
114
|
-
}
|
|
115
|
-
catch (error) {
|
|
116
|
-
const code = error.code;
|
|
117
|
-
if (code === 'ESRCH') {
|
|
118
|
-
this.markStatus(id, 'abandoned');
|
|
119
|
-
return { killed: false, method: 'noop' };
|
|
120
|
-
}
|
|
121
|
-
return { killed: false, method: 'noop' };
|
|
122
|
-
}
|
|
123
|
-
const sigtermSettled = await waitForExit(target.pid, Math.min(grace, 1_500));
|
|
124
|
-
if (sigtermSettled) {
|
|
125
|
-
this.markStatus(id, 'killed');
|
|
126
|
-
return { killed: true, method: 'SIGTERM' };
|
|
127
|
-
}
|
|
128
|
-
try {
|
|
129
|
-
process.kill(target.pid, 'SIGKILL');
|
|
130
|
-
}
|
|
131
|
-
catch {
|
|
132
|
-
// already gone between the check and the signal
|
|
133
|
-
}
|
|
134
|
-
await waitForExit(target.pid, 500);
|
|
135
|
-
this.markStatus(id, 'killed');
|
|
136
|
-
return { killed: true, method: 'SIGKILL' };
|
|
137
|
-
}
|
|
138
|
-
async markFinished(id, exitCode) {
|
|
139
|
-
const entries = this.readAll();
|
|
140
|
-
const next = entries.map((entry) => entry.id === id
|
|
141
|
-
? {
|
|
142
|
-
...entry,
|
|
143
|
-
status: (exitCode === 0 ? 'finished' : 'failed'),
|
|
144
|
-
exitCode,
|
|
145
|
-
finishedAt: new Date().toISOString(),
|
|
146
|
-
}
|
|
147
|
-
: entry);
|
|
148
|
-
this.writeAll(next);
|
|
149
|
-
}
|
|
150
|
-
async reapStale() {
|
|
151
|
-
const entries = this.readAll();
|
|
152
|
-
const abandoned = [];
|
|
153
|
-
const next = entries.map((entry) => {
|
|
154
|
-
if (entry.status !== 'running')
|
|
155
|
-
return entry;
|
|
156
|
-
if (processAlive(entry.pid))
|
|
157
|
-
return entry;
|
|
158
|
-
abandoned.push(entry.id);
|
|
159
|
-
return {
|
|
160
|
-
...entry,
|
|
161
|
-
status: 'abandoned',
|
|
162
|
-
finishedAt: new Date().toISOString(),
|
|
163
|
-
};
|
|
164
|
-
});
|
|
165
|
-
if (abandoned.length > 0) {
|
|
166
|
-
this.writeAll(next);
|
|
167
|
-
}
|
|
168
|
-
return { abandoned };
|
|
169
|
-
}
|
|
170
|
-
markStatus(id, status) {
|
|
171
|
-
const entries = this.readAll();
|
|
172
|
-
const next = entries.map((entry) => entry.id === id
|
|
173
|
-
? {
|
|
174
|
-
...entry,
|
|
175
|
-
status,
|
|
176
|
-
finishedAt: entry.finishedAt ?? new Date().toISOString(),
|
|
177
|
-
}
|
|
178
|
-
: entry);
|
|
179
|
-
this.writeAll(next);
|
|
180
|
-
}
|
|
181
|
-
readAll() {
|
|
182
|
-
if (!existsSync(this.path))
|
|
183
|
-
return [];
|
|
184
|
-
try {
|
|
185
|
-
const raw = readFileSync(this.path, 'utf8');
|
|
186
|
-
if (raw.trim() === '')
|
|
187
|
-
return [];
|
|
188
|
-
const parsed = JSON.parse(raw);
|
|
189
|
-
if (!Array.isArray(parsed))
|
|
190
|
-
return [];
|
|
191
|
-
return parsed
|
|
192
|
-
.map(normalizeEntry)
|
|
193
|
-
.filter((entry) => entry !== undefined);
|
|
194
|
-
}
|
|
195
|
-
catch {
|
|
196
|
-
return [];
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
writeAll(entries) {
|
|
200
|
-
const dir = dirname(this.path);
|
|
201
|
-
try {
|
|
202
|
-
mkdirSync(dir, { recursive: true });
|
|
203
|
-
}
|
|
204
|
-
catch {
|
|
205
|
-
// ignore — the open() below surfaces the failure
|
|
206
|
-
}
|
|
207
|
-
// Atomic write: write to a sibling temp file, fsync, then rename
|
|
208
|
-
// onto the target. Rename is atomic on POSIX so concurrent
|
|
209
|
-
// readers either see the previous file or the new file, never a
|
|
210
|
-
// half-written buffer.
|
|
211
|
-
const tempPath = `${this.path}.${process.pid}.${Date.now()}.${randomUUID()}.tmp`;
|
|
212
|
-
const body = `${JSON.stringify(entries, null, 2)}\n`;
|
|
213
|
-
let fd;
|
|
214
|
-
try {
|
|
215
|
-
fd = openSync(tempPath, 'w', 0o600);
|
|
216
|
-
writeSync(fd, body);
|
|
217
|
-
fsyncSync(fd);
|
|
218
|
-
}
|
|
219
|
-
finally {
|
|
220
|
-
if (fd !== undefined) {
|
|
221
|
-
try {
|
|
222
|
-
closeSync(fd);
|
|
223
|
-
}
|
|
224
|
-
catch {
|
|
225
|
-
// best-effort
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
try {
|
|
230
|
-
renameSync(tempPath, this.path);
|
|
231
|
-
}
|
|
232
|
-
catch (error) {
|
|
233
|
-
// Cleanup the temp file if rename failed for any reason.
|
|
234
|
-
try {
|
|
235
|
-
unlinkSync(tempPath);
|
|
236
|
-
}
|
|
237
|
-
catch {
|
|
238
|
-
// best-effort
|
|
239
|
-
}
|
|
240
|
-
throw error;
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
reapInMemory(entries) {
|
|
244
|
-
return entries.map((entry) => {
|
|
245
|
-
if (entry.status !== 'running')
|
|
246
|
-
return entry;
|
|
247
|
-
if (processAlive(entry.pid))
|
|
248
|
-
return entry;
|
|
249
|
-
return {
|
|
250
|
-
...entry,
|
|
251
|
-
status: 'abandoned',
|
|
252
|
-
finishedAt: entry.finishedAt ?? new Date().toISOString(),
|
|
253
|
-
};
|
|
254
|
-
});
|
|
255
|
-
}
|
|
256
|
-
pruneOld(entries) {
|
|
257
|
-
const cutoff = Date.now() - JOB_RETENTION_MS;
|
|
258
|
-
return entries.filter((entry) => {
|
|
259
|
-
if (entry.status === 'running')
|
|
260
|
-
return true;
|
|
261
|
-
const stamp = entry.finishedAt ?? entry.startedAt;
|
|
262
|
-
const parsed = Date.parse(stamp);
|
|
263
|
-
if (Number.isNaN(parsed))
|
|
264
|
-
return true;
|
|
265
|
-
return parsed >= cutoff;
|
|
266
|
-
});
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
/**
|
|
270
|
-
* Normalize legacy `~/.pugi/jobs.json` entries (Sprint shape with
|
|
271
|
-
* `jobId` / `class` / no status) into the JobEntry shape used here.
|
|
272
|
-
* Returns undefined for entries that cannot be migrated so the loop
|
|
273
|
-
* silently skips garbage.
|
|
274
|
-
*/
|
|
275
|
-
function normalizeEntry(candidate) {
|
|
276
|
-
if (typeof candidate !== 'object' || candidate === null)
|
|
277
|
-
return undefined;
|
|
278
|
-
const c = candidate;
|
|
279
|
-
const id = typeof c['id'] === 'string'
|
|
280
|
-
? c['id']
|
|
281
|
-
: typeof c['jobId'] === 'string'
|
|
282
|
-
? c['jobId']
|
|
283
|
-
: undefined;
|
|
284
|
-
if (!id)
|
|
285
|
-
return undefined;
|
|
286
|
-
const pid = typeof c['pid'] === 'number' ? c['pid'] : undefined;
|
|
287
|
-
if (pid === undefined)
|
|
288
|
-
return undefined;
|
|
289
|
-
const command = typeof c['command'] === 'string'
|
|
290
|
-
? c['command']
|
|
291
|
-
: typeof c['cmd'] === 'string'
|
|
292
|
-
? c['cmd']
|
|
293
|
-
: undefined;
|
|
294
|
-
if (command === undefined)
|
|
295
|
-
return undefined;
|
|
296
|
-
const bashClassRaw = typeof c['bashClass'] === 'string'
|
|
297
|
-
? c['bashClass']
|
|
298
|
-
: typeof c['class'] === 'string'
|
|
299
|
-
? c['class']
|
|
300
|
-
: 'unknown';
|
|
301
|
-
const bashClass = bashClassRaw;
|
|
302
|
-
const cwd = typeof c['cwd'] === 'string' ? c['cwd'] : '';
|
|
303
|
-
const startedAt = typeof c['startedAt'] === 'string'
|
|
304
|
-
? c['startedAt']
|
|
305
|
-
: new Date().toISOString();
|
|
306
|
-
const sessionId = typeof c['sessionId'] === 'string' ? c['sessionId'] : 'unknown';
|
|
307
|
-
const status = c['status'] === 'finished' ||
|
|
308
|
-
c['status'] === 'killed' ||
|
|
309
|
-
c['status'] === 'failed' ||
|
|
310
|
-
c['status'] === 'abandoned'
|
|
311
|
-
? c['status']
|
|
312
|
-
: 'running';
|
|
313
|
-
const exitCode = typeof c['exitCode'] === 'number' ? c['exitCode'] : undefined;
|
|
314
|
-
const finishedAt = typeof c['finishedAt'] === 'string' ? c['finishedAt'] : undefined;
|
|
315
|
-
const outputArtifactRef = typeof c['outputArtifactRef'] === 'string'
|
|
316
|
-
? c['outputArtifactRef']
|
|
317
|
-
: undefined;
|
|
318
|
-
return {
|
|
319
|
-
id,
|
|
320
|
-
pid,
|
|
321
|
-
command,
|
|
322
|
-
bashClass,
|
|
323
|
-
cwd,
|
|
324
|
-
startedAt,
|
|
325
|
-
finishedAt,
|
|
326
|
-
status,
|
|
327
|
-
exitCode,
|
|
328
|
-
sessionId,
|
|
329
|
-
outputArtifactRef,
|
|
330
|
-
};
|
|
331
|
-
}
|
|
332
|
-
function processAlive(pid) {
|
|
333
|
-
if (!Number.isInteger(pid) || pid <= 0)
|
|
334
|
-
return false;
|
|
335
|
-
try {
|
|
336
|
-
process.kill(pid, 0);
|
|
337
|
-
return true;
|
|
338
|
-
}
|
|
339
|
-
catch (error) {
|
|
340
|
-
const code = error.code;
|
|
341
|
-
// EPERM means the process exists but we cannot signal it — still alive.
|
|
342
|
-
if (code === 'EPERM')
|
|
343
|
-
return true;
|
|
344
|
-
return false;
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
async function waitForExit(pid, timeoutMs) {
|
|
348
|
-
const deadline = Date.now() + timeoutMs;
|
|
349
|
-
while (Date.now() < deadline) {
|
|
350
|
-
if (!processAlive(pid))
|
|
351
|
-
return true;
|
|
352
|
-
await new Promise((resolve) => setTimeout(resolve, 25));
|
|
353
|
-
}
|
|
354
|
-
return !processAlive(pid);
|
|
355
|
-
}
|
|
356
|
-
function mutated(before, after) {
|
|
357
|
-
if (before.length !== after.length)
|
|
358
|
-
return true;
|
|
359
|
-
for (let i = 0; i < before.length; i++) {
|
|
360
|
-
const a = before[i];
|
|
361
|
-
const b = after[i];
|
|
362
|
-
if (!a || !b)
|
|
363
|
-
return true;
|
|
364
|
-
if (a.id !== b.id)
|
|
365
|
-
return true;
|
|
366
|
-
if (a.status !== b.status)
|
|
367
|
-
return true;
|
|
368
|
-
if (a.exitCode !== b.exitCode)
|
|
369
|
-
return true;
|
|
370
|
-
if (a.finishedAt !== b.finishedAt)
|
|
371
|
-
return true;
|
|
372
|
-
}
|
|
373
|
-
return false;
|
|
374
|
-
}
|
|
375
|
-
/**
|
|
376
|
-
* Format a compact one-line summary for the engine prompt snapshot.
|
|
377
|
-
* Public so the prompts module can pre-format without re-implementing
|
|
378
|
-
* the rendering rules.
|
|
379
|
-
*/
|
|
380
|
-
export function summarizeJobsForPrompt(entries) {
|
|
381
|
-
if (entries.length === 0) {
|
|
382
|
-
return 'BACKGROUND JOBS: none on watch.';
|
|
383
|
-
}
|
|
384
|
-
const running = entries.filter((entry) => entry.status === 'running');
|
|
385
|
-
const finished = entries
|
|
386
|
-
.filter((entry) => entry.status !== 'running')
|
|
387
|
-
.slice(-5);
|
|
388
|
-
const lines = ['BACKGROUND JOBS:'];
|
|
389
|
-
if (running.length === 0) {
|
|
390
|
-
lines.push(' - Running: 0');
|
|
391
|
-
}
|
|
392
|
-
else {
|
|
393
|
-
const oldest = running.reduce((min, entry) => {
|
|
394
|
-
return Date.parse(entry.startedAt) < Date.parse(min.startedAt)
|
|
395
|
-
? entry
|
|
396
|
-
: min;
|
|
397
|
-
}, running[0]);
|
|
398
|
-
lines.push(` - Running: ${running.length} (oldest started ${relativeAge(oldest.startedAt)} ago)`);
|
|
399
|
-
const counts = new Map();
|
|
400
|
-
for (const entry of running) {
|
|
401
|
-
counts.set(entry.bashClass, (counts.get(entry.bashClass) ?? 0) + 1);
|
|
402
|
-
}
|
|
403
|
-
const classSummary = [...counts.entries()]
|
|
404
|
-
.map(([cls, count]) => `${cls} (${count})`)
|
|
405
|
-
.join(', ');
|
|
406
|
-
lines.push(` - Classes in flight: ${classSummary}`);
|
|
407
|
-
}
|
|
408
|
-
if (finished.length > 0) {
|
|
409
|
-
const compact = finished
|
|
410
|
-
.map((entry) => `${entry.id.slice(0, 8)} ${entry.status}${entry.exitCode !== undefined ? ` exit=${entry.exitCode}` : ''}`)
|
|
411
|
-
.join('; ');
|
|
412
|
-
lines.push(` - Recent finished (last ${finished.length}): ${compact}`);
|
|
413
|
-
}
|
|
414
|
-
lines.push(' Use `pugi jobs tail <id>` to inspect output. Do not spawn another long-running background job while 3+ are already on watch.');
|
|
415
|
-
return lines.join('\n');
|
|
416
|
-
}
|
|
417
|
-
/**
|
|
418
|
-
* Renders an ISO timestamp as a compact relative age (`2m`, `1h 14m`,
|
|
419
|
-
* `3d`). Used by the engine prompt snapshot and the `pugi jobs list`
|
|
420
|
-
* table rendering.
|
|
421
|
-
*/
|
|
422
|
-
export function relativeAge(iso) {
|
|
423
|
-
const ms = Math.max(0, Date.now() - Date.parse(iso));
|
|
424
|
-
const seconds = Math.floor(ms / 1000);
|
|
425
|
-
if (seconds < 60)
|
|
426
|
-
return `${seconds}s`;
|
|
427
|
-
const minutes = Math.floor(seconds / 60);
|
|
428
|
-
if (minutes < 60)
|
|
429
|
-
return `${minutes}m`;
|
|
430
|
-
const hours = Math.floor(minutes / 60);
|
|
431
|
-
const remMinutes = minutes - hours * 60;
|
|
432
|
-
if (hours < 24) {
|
|
433
|
-
return remMinutes === 0 ? `${hours}h` : `${hours}h ${remMinutes}m`;
|
|
434
|
-
}
|
|
435
|
-
const days = Math.floor(hours / 24);
|
|
436
|
-
const remHours = hours - days * 24;
|
|
437
|
-
return remHours === 0 ? `${days}d` : `${days}d ${remHours}h`;
|
|
438
|
-
}
|
|
439
|
-
/**
|
|
440
|
-
* Renders a finished-duration in the same units as `relativeAge` but
|
|
441
|
-
* computed from a startedAt + finishedAt pair. Falls back to `?` when
|
|
442
|
-
* the timestamps cannot be parsed.
|
|
443
|
-
*/
|
|
444
|
-
export function formatDuration(startedAt, finishedAt) {
|
|
445
|
-
const start = Date.parse(startedAt);
|
|
446
|
-
const end = finishedAt ? Date.parse(finishedAt) : Date.now();
|
|
447
|
-
if (Number.isNaN(start) || Number.isNaN(end) || end < start)
|
|
448
|
-
return '?';
|
|
449
|
-
const ms = end - start;
|
|
450
|
-
const seconds = Math.floor(ms / 1000);
|
|
451
|
-
if (seconds < 60)
|
|
452
|
-
return `${seconds}s`;
|
|
453
|
-
const minutes = Math.floor(seconds / 60);
|
|
454
|
-
const remSeconds = seconds - minutes * 60;
|
|
455
|
-
if (minutes < 60) {
|
|
456
|
-
return remSeconds === 0 ? `${minutes}m` : `${minutes}m ${remSeconds}s`;
|
|
457
|
-
}
|
|
458
|
-
const hours = Math.floor(minutes / 60);
|
|
459
|
-
const remMinutes = minutes - hours * 60;
|
|
460
|
-
return remMinutes === 0 ? `${hours}h` : `${hours}h ${remMinutes}m`;
|
|
461
|
-
}
|
|
462
|
-
//# sourceMappingURL=registry.js.map
|