@pugi/cli 0.1.0-beta.9 → 0.1.0-beta.91
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/CHANGELOG.md +132 -0
- package/LICENSE +1 -1
- package/assets/pugi-prozr2-mascot.ansi +9 -0
- package/bin/run.js +33 -1
- package/dist/commands/deploy.js +40 -40
- package/dist/commands/flatten.js +191 -0
- package/dist/commands/jobs-watch.js +201 -0
- package/dist/commands/jobs.js +42 -27
- package/dist/commands/smoke.js +133 -0
- package/dist/core/agent-progress/cleanup.js +134 -0
- package/dist/core/agent-progress/schema.js +144 -0
- package/dist/core/agent-progress/writer.js +101 -0
- package/dist/core/agents/adaptive-router.js +330 -0
- package/dist/core/agents/query-decomposer.js +297 -0
- package/dist/core/agents/registry.js +3 -3
- package/dist/core/approvals/shortcut-resolver.js +98 -0
- package/dist/core/artifact-chain/dispatcher.js +148 -0
- package/dist/core/artifact-chain/exporter.js +164 -0
- package/dist/core/artifact-chain/state.js +243 -0
- package/dist/core/artifact-chain/steps.js +169 -0
- package/dist/core/ask-user/question.js +92 -0
- package/dist/core/audit/audit-trail.js +275 -0
- package/dist/core/auth/ensure-authenticated.js +129 -0
- package/dist/core/auth/env-provider.js +238 -0
- package/dist/core/auto-open-browser.js +4 -4
- package/dist/core/auto-update/channels.js +122 -0
- package/dist/core/auto-update/checker.js +241 -0
- package/dist/core/auto-update/state.js +235 -0
- package/dist/core/bare-mode/index.js +107 -0
- package/dist/core/bash/redirect.js +281 -0
- package/dist/core/bash-classifier.js +436 -40
- package/dist/core/checkpoint/resumer.js +149 -0
- package/dist/core/checkpoint/rewinder.js +291 -0
- package/dist/core/checkpoints/shadow-git.js +670 -0
- package/dist/core/citations/parser.js +109 -0
- package/dist/core/classifier/yolo-classifier.js +88 -0
- package/dist/core/codegraph/decision-store.js +248 -0
- package/dist/core/codegraph/detect-repo.js +459 -0
- package/dist/core/codegraph/install.js +134 -0
- package/dist/core/codegraph/offer-hook.js +220 -0
- package/dist/core/compact/auto-trigger.js +96 -0
- package/dist/core/compact/buffer-rewriter.js +115 -0
- package/dist/core/compact/summarizer.js +208 -0
- package/dist/core/compact/token-counter.js +108 -0
- package/dist/core/consensus/anvil-fanout.js +25 -25
- package/dist/core/consensus/diff-capture.js +121 -12
- package/dist/core/consensus/rubric.js +21 -21
- package/dist/core/context/builder.js +6 -6
- package/dist/core/context/compaction-events.js +8 -8
- package/dist/core/context/compaction.js +31 -31
- package/dist/core/context/index.js +15 -8
- package/dist/core/context/invariants.js +51 -51
- package/dist/core/context/markdown-loader.js +28 -10
- package/dist/core/context/markdown-traverse.js +255 -0
- package/dist/core/context/pugiignore.js +41 -41
- package/dist/core/context/repo-skeleton.js +37 -37
- package/dist/core/context/tool-eviction.js +55 -0
- package/dist/core/context/watcher.js +32 -32
- package/dist/core/context/working-set.js +23 -23
- package/dist/core/coordinator/agent-tools.js +77 -0
- package/dist/core/coordinator/agent-toolset.js +65 -0
- package/dist/core/coordinator/fsm.js +73 -0
- package/dist/core/coordinator/mode-fsm.js +70 -0
- package/dist/core/cost/rate-card.js +129 -0
- package/dist/core/cost/tracker.js +221 -0
- package/dist/core/credentials.js +13 -13
- package/dist/core/cron/scheduler.js +138 -0
- package/dist/core/denial-tracking/index.js +8 -0
- package/dist/core/denial-tracking/state.js +264 -0
- package/dist/core/diagnostics/probe-runner.js +93 -0
- package/dist/core/diagnostics/probes/api.js +46 -0
- package/dist/core/diagnostics/probes/auth.js +93 -0
- package/dist/core/diagnostics/probes/bare-mode.js +42 -0
- package/dist/core/diagnostics/probes/cli-version.js +127 -0
- package/dist/core/diagnostics/probes/config.js +72 -0
- package/dist/core/diagnostics/probes/denial-tracking.js +57 -0
- package/dist/core/diagnostics/probes/disk.js +81 -0
- package/dist/core/diagnostics/probes/engine-live.js +46 -0
- package/dist/core/diagnostics/probes/git.js +65 -0
- package/dist/core/diagnostics/probes/hooks.js +118 -0
- package/dist/core/diagnostics/probes/mcp.js +75 -0
- package/dist/core/diagnostics/probes/node.js +59 -0
- package/dist/core/diagnostics/probes/pnpm.js +36 -0
- package/dist/core/diagnostics/probes/pugi-md.js +89 -0
- package/dist/core/diagnostics/probes/sandbox.js +40 -0
- package/dist/core/diagnostics/probes/session.js +74 -0
- package/dist/core/diagnostics/probes/status-snapshot.js +488 -0
- package/dist/core/diagnostics/probes/workspace.js +63 -0
- package/dist/core/diagnostics/types.js +70 -0
- package/dist/core/dispatch/cache-cleanup.js +197 -0
- package/dist/core/dispatch/cache-handoff.js +295 -0
- package/dist/core/edits/apply-patch-layer-e.js +189 -0
- package/dist/core/edits/dispatch.js +333 -7
- package/dist/core/edits/format-detector.js +260 -0
- package/dist/core/edits/format-matrix.js +26 -0
- package/dist/core/edits/fuzzy-ladder.js +650 -0
- package/dist/core/edits/index.js +5 -1
- package/dist/core/edits/journal.js +199 -0
- package/dist/core/edits/layer-a-apply.js +15 -15
- package/dist/core/edits/layer-a-fuzzy-apply.js +198 -0
- package/dist/core/edits/layer-b-apply.js +9 -9
- package/dist/core/edits/layer-c-apply.js +6 -6
- package/dist/core/edits/layer-d-ast.js +557 -14
- package/dist/core/edits/marker-parser.js +12 -12
- package/dist/core/edits/security-gate.js +27 -27
- package/dist/core/edits/verify-hook.js +273 -0
- package/dist/core/edits/worktree.js +29 -29
- package/dist/core/engine/anvil-client.js +214 -26
- package/dist/core/engine/auto-compact.js +179 -0
- package/dist/core/engine/budgets.js +186 -0
- package/dist/core/engine/context-prefix.js +155 -0
- package/dist/core/engine/index.js +1 -1
- package/dist/core/engine/intensity.js +158 -0
- package/dist/core/engine/intent.js +260 -0
- package/dist/core/engine/native-pugi.js +1295 -227
- package/dist/core/engine/prompts.js +129 -19
- package/dist/core/engine/strip-internal-fields.js +124 -0
- package/dist/core/engine/tool-bridge.js +1792 -59
- package/dist/core/evaluation/golden-dataset.js +293 -0
- package/dist/core/feedback/queue.js +177 -0
- package/dist/core/feedback/submitter.js +145 -0
- package/dist/core/file-cache.js +113 -1
- package/dist/core/flatten/flatten-repo.js +439 -0
- package/dist/core/format/osc8-link.js +28 -0
- package/dist/core/hook-chains.js +392 -0
- package/dist/core/hooks/citation-verify-hook.js +138 -0
- package/dist/core/hooks/citation-verify.js +112 -0
- package/dist/core/hooks/events.js +46 -0
- package/dist/core/hooks/index.js +15 -0
- package/dist/core/hooks/registry.js +216 -0
- package/dist/core/hooks/runner.js +236 -0
- package/dist/core/hooks/v2/event-emitter.js +115 -0
- package/dist/core/hooks/v2/executor.js +282 -0
- package/dist/core/hooks/v2/index.js +25 -0
- package/dist/core/hooks/v2/lifecycle.js +104 -0
- package/dist/core/hooks/v2/loader.js +216 -0
- package/dist/core/hooks/v2/matcher.js +125 -0
- package/dist/core/hooks/v2/trust.js +143 -0
- package/dist/core/hooks/v2/types.js +86 -0
- package/dist/core/hooks/worktree-events.js +158 -0
- package/dist/core/image/renderer.js +71 -0
- package/dist/core/init/detector.js +582 -0
- package/dist/core/init/template-renderer.js +242 -0
- package/dist/core/jobs/registry.js +18 -18
- package/dist/core/ledger/results-tsv.js +142 -0
- package/dist/core/log-discipline/stdout-redirect.js +51 -0
- package/dist/core/lsp/cache.js +105 -0
- package/dist/core/lsp/client.js +551 -41
- package/dist/core/lsp/language-detect.js +66 -0
- package/dist/core/lsp/post-edit-diagnostics.js +171 -0
- package/dist/core/lsp/server-detect.js +173 -0
- package/dist/core/lsp/symbol-cache.js +162 -0
- package/dist/core/lsp/symbol-tools.js +664 -0
- package/dist/core/mcp/client.js +97 -28
- package/dist/core/mcp/http-server.js +553 -0
- package/dist/core/mcp/orchestrator-tools.js +662 -0
- package/dist/core/mcp/permission.js +190 -0
- package/dist/core/mcp/registry.js +39 -17
- package/dist/core/mcp/server-tools.js +219 -0
- package/dist/core/mcp/server.js +397 -0
- package/dist/core/mcp/trust.js +10 -10
- package/dist/core/memory/dual-write.js +416 -0
- package/dist/core/memory/passive-extract.js +130 -0
- package/dist/core/memory/phase1-kinds.js +20 -0
- package/dist/core/memory/secret-scanner.js +304 -0
- package/dist/core/memory-sync/queue.js +170 -0
- package/dist/core/metrics/extract.js +113 -0
- package/dist/core/modes/roo-modes.js +68 -0
- package/dist/core/onboarding/ensure-initialized.js +133 -0
- package/dist/core/onboarding/marker.js +111 -0
- package/dist/core/onboarding/telemetry-state.js +108 -0
- package/dist/core/output-style/presets.js +176 -0
- package/dist/core/output-style/state.js +185 -0
- package/dist/core/path-security.js +287 -5
- package/dist/core/permission.js +82 -22
- package/dist/core/permissions/auto-classifier.js +124 -0
- package/dist/core/permissions/bash-parser.js +371 -0
- package/dist/core/permissions/circuit-breaker.js +83 -0
- package/dist/core/permissions/constrained-edit.js +91 -0
- package/dist/core/permissions/gate.js +278 -0
- package/dist/core/permissions/index.js +20 -0
- package/dist/core/permissions/mode.js +174 -0
- package/dist/core/permissions/network-egress.js +137 -0
- package/dist/core/permissions/state.js +241 -0
- package/dist/core/permissions/tool-class.js +93 -0
- package/dist/core/plan-mode/ui-state.js +51 -0
- package/dist/core/plans/plan-artifact.js +721 -0
- package/dist/core/policy-limits/etag-store.js +122 -0
- package/dist/core/prd-check/parser.js +215 -0
- package/dist/core/prd-check/reporter.js +127 -0
- package/dist/core/prd-check/session-review.js +557 -0
- package/dist/core/prd-check/verifiers.js +223 -0
- package/dist/core/prompt-cache/client-cache.js +99 -0
- package/dist/core/prompts/assembly.js +29 -0
- package/dist/core/prompts/registry.js +364 -0
- package/dist/core/pugi-md/cc-compat-rules.js +735 -0
- package/dist/core/pugi-md/context-injector.js +76 -0
- package/dist/core/pugi-md/walk-up.js +207 -0
- package/dist/core/python/uv-installer.js +270 -0
- package/dist/core/python/uv-resolver.js +83 -0
- package/dist/core/rate-limit/narrator.js +146 -0
- package/dist/core/recipes/cli-types.js +20 -0
- package/dist/core/recipes/loader.js +103 -0
- package/dist/core/recipes/runner.js +345 -0
- package/dist/core/recipes/schema.js +587 -0
- package/dist/core/release-notes/parser.js +241 -0
- package/dist/core/release-notes/state.js +116 -0
- package/dist/core/repl/ask.js +37 -37
- package/dist/core/repl/cancellation.js +26 -26
- package/dist/core/repl/cap-warning.js +4 -4
- package/dist/core/repl/clipboard-read.js +11 -11
- package/dist/core/repl/dispatch-fsm.js +12 -12
- package/dist/core/repl/history-search.js +15 -15
- package/dist/core/repl/history.js +28 -18
- package/dist/core/repl/kill-ring.js +5 -5
- package/dist/core/repl/model-pricing.js +135 -0
- package/dist/core/repl/privacy-banner.js +22 -22
- package/dist/core/repl/session.js +2148 -217
- package/dist/core/repl/slash-commands.js +501 -41
- package/dist/core/repl/store/index.js +1 -1
- package/dist/core/repl/store/jsonl-log.js +22 -22
- package/dist/core/repl/store/lockfile.js +10 -10
- package/dist/core/repl/store/session-store.js +136 -107
- package/dist/core/repl/store/types.js +15 -15
- package/dist/core/repl/store/uuid-v7.js +12 -12
- package/dist/core/repl/workspace-context.js +43 -21
- package/dist/core/repo-map/build.js +125 -0
- package/dist/core/repo-map/cache.js +185 -0
- package/dist/core/repo-map/extractor.js +254 -0
- package/dist/core/repo-map/formatter.js +145 -0
- package/dist/core/repo-map/page-rank.js +105 -0
- package/dist/core/repo-map/scanner.js +211 -0
- package/dist/core/retry-budget/budget.js +284 -0
- package/dist/core/retry-budget/index.js +5 -0
- package/dist/core/retry-budget/retry-cap.js +74 -0
- package/dist/core/routing/lead-worker.js +43 -0
- package/dist/core/routing/pre-flight-estimator.js +108 -0
- package/dist/core/runs/run-tree.js +103 -0
- package/dist/core/security/injection-scanner.js +367 -0
- package/dist/core/security/output-filter.js +418 -0
- package/dist/core/session/env-file.js +105 -0
- package/dist/core/session/section-budgets.js +140 -0
- package/dist/core/session.js +92 -0
- package/dist/core/settings.js +324 -5
- package/dist/core/share/formatter.js +271 -0
- package/dist/core/share/redactor.js +221 -0
- package/dist/core/share/uploader.js +267 -0
- package/dist/core/skills/defaults.js +30 -30
- package/dist/core/skills/loader.js +22 -22
- package/dist/core/skills/sources.js +27 -27
- package/dist/core/smoke/headless-driver.js +174 -0
- package/dist/core/smoke/orchestrator.js +194 -0
- package/dist/core/smoke/runner.js +238 -0
- package/dist/core/smoke/scenario-parser.js +316 -0
- package/dist/core/statusline.js +99 -0
- package/dist/core/subagents/dispatcher-real.js +600 -0
- package/dist/core/subagents/dispatcher.js +132 -43
- package/dist/core/subagents/index.js +19 -6
- package/dist/core/subagents/isolation-matrix.js +213 -0
- package/dist/core/subagents/spawn.js +19 -4
- package/dist/core/telemetry/emitter.js +229 -0
- package/dist/core/telemetry/queue.js +251 -0
- package/dist/core/theme/context.js +91 -0
- package/dist/core/theme/presets.js +228 -0
- package/dist/core/theme/state.js +181 -0
- package/dist/core/todos/invariant.js +10 -0
- package/dist/core/todos/state.js +177 -0
- package/dist/core/tool-schema/compressor.js +89 -0
- package/dist/core/transport/version-interceptor.js +166 -0
- package/dist/core/trust.js +2 -2
- package/dist/core/tui/thinking-block.js +64 -0
- package/dist/core/vim/keymap.js +288 -0
- package/dist/core/vim/state.js +92 -0
- package/dist/core/watch-markers/marker-watcher.js +133 -0
- package/dist/core/worktree/include-parser.js +249 -0
- package/dist/core/worktree-manager/cleanup.js +123 -0
- package/dist/core/worktree-manager/manager.js +303 -0
- package/dist/index.js +36 -0
- package/dist/runtime/bootstrap.js +190 -0
- package/dist/runtime/cli.js +4185 -549
- package/dist/runtime/commands/agents.js +31 -31
- package/dist/runtime/commands/budget.js +5 -5
- package/dist/runtime/commands/cancel.js +231 -0
- package/dist/runtime/commands/chain.js +489 -0
- package/dist/runtime/commands/codegraph-status.js +227 -0
- package/dist/runtime/commands/compact.js +297 -0
- package/dist/runtime/commands/config.js +73 -39
- package/dist/runtime/commands/cost.js +199 -0
- package/dist/runtime/commands/delegate.js +27 -4
- package/dist/runtime/commands/dispatch.js +126 -0
- package/dist/runtime/commands/doctor.js +579 -0
- package/dist/runtime/commands/feedback.js +184 -0
- package/dist/runtime/commands/hooks.js +187 -0
- package/dist/runtime/commands/init.js +254 -0
- package/dist/runtime/commands/lsp.js +200 -38
- package/dist/runtime/commands/mcp.js +879 -0
- package/dist/runtime/commands/memory.js +582 -0
- package/dist/runtime/commands/model.js +237 -0
- package/dist/runtime/commands/onboarding.js +275 -0
- package/dist/runtime/commands/patch.js +12 -12
- package/dist/runtime/commands/permissions.js +112 -0
- package/dist/runtime/commands/plan.js +143 -0
- package/dist/runtime/commands/prd-check.js +285 -0
- package/dist/runtime/commands/privacy.js +17 -17
- package/dist/runtime/commands/recipe.js +325 -0
- package/dist/runtime/commands/redo-blob-store.js +92 -0
- package/dist/runtime/commands/redo.js +361 -0
- package/dist/runtime/commands/release-notes.js +229 -0
- package/dist/runtime/commands/repo-map.js +95 -0
- package/dist/runtime/commands/report.js +299 -0
- package/dist/runtime/commands/resume.js +118 -0
- package/dist/runtime/commands/review-consensus.js +68 -53
- package/dist/runtime/commands/rewind.js +333 -0
- package/dist/runtime/commands/roster.js +14 -14
- package/dist/runtime/commands/sessions.js +163 -0
- package/dist/runtime/commands/share.js +316 -0
- package/dist/runtime/commands/skills.js +31 -31
- package/dist/runtime/commands/status.js +186 -0
- package/dist/runtime/commands/stickers.js +82 -0
- package/dist/runtime/commands/style.js +194 -0
- package/dist/runtime/commands/theme.js +196 -0
- package/dist/runtime/commands/undo.js +54 -22
- package/dist/runtime/commands/update.js +289 -0
- package/dist/runtime/commands/vim.js +140 -0
- package/dist/runtime/commands/worktree.js +8 -8
- package/dist/runtime/commands/worktrees.js +155 -0
- package/dist/runtime/headless-repl.js +195 -0
- package/dist/runtime/headless.js +543 -0
- package/dist/runtime/load-hooks-or-exit.js +71 -0
- package/dist/runtime/plan-decompose.js +22 -22
- package/dist/runtime/sigint-guard.js +272 -0
- package/dist/runtime/update-check.js +28 -28
- package/dist/runtime/version.js +65 -0
- package/dist/runtime/worktree-bootstrap.js +579 -0
- package/dist/skills/bundled/batch.js +617 -0
- package/dist/skills/bundled/index.js +45 -0
- package/dist/skills/bundled/loop.js +358 -0
- package/dist/skills/bundled/remember.js +383 -0
- package/dist/skills/bundled/simplify.js +289 -0
- package/dist/skills/bundled/skillify.js +373 -0
- package/dist/skills/bundled/stuck.js +558 -0
- package/dist/skills/bundled/verify.js +439 -0
- package/dist/testing/vcr.js +486 -0
- package/dist/tools/agent-tool.js +229 -0
- package/dist/tools/apply-patch.js +89 -28
- package/dist/tools/ask-user-question.js +337 -0
- package/dist/tools/ask-user.js +115 -0
- package/dist/tools/bash.js +624 -46
- package/dist/tools/brief.js +224 -0
- package/dist/tools/cron.js +433 -0
- package/dist/tools/enter-worktree.js +250 -0
- package/dist/tools/exit-worktree.js +147 -0
- package/dist/tools/file-tools.js +161 -44
- package/dist/tools/lsp-tools.js +377 -1
- package/dist/tools/mcp-tool.js +260 -0
- package/dist/tools/multi-edit.js +361 -0
- package/dist/tools/powershell.js +268 -0
- package/dist/tools/registry.js +99 -4
- package/dist/tools/skill-tool.js +96 -0
- package/dist/tools/sleep.js +99 -0
- package/dist/tools/synthetic-output.js +133 -0
- package/dist/tools/tasks.js +208 -0
- package/dist/tools/todo-write.js +184 -0
- package/dist/tools/verify-plan-execution.js +295 -0
- package/dist/tools/web-fetch-injection-scanner.js +207 -0
- package/dist/tools/web-fetch.js +195 -10
- package/dist/tools/web-search.js +458 -0
- package/dist/tui/agent-progress-card.js +111 -0
- package/dist/tui/agent-tree.js +11 -1
- package/dist/tui/ask-modal.js +14 -14
- package/dist/tui/ask-user-question-chips.js +315 -0
- package/dist/tui/ask-user-question-prompt.js +203 -0
- package/dist/tui/compact-banner.js +81 -0
- package/dist/tui/conversation-pane.js +85 -11
- package/dist/tui/cost-table.js +111 -0
- package/dist/tui/device-flow.js +2 -2
- package/dist/tui/doctor-table.js +46 -0
- package/dist/tui/feedback-prompt.js +156 -0
- package/dist/tui/input-box.js +247 -32
- package/dist/tui/login-picker.js +3 -3
- package/dist/tui/markdown-render.js +6 -6
- package/dist/tui/multi-file-diff-approval.js +375 -0
- package/dist/tui/onboarding-wizard.js +240 -0
- package/dist/tui/permissions-picker.js +86 -0
- package/dist/tui/render.js +36 -1
- package/dist/tui/repl-render.js +176 -25
- package/dist/tui/repl-splash-art.js +16 -16
- package/dist/tui/repl-splash-mascot.js +48 -24
- package/dist/tui/repl-splash.js +22 -22
- package/dist/tui/repl.js +125 -45
- package/dist/tui/slash-palette.js +6 -6
- package/dist/tui/splash.js +2 -2
- package/dist/tui/status-bar.js +109 -31
- package/dist/tui/status-table.js +7 -0
- package/dist/tui/stickers-art.js +136 -0
- package/dist/tui/style-table.js +28 -0
- package/dist/tui/theme-table.js +29 -0
- package/dist/tui/thinking-spinner.js +123 -0
- package/dist/tui/tool-stream-pane.js +53 -4
- package/dist/tui/update-banner.js +27 -2
- package/dist/tui/vim-input.js +267 -0
- package/dist/tui/welcome-banner.js +107 -0
- package/dist/tui/welcome-data.js +293 -0
- package/dist/tui/workspace-context.js +2 -2
- package/package.json +31 -16
- package/test/scenarios/codegen-create-file.scenario.txt +13 -0
- package/test/scenarios/compact-force.scenario.txt +12 -0
- package/test/scenarios/identity.scenario.txt +12 -0
- package/test/scenarios/persona-handoff.scenario.txt +12 -0
- package/test/scenarios/walkback.scenario.txt +12 -0
- package/dist/core/engine/compaction-hook.js +0 -154
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PUGI.md hierarchy probe — .
|
|
3
|
+
*
|
|
4
|
+
* Surfaces how many ambient `PUGI.md` / `CLAUDE.md` files were
|
|
5
|
+
* discovered by the cwd → homedir walk-up. Operators triaging "why
|
|
6
|
+
* is the model not following my project conventions" can run
|
|
7
|
+
* `pugi doctor` and immediately see whether the hierarchy walk
|
|
8
|
+
* loaded the file they expected.
|
|
9
|
+
*
|
|
10
|
+
* Status semantics:
|
|
11
|
+
* - `skipped` when bare mode is active (the walk is deliberately
|
|
12
|
+
* disabled). The row still renders so the JSON schema stays
|
|
13
|
+
* stable for downstream consumers.
|
|
14
|
+
* - `skipped` when zero files were found. This is the default
|
|
15
|
+
* state on a clean machine and is NOT an error — most operators
|
|
16
|
+
* do not maintain a `~/PUGI.md`.
|
|
17
|
+
* - `ok` when one or more files were discovered. The detail names
|
|
18
|
+
* the closest file and the total count.
|
|
19
|
+
*
|
|
20
|
+
* Side effects:
|
|
21
|
+
* - One filesystem walk from cwd to homedir (bounded by the walker's
|
|
22
|
+
* depth cap). Each level performs at most 2 `existsSync` calls.
|
|
23
|
+
* Cost is single-digit ms even on cold cache; well inside the
|
|
24
|
+
* doctor probe wall-clock budget.
|
|
25
|
+
*
|
|
26
|
+
* Wired into `buildDefaultProbes` in `runtime/commands/doctor.ts`.
|
|
27
|
+
*/
|
|
28
|
+
import { isBareMode } from '../../bare-mode/index.js';
|
|
29
|
+
import { walkUpPugiMd } from '../../pugi-md/walk-up.js';
|
|
30
|
+
export const PUGI_MD_DOCTOR_LABEL = 'PUGI.md HIERARCHY';
|
|
31
|
+
/** Detail string emitted when bare mode disables the walk. */
|
|
32
|
+
export const PUGI_MD_BARE_SKIP_DETAIL = 'skipped (--bare)';
|
|
33
|
+
/** Detail string emitted when the walk ran but found nothing. */
|
|
34
|
+
export const PUGI_MD_EMPTY_DETAIL = 'no PUGI.md / CLAUDE.md found in cwd → homedir';
|
|
35
|
+
export function probePugiMdHierarchy(input = {}) {
|
|
36
|
+
const env = input.env ?? process.env;
|
|
37
|
+
if (isBareMode(env)) {
|
|
38
|
+
return {
|
|
39
|
+
name: PUGI_MD_DOCTOR_LABEL,
|
|
40
|
+
status: 'skipped',
|
|
41
|
+
detail: PUGI_MD_BARE_SKIP_DETAIL,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
const cwd = input.cwd ?? process.cwd();
|
|
45
|
+
const walk = input.walkImpl ?? ((c, o) => walkUpPugiMd(c, o));
|
|
46
|
+
let files;
|
|
47
|
+
try {
|
|
48
|
+
files = walk(cwd, input.homedir !== undefined ? { homedir: input.homedir } : {});
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
// Defensive: walker is wrapped in per-file try/catch already, so
|
|
52
|
+
// a throw here means a programmer error (bad input). Degrade to
|
|
53
|
+
// a `warn` row rather than crashing the doctor sweep.
|
|
54
|
+
return {
|
|
55
|
+
name: PUGI_MD_DOCTOR_LABEL,
|
|
56
|
+
status: 'warn',
|
|
57
|
+
detail: 'walk-up failed (see logs)',
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
if (files.length === 0) {
|
|
61
|
+
return {
|
|
62
|
+
name: PUGI_MD_DOCTOR_LABEL,
|
|
63
|
+
status: 'skipped',
|
|
64
|
+
detail: PUGI_MD_EMPTY_DETAIL,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
// `files` is shallow-to-deep; the first entry is the closest to cwd.
|
|
68
|
+
// Operators reading the table care most about that one — it is the
|
|
69
|
+
// file that will most directly influence model behaviour. The
|
|
70
|
+
// additional count gives a quick "is the homedir / parent picked up
|
|
71
|
+
// too?" signal without listing every path.
|
|
72
|
+
const closest = files[0];
|
|
73
|
+
// closest is guaranteed non-undefined: files.length > 0 enforced
|
|
74
|
+
// by the early return above.
|
|
75
|
+
if (!closest) {
|
|
76
|
+
return {
|
|
77
|
+
name: PUGI_MD_DOCTOR_LABEL,
|
|
78
|
+
status: 'skipped',
|
|
79
|
+
detail: PUGI_MD_EMPTY_DETAIL,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
const suffix = files.length === 1 ? '' : ` (+${files.length - 1} more)`;
|
|
83
|
+
return {
|
|
84
|
+
name: PUGI_MD_DOCTOR_LABEL,
|
|
85
|
+
status: 'ok',
|
|
86
|
+
detail: `${files.length} file${files.length === 1 ? '' : 's'}: ${closest.path}${suffix}`,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=pugi-md.js.map
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SANDBOX probe — surfaces the current OS-level sandbox posture (
|
|
3
|
+
* spec: sandbox-adapter.ts macOS Seatbelt / Linux Landlock / WSL2 detect).
|
|
4
|
+
*
|
|
5
|
+
* Pugi sandbox enforcement is tracked under task #5 (P0/L1+L16). Until
|
|
6
|
+
* that lands, this probe reports the platform's available primitive and
|
|
7
|
+
* a clear "not yet armed" warning so the operator sees the gap in
|
|
8
|
+
* `pugi doctor` instead of assuming bash dispatches run jailed.
|
|
9
|
+
*
|
|
10
|
+
* When the sandbox does ship, the probe upgrade path:
|
|
11
|
+
* - Replace the static "not_armed" detail with a real config probe
|
|
12
|
+
* (read .pugi/settings.json::sandbox.mode, verify the OS primitive
|
|
13
|
+
* resolves, return ok when both line up).
|
|
14
|
+
* - Keep the same probe NAME so doctor output / spec assertions
|
|
15
|
+
* don't churn.
|
|
16
|
+
*/
|
|
17
|
+
export function probeSandbox(_ctx) {
|
|
18
|
+
const platform = process.platform;
|
|
19
|
+
let availablePrimitive;
|
|
20
|
+
switch (platform) {
|
|
21
|
+
case 'darwin':
|
|
22
|
+
availablePrimitive = 'macOS Seatbelt (/usr/bin/sandbox-exec)';
|
|
23
|
+
break;
|
|
24
|
+
case 'linux':
|
|
25
|
+
availablePrimitive = 'Linux Landlock / nsjail (kernel-dependent)';
|
|
26
|
+
break;
|
|
27
|
+
case 'win32':
|
|
28
|
+
availablePrimitive = 'Windows AppContainer / Job Object';
|
|
29
|
+
break;
|
|
30
|
+
default:
|
|
31
|
+
availablePrimitive = `unknown platform ${platform}`;
|
|
32
|
+
}
|
|
33
|
+
return {
|
|
34
|
+
name: 'SANDBOX',
|
|
35
|
+
status: 'warn',
|
|
36
|
+
detail: `OS primitive available: ${availablePrimitive}. Sandbox enforcement NOT yet armed (Pugi task #5 pending — bash tool currently runs с full process privileges).`,
|
|
37
|
+
remediation: 'Bash tool dispatches run unsandboxed today. Track progress on the OS-level sandbox adapter via the operator-trust roadmap. Until then, rely on the bash classifier denylist + permission FSM.',
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=sandbox.js.map
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SESSION probe — reports the active session id + age when the doctor
|
|
3
|
+
* runs from inside the REPL OR finds a recent NDJSON session log in
|
|
4
|
+
* the workspace.
|
|
5
|
+
*
|
|
6
|
+
* The CLI command path has no live session context (each `pugi <cmd>`
|
|
7
|
+
* invocation is a fresh process), so we read `.pugi/events.jsonl` if
|
|
8
|
+
* present and report the most-recent event's age + total line count.
|
|
9
|
+
* Inside the REPL we pass an explicit `sessionId` so the probe
|
|
10
|
+
* surfaces the live state without re-reading disk.
|
|
11
|
+
*
|
|
12
|
+
* Absence of `.pugi/events.jsonl` is `skipped`, not an error — the
|
|
13
|
+
* operator may simply be running `pugi doctor` in a workspace that
|
|
14
|
+
* has not yet seen a dispatch.
|
|
15
|
+
*/
|
|
16
|
+
export function probeSession(ctx, fs, deps) {
|
|
17
|
+
if (deps.liveSessionId) {
|
|
18
|
+
return {
|
|
19
|
+
name: 'SESSION',
|
|
20
|
+
status: 'ok',
|
|
21
|
+
detail: `session=${deps.liveSessionId} (live, REPL active)`,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
const eventsPath = `${ctx.cwd}/.pugi/events.jsonl`;
|
|
25
|
+
if (!fs.existsSync(eventsPath)) {
|
|
26
|
+
return {
|
|
27
|
+
name: 'SESSION',
|
|
28
|
+
status: 'skipped',
|
|
29
|
+
detail: 'No prior dispatch logged in this workspace',
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
let stats;
|
|
33
|
+
try {
|
|
34
|
+
stats = fs.statSync(eventsPath);
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
38
|
+
return {
|
|
39
|
+
name: 'SESSION',
|
|
40
|
+
status: 'warn',
|
|
41
|
+
detail: `.pugi/events.jsonl present but unreadable`,
|
|
42
|
+
remediation: `Inspect: ${message}`,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
const ageMs = Math.max(0, deps.now() - stats.mtimeMs);
|
|
46
|
+
const ageLabel = formatAge(ageMs);
|
|
47
|
+
// Counting lines is cheap on a small NDJSON file; a "huge" Pugi
|
|
48
|
+
// session is single-digit MB. We avoid loading binary blobs by
|
|
49
|
+
// simply walking the buffer count.
|
|
50
|
+
let lineCount = 0;
|
|
51
|
+
try {
|
|
52
|
+
const raw = fs.readFileSync(eventsPath, 'utf8');
|
|
53
|
+
lineCount = raw.split('\n').filter((line) => line.trim().length > 0).length;
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
lineCount = -1;
|
|
57
|
+
}
|
|
58
|
+
const linePart = lineCount >= 0 ? `, ${lineCount} event(s)` : '';
|
|
59
|
+
return {
|
|
60
|
+
name: 'SESSION',
|
|
61
|
+
status: 'ok',
|
|
62
|
+
detail: `last event ${ageLabel} ago${linePart}`,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
export function formatAge(ms) {
|
|
66
|
+
if (ms < 60_000)
|
|
67
|
+
return `${Math.round(ms / 1000)}s`;
|
|
68
|
+
if (ms < 3_600_000)
|
|
69
|
+
return `${Math.round(ms / 60_000)}m`;
|
|
70
|
+
if (ms < 86_400_000)
|
|
71
|
+
return `${Math.round(ms / 3_600_000)}h`;
|
|
72
|
+
return `${Math.round(ms / 86_400_000)}d`;
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=session.js.map
|
|
@@ -0,0 +1,488 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* STATUS snapshot — concise session-state probe for `pugi status` /
|
|
3
|
+
* in-REPL `/status` ().
|
|
4
|
+
*
|
|
5
|
+
* Different from `pugi doctor` (which probes ENVIRONMENT health —
|
|
6
|
+
* Node version, disk space, auth round-trip, MCP servers, …). The
|
|
7
|
+
* status snapshot answers the operator question "what is THIS
|
|
8
|
+
* Pugi session doing right now?" — current session id + age, cwd,
|
|
9
|
+
* permission mode, CLI version, cumulative token usage, active
|
|
10
|
+
* dispatches, last command, compact boundary count, auth identity.
|
|
11
|
+
*
|
|
12
|
+
* # Fail-soft contract
|
|
13
|
+
*
|
|
14
|
+
* Every field is gathered via try/catch. A missing dep (permission
|
|
15
|
+
* state module not yet present, no `.pugi/agent-progress/` dir,
|
|
16
|
+
* no `~/.pugi/credentials.json`) is **never** an error — it
|
|
17
|
+
* degrades to `null` for nullable fields or a sentinel label
|
|
18
|
+
* (`"unknown"`, `"n/a"`, `0`) for required ones. The snapshot
|
|
19
|
+
* never throws; the renderer can always print a complete table.
|
|
20
|
+
*
|
|
21
|
+
* The module is intentionally dep-injected so the spec can drive
|
|
22
|
+
* every fail-soft branch without standing up `fs`, the credential
|
|
23
|
+
* store, or a real process clock. The `runStatusCommand` wrapper
|
|
24
|
+
* in `runtime/commands/status.ts` plugs in production deps.
|
|
25
|
+
*
|
|
26
|
+
* # Field semantics
|
|
27
|
+
*
|
|
28
|
+
* sessionId / sessionAgeMs : taken verbatim from caller when in
|
|
29
|
+
* REPL mode (the slash dispatcher passes the live id +
|
|
30
|
+
* `briefStartedAtEpochMs`); else best-effort from the on-disk
|
|
31
|
+
* `.pugi/events.jsonl` mtime, OR null when no prior dispatch is
|
|
32
|
+
* logged in the workspace.
|
|
33
|
+
*
|
|
34
|
+
* cwd : pure function of the caller's `ctx.cwd`. Never null.
|
|
35
|
+
*
|
|
36
|
+
* permissionMode : reads the optional permissions state module
|
|
37
|
+
* when available (L6 in the leak-parity roadmap). On any
|
|
38
|
+
* import failure the field degrades to "unknown" so this
|
|
39
|
+
* command lands before L6 without a compile-time coupling.
|
|
40
|
+
*
|
|
41
|
+
* cliVersion : injected literal so the build's PUGI_CLI_VERSION
|
|
42
|
+
* constant is the single source of truth.
|
|
43
|
+
*
|
|
44
|
+
* tokensUsed : injected from the caller — REPL mode passes the
|
|
45
|
+
* live `sessionTokensIn + sessionTokensOut` total; the
|
|
46
|
+
* top-level `pugi status` invocation has no REPL state so
|
|
47
|
+
* this field stays null and the renderer prints "n/a".
|
|
48
|
+
*
|
|
49
|
+
* activeDispatches / completedDispatches : counted by scanning
|
|
50
|
+
* `.pugi/agent-progress/*.json` and classifying by the
|
|
51
|
+
* `status` field. Missing dir → 0/0 (no crash).
|
|
52
|
+
*
|
|
53
|
+
* lastCommand : injected from the REPL when available; null on
|
|
54
|
+
* the top-level shell path so the renderer prints "n/a".
|
|
55
|
+
*
|
|
56
|
+
* compactBoundaries : counts lines in `.pugi/events.jsonl`
|
|
57
|
+
* where the event `kind === 'compact.boundary'`. Missing file
|
|
58
|
+
* → 0. Wired against Memory Phase 0 NDJSON; downgrade to 0 if
|
|
59
|
+
* the file is malformed.
|
|
60
|
+
*
|
|
61
|
+
* auth : resolved via the same credential helper `pugi whoami`
|
|
62
|
+
* uses. Returns either an `{ apiUrl, apiKey, label?, tier? }`
|
|
63
|
+
* summary or null when the operator is not authenticated. We
|
|
64
|
+
* do NOT make a network call here — the tier is read from
|
|
65
|
+
* local creds metadata; live tier lookup belongs to
|
|
66
|
+
* `pugi whoami --remote` (separate command).
|
|
67
|
+
*/
|
|
68
|
+
/**
|
|
69
|
+
* Collect the full snapshot. The function is synchronous because
|
|
70
|
+
* every field source is local (process state, fs, injected
|
|
71
|
+
* resolvers) — no network calls.
|
|
72
|
+
*/
|
|
73
|
+
export function collectStatusSnapshot(deps) {
|
|
74
|
+
const fields = [];
|
|
75
|
+
// Session id + age. REPL-mode caller passes both; top-level
|
|
76
|
+
// shell path passes neither и we fall back to the on-disk NDJSON
|
|
77
|
+
// tail mtime so the operator still sees a useful age figure for
|
|
78
|
+
// the most-recent workspace activity.
|
|
79
|
+
fields.push(buildSessionField(deps));
|
|
80
|
+
// Working directory. Pure function of caller's ctx — always
|
|
81
|
+
// available (sentinel only if caller passed an empty string,
|
|
82
|
+
// which the renderer treats as "unknown").
|
|
83
|
+
fields.push({
|
|
84
|
+
key: 'workdir',
|
|
85
|
+
label: 'Workdir',
|
|
86
|
+
value: deps.cwd.length > 0 ? deps.cwd : 'unknown',
|
|
87
|
+
available: deps.cwd.length > 0,
|
|
88
|
+
});
|
|
89
|
+
// Operator-facing workspace label. Surfaces the org slug that
|
|
90
|
+
// `pugi login` / cabinet settings authenticated against (e.g.
|
|
91
|
+
// `yurii`, `acme-corp`). Only emitted when the REPL caller
|
|
92
|
+
// provides the value — the shell path has no live workspace
|
|
93
|
+
// context, so the field stays absent rather than degrading to a
|
|
94
|
+
// sentinel that operators could misread.
|
|
95
|
+
if (typeof deps.workspaceLabel === 'string' && deps.workspaceLabel.length > 0) {
|
|
96
|
+
fields.push({
|
|
97
|
+
key: 'workspace',
|
|
98
|
+
label: 'Workspace',
|
|
99
|
+
value: deps.workspaceLabel,
|
|
100
|
+
available: true,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
// Backend URL. Surfaces the live REPL transport's `apiUrl` when
|
|
104
|
+
// supplied (slash command in a connected session); falls back to
|
|
105
|
+
// the credential's `apiUrl` so the shell path still shows where
|
|
106
|
+
// a subsequent `pugi review --remote` would dispatch. Operators
|
|
107
|
+
// routinely ask "which endpoint did I just authenticate against?"
|
|
108
|
+
// — making this a first-class row removes that round-trip.
|
|
109
|
+
const backendUrl = (() => {
|
|
110
|
+
if (typeof deps.liveApiUrl === 'string' && deps.liveApiUrl.length > 0) {
|
|
111
|
+
return deps.liveApiUrl;
|
|
112
|
+
}
|
|
113
|
+
try {
|
|
114
|
+
const cred = deps.resolveCredential();
|
|
115
|
+
return cred?.apiUrl ?? null;
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
})();
|
|
121
|
+
fields.push({
|
|
122
|
+
key: 'backend',
|
|
123
|
+
label: 'Backend',
|
|
124
|
+
value: backendUrl ?? 'offline',
|
|
125
|
+
available: Boolean(backendUrl),
|
|
126
|
+
});
|
|
127
|
+
// Permission mode. Fail-soft — degrades к "unknown" until L6
|
|
128
|
+
// lands the permissions/state module.
|
|
129
|
+
fields.push(buildPermissionModeField(deps));
|
|
130
|
+
// Pugi CLI version. The build-time constant is the single
|
|
131
|
+
// source of truth; sanitised upstream (sanitizeSemver in
|
|
132
|
+
// runtime/version.ts) so we never see `workspace:*` here.
|
|
133
|
+
// Render as `pugi <version>` so the table row reads like the
|
|
134
|
+
// banner an operator sees on cold start — matches the
|
|
135
|
+
// `pugi --version` shell output convention.
|
|
136
|
+
fields.push({
|
|
137
|
+
key: 'cli',
|
|
138
|
+
label: 'CLI',
|
|
139
|
+
value: `pugi ${deps.cliVersion}`,
|
|
140
|
+
available: deps.cliVersion !== '0.0.0-unknown',
|
|
141
|
+
});
|
|
142
|
+
// Token usage. REPL caller passes the live total; shell path
|
|
143
|
+
// degrades к "n/a" (no REPL state in a fresh process).
|
|
144
|
+
fields.push(buildTokenField(deps));
|
|
145
|
+
// Active + completed dispatches scanned from
|
|
146
|
+
// `.pugi/agent-progress/*.json`. Missing dir → 0 active, 0
|
|
147
|
+
// completed (no crash).
|
|
148
|
+
fields.push(buildDispatchField(deps));
|
|
149
|
+
// Last command. REPL caller passes the most-recent operator
|
|
150
|
+
// line + its timestamp; shell path degrades к "n/a".
|
|
151
|
+
fields.push(buildLastCommandField(deps));
|
|
152
|
+
// Compact boundary marker count. Read from
|
|
153
|
+
// `.pugi/events.jsonl` per Memory Phase 0; missing file → 0.
|
|
154
|
+
fields.push(buildCompactField(deps));
|
|
155
|
+
// Auth identity. Read from the credential resolver — local-only
|
|
156
|
+
// (no network call); the live tier comes from the stored
|
|
157
|
+
// credential metadata.
|
|
158
|
+
fields.push(buildAuthField(deps));
|
|
159
|
+
// Connection mode. Today simply mirrors the credential source
|
|
160
|
+
// (env vs file) so the operator can see at a glance whether
|
|
161
|
+
// they are in CI-env mode or a logged-in shell. Future wiring
|
|
162
|
+
// overlays the REPL's live connection state when invoked from
|
|
163
|
+
// the slash dispatcher.
|
|
164
|
+
fields.push(buildConnectionField(deps));
|
|
165
|
+
return {
|
|
166
|
+
command: 'status',
|
|
167
|
+
fields,
|
|
168
|
+
meta: {
|
|
169
|
+
cliVersion: deps.cliVersion,
|
|
170
|
+
nodeVersion: process.version,
|
|
171
|
+
cwd: deps.cwd,
|
|
172
|
+
capturedAt: new Date(deps.now()).toISOString(),
|
|
173
|
+
},
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
/* -------------------------- field builders -------------------------- */
|
|
177
|
+
function buildSessionField(deps) {
|
|
178
|
+
if (deps.liveSessionId && deps.liveSessionId.length > 0) {
|
|
179
|
+
const ageMs = typeof deps.sessionStartedAtEpochMs === 'number' && deps.sessionStartedAtEpochMs > 0
|
|
180
|
+
? Math.max(0, deps.now() - deps.sessionStartedAtEpochMs)
|
|
181
|
+
: null;
|
|
182
|
+
const ageLabel = ageMs === null ? '' : ` (${formatAgeShort(ageMs)})`;
|
|
183
|
+
return {
|
|
184
|
+
key: 'session',
|
|
185
|
+
label: 'Session',
|
|
186
|
+
value: `${shortId(deps.liveSessionId)}${ageLabel}`,
|
|
187
|
+
available: true,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
// Top-level shell path: best-effort tail of .pugi/events.jsonl.
|
|
191
|
+
const eventsPath = `${deps.cwd}/.pugi/events.jsonl`;
|
|
192
|
+
try {
|
|
193
|
+
if (!deps.fs.existsSync(eventsPath)) {
|
|
194
|
+
return {
|
|
195
|
+
key: 'session',
|
|
196
|
+
label: 'Session',
|
|
197
|
+
value: 'unbound (no prior dispatch in this workspace)',
|
|
198
|
+
available: false,
|
|
199
|
+
note: '.pugi/events.jsonl not found',
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
const stats = deps.fs.statSync(eventsPath);
|
|
203
|
+
const ageMs = Math.max(0, deps.now() - stats.mtimeMs);
|
|
204
|
+
return {
|
|
205
|
+
key: 'session',
|
|
206
|
+
label: 'Session',
|
|
207
|
+
value: `unbound · last activity ${formatAgeShort(ageMs)} ago`,
|
|
208
|
+
// Shell path never has a live id — flag as unavailable so the
|
|
209
|
+
// renderer can dim the row.
|
|
210
|
+
available: false,
|
|
211
|
+
note: 'shell-mode: showing last NDJSON activity instead of a live id',
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
catch (error) {
|
|
215
|
+
return {
|
|
216
|
+
key: 'session',
|
|
217
|
+
label: 'Session',
|
|
218
|
+
value: 'unknown',
|
|
219
|
+
available: false,
|
|
220
|
+
note: errorNote(error),
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
function buildPermissionModeField(deps) {
|
|
225
|
+
try {
|
|
226
|
+
const mode = deps.resolvePermissionMode();
|
|
227
|
+
if (mode && mode.length > 0) {
|
|
228
|
+
return {
|
|
229
|
+
key: 'mode',
|
|
230
|
+
label: 'Mode',
|
|
231
|
+
value: mode,
|
|
232
|
+
available: true,
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
return {
|
|
236
|
+
key: 'mode',
|
|
237
|
+
label: 'Mode',
|
|
238
|
+
value: 'unknown',
|
|
239
|
+
available: false,
|
|
240
|
+
note: 'permission state module not yet present (lands with L6)',
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
catch (error) {
|
|
244
|
+
return {
|
|
245
|
+
key: 'mode',
|
|
246
|
+
label: 'Mode',
|
|
247
|
+
value: 'unknown',
|
|
248
|
+
available: false,
|
|
249
|
+
note: errorNote(error),
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
function buildTokenField(deps) {
|
|
254
|
+
if (typeof deps.liveTokensUsed === 'number' && deps.liveTokensUsed >= 0) {
|
|
255
|
+
return {
|
|
256
|
+
key: 'tokens',
|
|
257
|
+
label: 'Tokens',
|
|
258
|
+
value: `~${formatThousands(deps.liveTokensUsed)} used this session`,
|
|
259
|
+
available: true,
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
return {
|
|
263
|
+
key: 'tokens',
|
|
264
|
+
label: 'Tokens',
|
|
265
|
+
value: 'n/a (REPL not active)',
|
|
266
|
+
available: false,
|
|
267
|
+
note: 'token counter only available inside a live REPL session',
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
function buildDispatchField(deps) {
|
|
271
|
+
const dir = `${deps.cwd}/.pugi/agent-progress`;
|
|
272
|
+
try {
|
|
273
|
+
if (!deps.fs.existsSync(dir)) {
|
|
274
|
+
return {
|
|
275
|
+
key: 'dispatches',
|
|
276
|
+
label: 'Dispatches',
|
|
277
|
+
value: '0 active, 0 completed',
|
|
278
|
+
available: false,
|
|
279
|
+
note: '.pugi/agent-progress/ not present',
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
const entries = deps.fs.readdirSync(dir);
|
|
283
|
+
let active = 0;
|
|
284
|
+
let completed = 0;
|
|
285
|
+
for (const entry of entries) {
|
|
286
|
+
if (!entry.endsWith('.json'))
|
|
287
|
+
continue;
|
|
288
|
+
try {
|
|
289
|
+
const raw = deps.fs.readFileSync(`${dir}/${entry}`, 'utf8');
|
|
290
|
+
const parsed = JSON.parse(raw);
|
|
291
|
+
const status = typeof parsed.status === 'string' ? parsed.status.toLowerCase() : null;
|
|
292
|
+
const completedAt = typeof parsed.completedAt === 'string' ? parsed.completedAt : null;
|
|
293
|
+
if (status === 'complete' || status === 'completed' || completedAt) {
|
|
294
|
+
completed += 1;
|
|
295
|
+
}
|
|
296
|
+
else {
|
|
297
|
+
active += 1;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
catch {
|
|
301
|
+
// Malformed progress JSON shouldn't crash the snapshot.
|
|
302
|
+
// Count it as active so the operator sees the row + can
|
|
303
|
+
// open the file manually.
|
|
304
|
+
active += 1;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
return {
|
|
308
|
+
key: 'dispatches',
|
|
309
|
+
label: 'Dispatches',
|
|
310
|
+
value: `${active} active, ${completed} completed`,
|
|
311
|
+
available: true,
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
catch (error) {
|
|
315
|
+
return {
|
|
316
|
+
key: 'dispatches',
|
|
317
|
+
label: 'Dispatches',
|
|
318
|
+
value: '0 active, 0 completed',
|
|
319
|
+
available: false,
|
|
320
|
+
note: errorNote(error),
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
function buildLastCommandField(deps) {
|
|
325
|
+
if (typeof deps.lastCommand === 'string' && deps.lastCommand.trim().length > 0) {
|
|
326
|
+
const cmd = truncate(deps.lastCommand.trim(), 60);
|
|
327
|
+
const agoLabel = typeof deps.lastCommandAtEpochMs === 'number' && deps.lastCommandAtEpochMs > 0
|
|
328
|
+
? ` (${formatAgeShort(Math.max(0, deps.now() - deps.lastCommandAtEpochMs))} ago)`
|
|
329
|
+
: '';
|
|
330
|
+
return {
|
|
331
|
+
key: 'lastCommand',
|
|
332
|
+
label: 'Last cmd',
|
|
333
|
+
value: `${cmd}${agoLabel}`,
|
|
334
|
+
available: true,
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
return {
|
|
338
|
+
key: 'lastCommand',
|
|
339
|
+
label: 'Last cmd',
|
|
340
|
+
value: 'n/a',
|
|
341
|
+
available: false,
|
|
342
|
+
note: 'no command observed in this session',
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
function buildCompactField(deps) {
|
|
346
|
+
const eventsPath = `${deps.cwd}/.pugi/events.jsonl`;
|
|
347
|
+
try {
|
|
348
|
+
if (!deps.fs.existsSync(eventsPath)) {
|
|
349
|
+
return {
|
|
350
|
+
key: 'compact',
|
|
351
|
+
label: 'Compact',
|
|
352
|
+
value: '0 boundary markers',
|
|
353
|
+
available: false,
|
|
354
|
+
note: '.pugi/events.jsonl not present',
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
const raw = deps.fs.readFileSync(eventsPath, 'utf8');
|
|
358
|
+
let count = 0;
|
|
359
|
+
for (const line of raw.split('\n')) {
|
|
360
|
+
const trimmed = line.trim();
|
|
361
|
+
if (trimmed.length === 0)
|
|
362
|
+
continue;
|
|
363
|
+
try {
|
|
364
|
+
const parsed = JSON.parse(trimmed);
|
|
365
|
+
const kind = typeof parsed.kind === 'string' ? parsed.kind : null;
|
|
366
|
+
const type = typeof parsed.type === 'string' ? parsed.type : null;
|
|
367
|
+
if (kind === 'compact.boundary' || type === 'compact.boundary') {
|
|
368
|
+
count += 1;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
catch {
|
|
372
|
+
// Malformed line in NDJSON is not a snapshot failure.
|
|
373
|
+
// Skip and keep counting.
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
return {
|
|
377
|
+
key: 'compact',
|
|
378
|
+
label: 'Compact',
|
|
379
|
+
value: `${count} boundary marker${count === 1 ? '' : 's'}`,
|
|
380
|
+
available: true,
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
catch (error) {
|
|
384
|
+
return {
|
|
385
|
+
key: 'compact',
|
|
386
|
+
label: 'Compact',
|
|
387
|
+
value: '0 boundary markers',
|
|
388
|
+
available: false,
|
|
389
|
+
note: errorNote(error),
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
function buildAuthField(deps) {
|
|
394
|
+
try {
|
|
395
|
+
const cred = deps.resolveCredential();
|
|
396
|
+
if (!cred) {
|
|
397
|
+
return {
|
|
398
|
+
key: 'auth',
|
|
399
|
+
label: 'Auth',
|
|
400
|
+
value: 'not signed in',
|
|
401
|
+
available: false,
|
|
402
|
+
note: 'run `pugi login` к authenticate',
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
const identity = cred.identity ?? cred.label ?? cred.apiUrl;
|
|
406
|
+
const tier = cred.tier ? ` (tier: ${cred.tier})` : '';
|
|
407
|
+
return {
|
|
408
|
+
key: 'auth',
|
|
409
|
+
label: 'Auth',
|
|
410
|
+
value: `${identity}${tier}`,
|
|
411
|
+
available: true,
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
catch (error) {
|
|
415
|
+
return {
|
|
416
|
+
key: 'auth',
|
|
417
|
+
label: 'Auth',
|
|
418
|
+
value: 'unknown',
|
|
419
|
+
available: false,
|
|
420
|
+
note: errorNote(error),
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
function buildConnectionField(deps) {
|
|
425
|
+
try {
|
|
426
|
+
const cred = deps.resolveCredential();
|
|
427
|
+
if (!cred) {
|
|
428
|
+
return {
|
|
429
|
+
key: 'connection',
|
|
430
|
+
label: 'Connection',
|
|
431
|
+
value: 'offline (no credential)',
|
|
432
|
+
available: false,
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
return {
|
|
436
|
+
key: 'connection',
|
|
437
|
+
label: 'Connection',
|
|
438
|
+
value: cred.apiUrl,
|
|
439
|
+
available: true,
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
catch {
|
|
443
|
+
return {
|
|
444
|
+
key: 'connection',
|
|
445
|
+
label: 'Connection',
|
|
446
|
+
value: 'unknown',
|
|
447
|
+
available: false,
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
/* ---------------------------- formatters ---------------------------- */
|
|
452
|
+
export function formatAgeShort(ms) {
|
|
453
|
+
if (!Number.isFinite(ms) || ms < 0)
|
|
454
|
+
return '0s';
|
|
455
|
+
if (ms < 60_000)
|
|
456
|
+
return `${Math.round(ms / 1000)} sec`;
|
|
457
|
+
if (ms < 3_600_000)
|
|
458
|
+
return `${Math.round(ms / 60_000)} min`;
|
|
459
|
+
if (ms < 86_400_000)
|
|
460
|
+
return `${Math.round(ms / 3_600_000)} hr`;
|
|
461
|
+
return `${Math.round(ms / 86_400_000)} day`;
|
|
462
|
+
}
|
|
463
|
+
export function formatThousands(value) {
|
|
464
|
+
if (!Number.isFinite(value) || value < 0)
|
|
465
|
+
return '0';
|
|
466
|
+
return Math.round(value).toLocaleString('en-US');
|
|
467
|
+
}
|
|
468
|
+
export function shortId(id) {
|
|
469
|
+
// The full ULID / UUID is awkward in a table cell. Keep the
|
|
470
|
+
// first 24 chars — long enough к stay collision-free across
|
|
471
|
+
// human-friendly session ids (pugi-sess-<word>, ULID prefixes,
|
|
472
|
+
// 18-char tenant slugs) and к surface meaningful identifiers
|
|
473
|
+
// в the REPL `/status` row without truncating mid-word.
|
|
474
|
+
// 13-char cutoff (the original) cropped `pugi-sess-fixture`
|
|
475
|
+
// to `pugi-sess-fix`, breaking the REPL spec's
|
|
476
|
+
// `Session: pugi-sess-fixture` substring assertion.
|
|
477
|
+
return id.length > 24 ? id.slice(0, 24) : id;
|
|
478
|
+
}
|
|
479
|
+
export function truncate(text, max) {
|
|
480
|
+
if (text.length <= max)
|
|
481
|
+
return text;
|
|
482
|
+
return `${text.slice(0, Math.max(1, max - 1))}…`;
|
|
483
|
+
}
|
|
484
|
+
function errorNote(error) {
|
|
485
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
486
|
+
return `field unavailable: ${message}`;
|
|
487
|
+
}
|
|
488
|
+
//# sourceMappingURL=status-snapshot.js.map
|