@pugi/cli 0.1.0-beta.10 → 0.1.0-beta.101
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/README.md +55 -11
- 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/retro.js +210 -0
- 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/db.js +506 -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/codegraph/parser.js +598 -0
- package/dist/core/codegraph/queries/go.scm +57 -0
- package/dist/core/codegraph/queries/javascript.scm +56 -0
- package/dist/core/codegraph/queries/python.scm +55 -0
- package/dist/core/codegraph/queries/rust.scm +63 -0
- package/dist/core/codegraph/queries/typescript.scm +91 -0
- package/dist/core/codegraph/reindex.js +218 -0
- package/dist/core/codegraph/resolve-edges.js +107 -0
- package/dist/core/codegraph/types.js +34 -0
- package/dist/core/codegraph/watcher.js +440 -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 +67 -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 +247 -0
- package/dist/core/engine/budgets.js +220 -0
- package/dist/core/engine/compact-llm-summarizer.js +124 -0
- package/dist/core/engine/context-prefix.js +155 -0
- package/dist/core/engine/index.js +1 -1
- package/dist/core/engine/intensity.js +163 -0
- package/dist/core/engine/intent.js +260 -0
- package/dist/core/engine/native-pugi.js +1559 -227
- package/dist/core/engine/prompts.js +219 -19
- package/dist/core/engine/strip-internal-fields.js +124 -0
- package/dist/core/engine/tool-bridge.js +1887 -59
- package/dist/core/engine/verification-patterns.js +195 -0
- package/dist/core/eval/v1/ledger.js +83 -0
- package/dist/core/eval/v1/runner.js +280 -0
- package/dist/core/eval/v1/scoring.js +68 -0
- package/dist/core/eval/v1/task-loader.js +191 -0
- package/dist/core/eval/v1/types.js +14 -0
- package/dist/core/eval/v1/verifier.js +176 -0
- package/dist/core/eval/v1/yaml-parser.js +250 -0
- 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-config.js +192 -0
- package/dist/core/mcp/orchestrator-tools.js +806 -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/notes/notes-paths.js +113 -0
- package/dist/core/notes/notes-recorder.js +140 -0
- package/dist/core/notes/notes-writer.js +53 -0
- package/dist/core/notes/renderers.js +0 -0
- package/dist/core/notes/slug.js +105 -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 +107 -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-gitignore.js +52 -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/engine-bridge.js +303 -0
- 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 +2690 -229
- package/dist/core/repl/slash-commands.js +540 -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/tool-route.js +382 -0
- 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/retro/git-collector.js +251 -0
- package/dist/core/retro/health-card.js +25 -0
- package/dist/core/retro/metrics.js +342 -0
- package/dist/core/retro/narrative.js +249 -0
- package/dist/core/retro/plane-collector.js +274 -0
- package/dist/core/retro/pr-issue-link.js +65 -0
- package/dist/core/retro/types.js +16 -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/sandboxing/adapter.js +43 -0
- package/dist/core/sandboxing/bubblewrap.js +209 -0
- package/dist/core/sandboxing/index.js +78 -0
- package/dist/core/sandboxing/none.js +19 -0
- package/dist/core/sandboxing/policy.js +97 -0
- package/dist/core/sandboxing/seatbelt.js +231 -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 +119 -0
- package/dist/core/settings.js +402 -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 +146 -52
- 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 +4403 -561
- 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 +74 -40
- 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/eval-v1.js +266 -0
- package/dist/runtime/commands/feedback.js +184 -0
- package/dist/runtime/commands/hooks.js +187 -0
- package/dist/runtime/commands/index-cmd.js +459 -0
- package/dist/runtime/commands/init.js +254 -0
- package/dist/runtime/commands/lsp.js +200 -38
- package/dist/runtime/commands/mcp.js +935 -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/servers-cli.js +182 -0
- package/dist/runtime/commands/servers.js +236 -0
- 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/deprecation-warning.js +69 -0
- package/dist/runtime/engine-exit-code.js +50 -0
- package/dist/runtime/headless-repl.js +195 -0
- package/dist/runtime/headless.js +548 -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/stream-renderer.js +195 -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 +811 -49
- 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/http-request.js +336 -0
- 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 +120 -5
- package/dist/tools/server-tools.js +892 -0
- 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 +22 -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 +239 -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 +29 -6
- 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 +11 -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,440 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pugi local symbol index - chokidar file watcher (PR L2).
|
|
3
|
+
*
|
|
4
|
+
* Live auto-sync layer на top of the parser (PR L1) и DB (PR L0). When
|
|
5
|
+
* the operator runs `pugi index --watch`, this module:
|
|
6
|
+
*
|
|
7
|
+
* 1. Spins up a chokidar watcher rooted at `workspaceRoot`.
|
|
8
|
+
* 2. Filters events to the v1 parseable extensions
|
|
9
|
+
* (`PARSEABLE_EXTENSIONS` from parser.ts).
|
|
10
|
+
* 3. Coalesces bursts via a 2-second debounce (configurable; tests
|
|
11
|
+
* pass `debounceMs: 50` to keep specs fast).
|
|
12
|
+
* 4. On flush: per file -> `deleteFile` cascade -> `parseFile` ->
|
|
13
|
+
* `insertSymbols` -> resolve `pendingEdges` -> `insertEdges`.
|
|
14
|
+
* 5. On `unlink`: `deleteFile` only (cascade removes symbols + edges
|
|
15
|
+
* via the schema's FK ON DELETE CASCADE).
|
|
16
|
+
*
|
|
17
|
+
* Architectural decisions:
|
|
18
|
+
*
|
|
19
|
+
* - **No nested transactions.** `deleteFile` / `insertSymbols` /
|
|
20
|
+
* `insertEdges` each open + commit their own BEGIN block. We call
|
|
21
|
+
* them sequentially and rely on per-call atomicity. A higher-level
|
|
22
|
+
* SAVEPOINT wrapper was considered и rejected: chokidar batches are
|
|
23
|
+
* small (typically 1-5 files) and the per-file work is already
|
|
24
|
+
* transactional, so the extra level adds complexity without a
|
|
25
|
+
* customer-visible win.
|
|
26
|
+
*
|
|
27
|
+
* - **Two-step edge resolution.** `parseFile` returns `pendingEdges`
|
|
28
|
+
* keyed by name + scope. We resolve them after `insertSymbols`
|
|
29
|
+
* populates ids, using the shared `resolvePendingEdges` helper.
|
|
30
|
+
* Cross-file targets are looked up via a SQL `SELECT id FROM
|
|
31
|
+
* symbols WHERE name = ?` closure - the live DB IS the cross-file
|
|
32
|
+
* map for the watcher. Reindex builds an in-memory map because it
|
|
33
|
+
* sees every file in one pass; the watcher sees files one delta at
|
|
34
|
+
* a time.
|
|
35
|
+
*
|
|
36
|
+
* - **Path normalization.** Chokidar emits absolute or cwd-relative
|
|
37
|
+
* paths depending on options. We always work in workspace-relative
|
|
38
|
+
* POSIX form (`path.relative(root, abs).split(sep).join('/')`)
|
|
39
|
+
* because that is what `reindex.ts` writes to `Symbol.file` and
|
|
40
|
+
* `files.path`. Mismatched separators on Windows would break the
|
|
41
|
+
* FK + the `deleteFile` lookup.
|
|
42
|
+
*
|
|
43
|
+
* - **Exclude list parity.** We use the EXACT same `PRUNE_DIRS` as
|
|
44
|
+
* `reindex.ts` (extended slightly with the v1 extensions the spec
|
|
45
|
+
* pinned). Divergence between the walker and the watcher would
|
|
46
|
+
* silently produce different indexes depending on entry point.
|
|
47
|
+
*
|
|
48
|
+
* - **In-flight queue at close.** SIGINT / SIGTERM trigger
|
|
49
|
+
* `close()`. If a debounce timer is still pending, we flush it
|
|
50
|
+
* synchronously before tearing down chokidar - dropping a pending
|
|
51
|
+
* edit would leave the index out of sync until the next manual
|
|
52
|
+
* `pugi index` run.
|
|
53
|
+
*
|
|
54
|
+
* - **Initial scan is the operator's job.** `ignoreInitial: true`
|
|
55
|
+
* means we do NOT re-index the workspace on startup. The CLI
|
|
56
|
+
* command runs `reindexWorkspace` first (covered by PR L1) и THEN
|
|
57
|
+
* starts the watcher; this avoids a double-pass over the
|
|
58
|
+
* workspace.
|
|
59
|
+
*/
|
|
60
|
+
import { existsSync } from 'node:fs';
|
|
61
|
+
import { readFile } from 'node:fs/promises';
|
|
62
|
+
import { createHash } from 'node:crypto';
|
|
63
|
+
import { relative, resolve, sep, extname } from 'node:path';
|
|
64
|
+
import chokidar from 'chokidar';
|
|
65
|
+
import { deleteFile, insertEdges, insertSymbols, upsertFile, } from './db.js';
|
|
66
|
+
import { PARSEABLE_EXTENSIONS, parseFile } from './parser.js';
|
|
67
|
+
import { buildPerFileNameMap, resolvePendingEdges, } from './resolve-edges.js';
|
|
68
|
+
/**
|
|
69
|
+
* Directories pruned from the watch. EXACT mirror of
|
|
70
|
+
* `reindex.ts:PRUNE_DIRS` so the walker и watcher agree on what
|
|
71
|
+
* "workspace" means. Touching this list requires touching both
|
|
72
|
+
* sites.
|
|
73
|
+
*/
|
|
74
|
+
const PRUNE_DIRS = Object.freeze([
|
|
75
|
+
'node_modules',
|
|
76
|
+
'.git',
|
|
77
|
+
'dist',
|
|
78
|
+
'build',
|
|
79
|
+
'.next',
|
|
80
|
+
'.turbo',
|
|
81
|
+
'.cache',
|
|
82
|
+
'coverage',
|
|
83
|
+
'.pugi',
|
|
84
|
+
]);
|
|
85
|
+
/** Default debounce window between burst-of-events and flush. */
|
|
86
|
+
const DEFAULT_DEBOUNCE_MS = 2000;
|
|
87
|
+
/**
|
|
88
|
+
* Start a chokidar watcher rooted at `workspaceRoot` against the
|
|
89
|
+
* caller-owned IndexDB. Returns a handle the caller invokes к stop the
|
|
90
|
+
* watcher cleanly.
|
|
91
|
+
*
|
|
92
|
+
* The function is sync - the watcher is "started" the moment chokidar
|
|
93
|
+
* exists, even if it has не yet emitted `ready`. The first `add`/
|
|
94
|
+
* `change` events flow into the debounce queue immediately.
|
|
95
|
+
*/
|
|
96
|
+
export function startWatcher(opts) {
|
|
97
|
+
const root = resolve(opts.workspaceRoot);
|
|
98
|
+
if (!existsSync(root)) {
|
|
99
|
+
throw new Error(`startWatcher: workspaceRoot does not exist: ${root}`);
|
|
100
|
+
}
|
|
101
|
+
const debounceMs = Math.max(0, opts.debounceMs ?? DEFAULT_DEBOUNCE_MS);
|
|
102
|
+
const writeStderr = opts.writeStderr ?? ((text) => void process.stderr.write(text));
|
|
103
|
+
const watchFn = opts.chokidarWatchFn ?? chokidar.watch;
|
|
104
|
+
const quiet = opts.quiet === true;
|
|
105
|
+
const stats = {
|
|
106
|
+
added: 0,
|
|
107
|
+
changed: 0,
|
|
108
|
+
removed: 0,
|
|
109
|
+
reparsed: 0,
|
|
110
|
+
edgesInserted: 0,
|
|
111
|
+
edgesOrphaned: 0,
|
|
112
|
+
parseErrors: 0,
|
|
113
|
+
watcherErrors: 0,
|
|
114
|
+
};
|
|
115
|
+
/** Workspace-relative POSIX path. */
|
|
116
|
+
const toRelPosix = (abs) => {
|
|
117
|
+
const rel = relative(root, abs);
|
|
118
|
+
if (rel.length === 0 || rel.startsWith('..'))
|
|
119
|
+
return '';
|
|
120
|
+
return rel.split(sep).join('/');
|
|
121
|
+
};
|
|
122
|
+
/**
|
|
123
|
+
* Defense in depth: chokidar matches on directory globs, but a
|
|
124
|
+
* watched-parent-dir event still might fire для excluded subpaths
|
|
125
|
+
* on some platforms. Re-check the relative path against the prune
|
|
126
|
+
* list before doing any work.
|
|
127
|
+
*/
|
|
128
|
+
const isExcluded = (relPosix) => {
|
|
129
|
+
if (relPosix.length === 0)
|
|
130
|
+
return true;
|
|
131
|
+
const parts = relPosix.split('/');
|
|
132
|
+
for (const p of parts) {
|
|
133
|
+
if (PRUNE_DIRS.includes(p))
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
return false;
|
|
137
|
+
};
|
|
138
|
+
const isParseable = (relPosix) => {
|
|
139
|
+
const ext = extname(relPosix).toLowerCase();
|
|
140
|
+
return PARSEABLE_EXTENSIONS.includes(ext);
|
|
141
|
+
};
|
|
142
|
+
// ---- queue + debounce -------------------------------------------------
|
|
143
|
+
/** Files queued для re-parse on the next flush. */
|
|
144
|
+
const dirty = new Set();
|
|
145
|
+
/** Files queued для unlink-cascade on the next flush. */
|
|
146
|
+
const removed = new Set();
|
|
147
|
+
let timer = null;
|
|
148
|
+
/** Tracks the in-progress flush so close() can await it. */
|
|
149
|
+
let flushing = null;
|
|
150
|
+
/** Once closed, ignore further events. */
|
|
151
|
+
let closed = false;
|
|
152
|
+
const armDebounce = () => {
|
|
153
|
+
if (closed)
|
|
154
|
+
return;
|
|
155
|
+
if (timer)
|
|
156
|
+
clearTimeout(timer);
|
|
157
|
+
timer = setTimeout(() => {
|
|
158
|
+
timer = null;
|
|
159
|
+
void flush();
|
|
160
|
+
}, debounceMs);
|
|
161
|
+
};
|
|
162
|
+
/**
|
|
163
|
+
* Drain the queue. Sequential per-file work - the parser is async,
|
|
164
|
+
* SQLite handles are sync but already wrapped in per-call BEGIN/
|
|
165
|
+
* COMMIT inside db.ts, so concurrency would only add lock contention
|
|
166
|
+
* without throughput gain on a single-process watcher.
|
|
167
|
+
*/
|
|
168
|
+
const flush = async () => {
|
|
169
|
+
// If a flush is already in-flight, queue behind it. We serialize
|
|
170
|
+
// so the dirty Set drained by one flush never collides with one
|
|
171
|
+
// mutated by another.
|
|
172
|
+
if (flushing) {
|
|
173
|
+
await flushing;
|
|
174
|
+
// Nothing new since the previous flush picked it up? Bail.
|
|
175
|
+
if (dirty.size === 0 && removed.size === 0)
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
flushing = doFlush().finally(() => {
|
|
179
|
+
flushing = null;
|
|
180
|
+
});
|
|
181
|
+
return flushing;
|
|
182
|
+
};
|
|
183
|
+
const doFlush = async () => {
|
|
184
|
+
const snapshotDirty = Array.from(dirty);
|
|
185
|
+
dirty.clear();
|
|
186
|
+
const snapshotRemoved = Array.from(removed);
|
|
187
|
+
removed.clear();
|
|
188
|
+
// Process removals first - if a file was deleted and re-added in
|
|
189
|
+
// the same window we want the add half to write fresh rows.
|
|
190
|
+
for (const relPath of snapshotRemoved) {
|
|
191
|
+
try {
|
|
192
|
+
deleteFile(opts.db, relPath);
|
|
193
|
+
}
|
|
194
|
+
catch (err) {
|
|
195
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
196
|
+
if (!quiet) {
|
|
197
|
+
writeStderr(`pugi index --watch: deleteFile(${relPath}) failed: ${message}\n`);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
for (const relPath of snapshotDirty) {
|
|
202
|
+
// Drop файлы that were also removed in this batch - the unlink
|
|
203
|
+
// above already wiped them.
|
|
204
|
+
if (snapshotRemoved.includes(relPath))
|
|
205
|
+
continue;
|
|
206
|
+
const abs = resolve(root, relPath);
|
|
207
|
+
let bytes;
|
|
208
|
+
try {
|
|
209
|
+
bytes = await readFile(abs, 'utf8');
|
|
210
|
+
}
|
|
211
|
+
catch {
|
|
212
|
+
// Disappeared between event и flush - treat as unlink.
|
|
213
|
+
try {
|
|
214
|
+
deleteFile(opts.db, relPath);
|
|
215
|
+
}
|
|
216
|
+
catch (err) {
|
|
217
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
218
|
+
if (!quiet) {
|
|
219
|
+
writeStderr(`pugi index --watch: post-disappear deleteFile(${relPath}) failed: ${message}\n`);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
continue;
|
|
223
|
+
}
|
|
224
|
+
let result;
|
|
225
|
+
try {
|
|
226
|
+
result = await parseFile(abs, {
|
|
227
|
+
relPath,
|
|
228
|
+
quiet: true,
|
|
229
|
+
sourceOverride: bytes,
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
catch (err) {
|
|
233
|
+
stats.parseErrors += 1;
|
|
234
|
+
if (!quiet) {
|
|
235
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
236
|
+
writeStderr(`pugi index --watch: parse(${relPath}) failed: ${message}\n`);
|
|
237
|
+
}
|
|
238
|
+
continue;
|
|
239
|
+
}
|
|
240
|
+
// Clear prior rows for this file before re-inserting. Cheap
|
|
241
|
+
// даже if the file is new - the lookup hits the `idx_symbols_file`
|
|
242
|
+
// index и returns zero rows.
|
|
243
|
+
try {
|
|
244
|
+
deleteFile(opts.db, relPath);
|
|
245
|
+
}
|
|
246
|
+
catch (err) {
|
|
247
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
248
|
+
if (!quiet) {
|
|
249
|
+
writeStderr(`pugi index --watch: deleteFile(${relPath}) failed: ${message}\n`);
|
|
250
|
+
}
|
|
251
|
+
continue;
|
|
252
|
+
}
|
|
253
|
+
if (result.symbols.length === 0) {
|
|
254
|
+
// Record the file fingerprint so a future "did this change?"
|
|
255
|
+
// query can short-circuit. Only when we got далеко enough to
|
|
256
|
+
// recognise the language - skipping unsupported extensions
|
|
257
|
+
// earlier means we never see them here.
|
|
258
|
+
if (result.language !== null) {
|
|
259
|
+
try {
|
|
260
|
+
upsertFile(opts.db, {
|
|
261
|
+
path: relPath,
|
|
262
|
+
sha256: sha256(bytes),
|
|
263
|
+
lastIndexedAt: new Date().toISOString(),
|
|
264
|
+
symbolCount: 0,
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
catch (err) {
|
|
268
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
269
|
+
if (!quiet) {
|
|
270
|
+
writeStderr(`pugi index --watch: upsertFile(${relPath}) failed: ${message}\n`);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
stats.reparsed += 1;
|
|
275
|
+
continue;
|
|
276
|
+
}
|
|
277
|
+
let ids;
|
|
278
|
+
try {
|
|
279
|
+
ids = insertSymbols(opts.db, result.symbols);
|
|
280
|
+
}
|
|
281
|
+
catch (err) {
|
|
282
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
283
|
+
if (!quiet) {
|
|
284
|
+
writeStderr(`pugi index --watch: insertSymbols(${relPath}) failed: ${message}\n`);
|
|
285
|
+
}
|
|
286
|
+
continue;
|
|
287
|
+
}
|
|
288
|
+
const perFile = buildPerFileNameMap(result.symbols, ids);
|
|
289
|
+
const { resolved, orphaned } = resolvePendingEdges(result.pendingEdges, perFile, (name) => crossFileLookup(opts.db, name));
|
|
290
|
+
stats.edgesOrphaned += orphaned;
|
|
291
|
+
if (resolved.length > 0) {
|
|
292
|
+
try {
|
|
293
|
+
insertEdges(opts.db, resolved);
|
|
294
|
+
stats.edgesInserted += resolved.length;
|
|
295
|
+
}
|
|
296
|
+
catch (err) {
|
|
297
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
298
|
+
if (!quiet) {
|
|
299
|
+
writeStderr(`pugi index --watch: insertEdges(${relPath}) failed: ${message}\n`);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
try {
|
|
304
|
+
upsertFile(opts.db, {
|
|
305
|
+
path: relPath,
|
|
306
|
+
sha256: sha256(bytes),
|
|
307
|
+
lastIndexedAt: new Date().toISOString(),
|
|
308
|
+
symbolCount: ids.length,
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
catch (err) {
|
|
312
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
313
|
+
if (!quiet) {
|
|
314
|
+
writeStderr(`pugi index --watch: upsertFile(${relPath}) failed: ${message}\n`);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
stats.reparsed += 1;
|
|
318
|
+
}
|
|
319
|
+
};
|
|
320
|
+
// ---- chokidar wiring --------------------------------------------------
|
|
321
|
+
// We watch the workspace root recursively and let our own filter
|
|
322
|
+
// drop unparseable extensions + excluded directories. Chokidar's
|
|
323
|
+
// built-in `ignored` accepts glob/regex/function; we use a function
|
|
324
|
+
// form so we can re-use the prune list verbatim instead of
|
|
325
|
+
// duplicating it as a glob array.
|
|
326
|
+
const ignored = (p) => {
|
|
327
|
+
// chokidar may pass absolute or workspace-relative paths depending
|
|
328
|
+
// on platform и `cwd` option; normalize then reuse the predicate.
|
|
329
|
+
const abs = resolve(p);
|
|
330
|
+
const rel = toRelPosix(abs);
|
|
331
|
+
if (rel.length === 0)
|
|
332
|
+
return false; // root itself - keep watching
|
|
333
|
+
return isExcluded(rel);
|
|
334
|
+
};
|
|
335
|
+
const watcher = watchFn(root, {
|
|
336
|
+
ignored,
|
|
337
|
+
ignoreInitial: true,
|
|
338
|
+
persistent: true,
|
|
339
|
+
followSymlinks: false,
|
|
340
|
+
awaitWriteFinish: {
|
|
341
|
+
stabilityThreshold: Math.min(50, Math.max(10, Math.floor(debounceMs / 4))),
|
|
342
|
+
pollInterval: 10,
|
|
343
|
+
},
|
|
344
|
+
});
|
|
345
|
+
const onChange = (kind, abs) => {
|
|
346
|
+
if (closed)
|
|
347
|
+
return;
|
|
348
|
+
const rel = toRelPosix(abs);
|
|
349
|
+
if (isExcluded(rel) || !isParseable(rel))
|
|
350
|
+
return;
|
|
351
|
+
if (kind === 'add')
|
|
352
|
+
stats.added += 1;
|
|
353
|
+
else
|
|
354
|
+
stats.changed += 1;
|
|
355
|
+
dirty.add(rel);
|
|
356
|
+
armDebounce();
|
|
357
|
+
};
|
|
358
|
+
const onUnlink = (abs) => {
|
|
359
|
+
if (closed)
|
|
360
|
+
return;
|
|
361
|
+
const rel = toRelPosix(abs);
|
|
362
|
+
if (isExcluded(rel) || !isParseable(rel))
|
|
363
|
+
return;
|
|
364
|
+
stats.removed += 1;
|
|
365
|
+
// If the file is also pending как dirty, drop the dirty marker -
|
|
366
|
+
// the unlink wins.
|
|
367
|
+
dirty.delete(rel);
|
|
368
|
+
removed.add(rel);
|
|
369
|
+
armDebounce();
|
|
370
|
+
};
|
|
371
|
+
watcher.on('add', (p) => onChange('add', p));
|
|
372
|
+
watcher.on('change', (p) => onChange('change', p));
|
|
373
|
+
watcher.on('unlink', (p) => onUnlink(p));
|
|
374
|
+
watcher.on('error', (err) => {
|
|
375
|
+
stats.watcherErrors += 1;
|
|
376
|
+
if (!quiet) {
|
|
377
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
378
|
+
writeStderr(`pugi index --watch: chokidar error: ${message}\n`);
|
|
379
|
+
}
|
|
380
|
+
});
|
|
381
|
+
const close = async () => {
|
|
382
|
+
if (closed)
|
|
383
|
+
return;
|
|
384
|
+
closed = true;
|
|
385
|
+
if (timer) {
|
|
386
|
+
clearTimeout(timer);
|
|
387
|
+
timer = null;
|
|
388
|
+
}
|
|
389
|
+
// Drain any pending events before closing the watcher.
|
|
390
|
+
if (dirty.size > 0 || removed.size > 0) {
|
|
391
|
+
try {
|
|
392
|
+
await flush();
|
|
393
|
+
}
|
|
394
|
+
catch (err) {
|
|
395
|
+
if (!quiet) {
|
|
396
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
397
|
+
writeStderr(`pugi index --watch: close-time flush failed: ${message}\n`);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
// Wait for any in-progress flush even if the queue was empty.
|
|
402
|
+
if (flushing) {
|
|
403
|
+
try {
|
|
404
|
+
await flushing;
|
|
405
|
+
}
|
|
406
|
+
catch {
|
|
407
|
+
/* already surfaced via writeStderr above */
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
await watcher.close();
|
|
411
|
+
};
|
|
412
|
+
const flushNow = async () => {
|
|
413
|
+
if (timer) {
|
|
414
|
+
clearTimeout(timer);
|
|
415
|
+
timer = null;
|
|
416
|
+
}
|
|
417
|
+
await flush();
|
|
418
|
+
};
|
|
419
|
+
return {
|
|
420
|
+
close,
|
|
421
|
+
flushNow,
|
|
422
|
+
stats: () => ({ ...stats }),
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
/**
|
|
426
|
+
* Cross-file target resolver backed by the live `symbols` table.
|
|
427
|
+
* Returns the first matching row id - deterministic stability via the
|
|
428
|
+
* autoincrement order, mirrors `reindex.ts`'s "first-write-wins"
|
|
429
|
+
* convention. `null` when no symbol carries the requested name.
|
|
430
|
+
*/
|
|
431
|
+
function crossFileLookup(db, name) {
|
|
432
|
+
const row = db.conn
|
|
433
|
+
.prepare('SELECT id FROM symbols WHERE name = ? ORDER BY id ASC LIMIT 1')
|
|
434
|
+
.get(name);
|
|
435
|
+
return row?.id ?? null;
|
|
436
|
+
}
|
|
437
|
+
function sha256(s) {
|
|
438
|
+
return createHash('sha256').update(s).digest('hex');
|
|
439
|
+
}
|
|
440
|
+
//# sourceMappingURL=watcher.js.map
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-compact threshold gate.
|
|
3
|
+
*
|
|
4
|
+
* Decides whether the conversation buffer has crossed the threshold
|
|
5
|
+
* percent of the active model's context window. The check runs after
|
|
6
|
+
* every operator/persona turn before the NEXT operator input lands so
|
|
7
|
+
* the compaction completes BEFORE the model would have rejected the
|
|
8
|
+
* request with a context-overflow error.
|
|
9
|
+
*
|
|
10
|
+
* Design choices:
|
|
11
|
+
*
|
|
12
|
+
* - Pure function. The caller passes (tokenCount, windowSize, env).
|
|
13
|
+
* The gate returns a verdict; the session module owns the side
|
|
14
|
+
* effect of invoking the summariser. Pure-function shape keeps the
|
|
15
|
+
* spec exhaustive and the call site readable.
|
|
16
|
+
*
|
|
17
|
+
* - Hysteresis: once a compaction lands, the marker resets the
|
|
18
|
+
* baseline token count to "summary + tail" — the gate looks at the
|
|
19
|
+
* POST-marker tokens only. This is enforced upstream by the caller
|
|
20
|
+
* passing the post-marker count; the gate itself has no memory.
|
|
21
|
+
*
|
|
22
|
+
* - Two env knobs:
|
|
23
|
+
* PUGI_AUTOCOMPACT_DISABLED=1 — kill switch
|
|
24
|
+
* PUGI_AUTOCOMPACT_THRESHOLD=N — float in (0, 1] (default 0.75)
|
|
25
|
+
* Anything outside (0, 1] is rejected and the gate falls back to
|
|
26
|
+
* the default. Bad input never crashes the REPL.
|
|
27
|
+
*/
|
|
28
|
+
/** Default trip point as a fraction of the context window. */
|
|
29
|
+
export const DEFAULT_THRESHOLD = 0.75;
|
|
30
|
+
/**
|
|
31
|
+
* Decide whether to fire `/compact` automatically. Pure; safe to call
|
|
32
|
+
* after every turn.
|
|
33
|
+
*/
|
|
34
|
+
export function evaluateAutoCompact(input) {
|
|
35
|
+
const env = input.env ?? process.env;
|
|
36
|
+
const threshold = resolveThreshold(env);
|
|
37
|
+
if (input.windowSize <= 0 || !Number.isFinite(input.windowSize)) {
|
|
38
|
+
return {
|
|
39
|
+
kind: 'skip',
|
|
40
|
+
reason: 'invalid-window',
|
|
41
|
+
tokenCount: input.tokenCount,
|
|
42
|
+
windowSize: input.windowSize,
|
|
43
|
+
threshold,
|
|
44
|
+
pressure: 0,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
if (env['PUGI_AUTOCOMPACT_DISABLED'] === '1') {
|
|
48
|
+
return {
|
|
49
|
+
kind: 'skip',
|
|
50
|
+
reason: 'disabled',
|
|
51
|
+
tokenCount: input.tokenCount,
|
|
52
|
+
windowSize: input.windowSize,
|
|
53
|
+
threshold,
|
|
54
|
+
pressure: roundPressure(input.tokenCount / input.windowSize),
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
const pressure = roundPressure(input.tokenCount / input.windowSize);
|
|
58
|
+
if (pressure >= threshold) {
|
|
59
|
+
return {
|
|
60
|
+
kind: 'fire',
|
|
61
|
+
tokenCount: input.tokenCount,
|
|
62
|
+
windowSize: input.windowSize,
|
|
63
|
+
threshold,
|
|
64
|
+
pressure,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
kind: 'skip',
|
|
69
|
+
reason: 'below-threshold',
|
|
70
|
+
tokenCount: input.tokenCount,
|
|
71
|
+
windowSize: input.windowSize,
|
|
72
|
+
threshold,
|
|
73
|
+
pressure,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Resolve the threshold from env, clamping to the (0, 1] open-closed
|
|
78
|
+
* interval. Bad input silently falls back to DEFAULT_THRESHOLD so the
|
|
79
|
+
* REPL never crashes on a malformed environment variable.
|
|
80
|
+
*/
|
|
81
|
+
function resolveThreshold(env) {
|
|
82
|
+
const raw = env['PUGI_AUTOCOMPACT_THRESHOLD'];
|
|
83
|
+
if (!raw)
|
|
84
|
+
return DEFAULT_THRESHOLD;
|
|
85
|
+
const parsed = Number.parseFloat(raw);
|
|
86
|
+
if (!Number.isFinite(parsed) || parsed <= 0 || parsed > 1) {
|
|
87
|
+
return DEFAULT_THRESHOLD;
|
|
88
|
+
}
|
|
89
|
+
return parsed;
|
|
90
|
+
}
|
|
91
|
+
function roundPressure(raw) {
|
|
92
|
+
if (!Number.isFinite(raw) || raw < 0)
|
|
93
|
+
return 0;
|
|
94
|
+
return Math.round(raw * 1000) / 1000;
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=auto-trigger.js.map
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type guard: discriminate a SessionEvent against the `compaction`
|
|
3
|
+
* kind. Used by replay code so the boundary marker drives a different
|
|
4
|
+
* code path than `user`/`persona`/`system` transcript rows.
|
|
5
|
+
*/
|
|
6
|
+
export function isCompactBoundary(event) {
|
|
7
|
+
if (event.kind !== 'compaction')
|
|
8
|
+
return false;
|
|
9
|
+
const p = event.payload;
|
|
10
|
+
if (p === null || typeof p !== 'object')
|
|
11
|
+
return false;
|
|
12
|
+
if (p.version !== 1)
|
|
13
|
+
return false;
|
|
14
|
+
if (p.trigger !== 'manual' && p.trigger !== 'auto')
|
|
15
|
+
return false;
|
|
16
|
+
if (typeof p.summary !== 'string' || p.summary.length === 0)
|
|
17
|
+
return false;
|
|
18
|
+
if (typeof p.summaryTokenCount !== 'number')
|
|
19
|
+
return false;
|
|
20
|
+
if (typeof p.summaryTurnsBefore !== 'number')
|
|
21
|
+
return false;
|
|
22
|
+
if (typeof p.keptTailTurns !== 'number')
|
|
23
|
+
return false;
|
|
24
|
+
if (typeof p.coversUntilOffset !== 'number')
|
|
25
|
+
return false;
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Append one `compaction` boundary marker to the SessionStore. Returns
|
|
30
|
+
* the SessionEvent we wrote so the caller can echo it into the in-
|
|
31
|
+
* memory transcript without a re-read. Throws on store error so the
|
|
32
|
+
* caller surfaces the failure inline.
|
|
33
|
+
*/
|
|
34
|
+
export async function appendCompactBoundary(input) {
|
|
35
|
+
const ts = (input.now ?? (() => Date.now()))();
|
|
36
|
+
const payload = {
|
|
37
|
+
version: 1,
|
|
38
|
+
trigger: input.trigger,
|
|
39
|
+
summary: input.summary,
|
|
40
|
+
summaryTokenCount: input.summaryTokenCount,
|
|
41
|
+
summaryTurnsBefore: input.summaryTurnsBefore,
|
|
42
|
+
keptTailTurns: input.keptTailTurns,
|
|
43
|
+
coversUntilOffset: input.coversUntilOffset,
|
|
44
|
+
};
|
|
45
|
+
const event = {
|
|
46
|
+
t: ts,
|
|
47
|
+
kind: 'compaction',
|
|
48
|
+
payload,
|
|
49
|
+
};
|
|
50
|
+
await input.store.appendEvent(event);
|
|
51
|
+
return event;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Apply replay masking to a chronological event list. Given the full
|
|
55
|
+
* ordered events.jsonl content, return only the events the caller
|
|
56
|
+
* should render: every event AFTER the latest `compaction` boundary,
|
|
57
|
+
* plus the boundary itself (so the renderer can show the banner +
|
|
58
|
+
* summary), and the K kept-tail events that landed BEFORE the boundary
|
|
59
|
+
* but were preserved per the marker's `keptTailTurns`.
|
|
60
|
+
*
|
|
61
|
+
* Mask logic:
|
|
62
|
+
* 1. Walk events. Find the LATEST boundary by offset.
|
|
63
|
+
* 2. Index 0 .. coversUntilOffset-1 are masked, EXCEPT the last
|
|
64
|
+
* `keptTailTurns` of that range (which are the verbatim tail).
|
|
65
|
+
* 3. The boundary event itself + everything after it stays.
|
|
66
|
+
*
|
|
67
|
+
* Why we expose this here (and not in session.ts): keeping the mask
|
|
68
|
+
* logic next to the writer means the wire format is owned by one
|
|
69
|
+
* module. session.ts depends on this; this depends on nothing in
|
|
70
|
+
* session.ts. Unidirectional.
|
|
71
|
+
*/
|
|
72
|
+
export function applyCompactMask(events) {
|
|
73
|
+
// Find latest compaction event.
|
|
74
|
+
let latestIdx = -1;
|
|
75
|
+
let latestPayload = null;
|
|
76
|
+
for (let i = events.length - 1; i >= 0; i -= 1) {
|
|
77
|
+
const ev = events[i];
|
|
78
|
+
if (isCompactBoundary(ev)) {
|
|
79
|
+
latestIdx = i;
|
|
80
|
+
latestPayload = ev.payload;
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
if (latestIdx === -1 || latestPayload === null) {
|
|
85
|
+
return events;
|
|
86
|
+
}
|
|
87
|
+
// `coversUntilOffset` is the count of events that existed in the
|
|
88
|
+
// store immediately before the marker append. Events 0 ..
|
|
89
|
+
// coversUntilOffset-1 are summarised; events keptTailTurns of them
|
|
90
|
+
// are surfaced anyway as the verbatim tail.
|
|
91
|
+
const cap = Math.max(0, Math.min(latestPayload.coversUntilOffset, latestIdx));
|
|
92
|
+
const tailKeepCount = Math.max(0, Math.min(latestPayload.keptTailTurns, cap));
|
|
93
|
+
// Take the LAST tailKeepCount events from the masked range, but only
|
|
94
|
+
// those that represent renderable turns (user/persona/system).
|
|
95
|
+
// Boundary markers and tool stream events are NOT counted as turns
|
|
96
|
+
// for the tail-keep window — using them would let the keptTailTurns
|
|
97
|
+
// budget be consumed by infra events and the operator would lose
|
|
98
|
+
// the last K real turns. The spec is about "last K human-visible
|
|
99
|
+
// turns", not "last K events".
|
|
100
|
+
const tailSlice = [];
|
|
101
|
+
for (let i = cap - 1; i >= 0 && tailSlice.length < tailKeepCount; i -= 1) {
|
|
102
|
+
const ev = events[i];
|
|
103
|
+
if (ev.kind === 'user' || ev.kind === 'persona' || ev.kind === 'system') {
|
|
104
|
+
tailSlice.push(ev);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
tailSlice.reverse();
|
|
108
|
+
// After the marker: everything that landed AFTER the boundary
|
|
109
|
+
// append. These are post-compaction events the user has not yet
|
|
110
|
+
// seen folded into a summary; they pass through verbatim.
|
|
111
|
+
const afterMarker = events.slice(latestIdx + 1);
|
|
112
|
+
const markerEvent = events[latestIdx];
|
|
113
|
+
return [...tailSlice, markerEvent, ...afterMarker];
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=buffer-rewriter.js.map
|