@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,254 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `pugi init` codebase scan → PUGI.md auto-gen (backlog #82).
|
|
3
|
+
*
|
|
4
|
+
* Extends the existing `init()` scaffold with a project-aware PUGI.md
|
|
5
|
+
* generator. Runs AFTER `scaffoldPugiWorkspace` so the `.pugi/` workspace
|
|
6
|
+
* exists for the `init-report.json` write.
|
|
7
|
+
*
|
|
8
|
+
* Flow:
|
|
9
|
+
*
|
|
10
|
+
* 1. Detect existing PUGI.md. If present without `--force`, refuse
|
|
11
|
+
* with `refused_existing`.
|
|
12
|
+
* 2. Run `detectWorkspace` against cwd.
|
|
13
|
+
* 3. Render PUGI.md content for the requested style.
|
|
14
|
+
* 4. Confirm with the operator unless `--yes` / `--json` / `--force`.
|
|
15
|
+
* 5. Write `<cwd>/PUGI.md` atomically (tmp + rename, fallback к
|
|
16
|
+
* direct write on EXDEV — happens когда tmp lives на a different
|
|
17
|
+
* volume).
|
|
18
|
+
* 6. Write `.pugi/init-report.json` (mode 0o600) with the raw
|
|
19
|
+
* detection payload for re-runs / auditing.
|
|
20
|
+
* 7. Emit a JSON envelope describing the outcome.
|
|
21
|
+
*/
|
|
22
|
+
import { chmodSync, existsSync, mkdirSync, readFileSync, renameSync, unlinkSync, writeFileSync, } from 'node:fs';
|
|
23
|
+
import { tmpdir } from 'node:os';
|
|
24
|
+
import { dirname, join, resolve } from 'node:path';
|
|
25
|
+
import { createInterface } from 'node:readline';
|
|
26
|
+
import { detectWorkspace } from '../../core/init/detector.js';
|
|
27
|
+
import { renderPugiMd, STYLE_WORD_CAPS, } from '../../core/init/template-renderer.js';
|
|
28
|
+
export const DEFAULT_PUGI_MD_STYLE = 'standard';
|
|
29
|
+
/** Entry point invoked by `cli.ts::init`. */
|
|
30
|
+
export async function runInitScanCommand(args, ctx) {
|
|
31
|
+
const cwd = resolve(ctx.cwd);
|
|
32
|
+
const pugiMdPath = join(cwd, 'PUGI.md');
|
|
33
|
+
const reportPath = join(cwd, '.pugi', 'init-report.json');
|
|
34
|
+
// `args` is currently consumed only via flags. Keep the parameter to
|
|
35
|
+
// mirror every other command surface so we can grow positional args
|
|
36
|
+
// (e.g. an explicit target path) without breaking the call site.
|
|
37
|
+
void args;
|
|
38
|
+
try {
|
|
39
|
+
if (existsSync(pugiMdPath) && !ctx.force) {
|
|
40
|
+
const envelope = {
|
|
41
|
+
command: 'init-scan',
|
|
42
|
+
status: 'refused_existing',
|
|
43
|
+
pugiMdPath,
|
|
44
|
+
reportPath,
|
|
45
|
+
style: ctx.style,
|
|
46
|
+
message: 'PUGI.md already exists. Re-run with --force to overwrite, or edit by hand.',
|
|
47
|
+
};
|
|
48
|
+
ctx.writeOutput(envelope, formatHuman(envelope));
|
|
49
|
+
return envelope;
|
|
50
|
+
}
|
|
51
|
+
const detection = detectWorkspace(cwd);
|
|
52
|
+
const rendered = renderPugiMd(detection, { style: ctx.style, now: ctx.now });
|
|
53
|
+
const shouldConfirm = !ctx.yes && !ctx.json && !ctx.force;
|
|
54
|
+
if (shouldConfirm) {
|
|
55
|
+
const confirm = ctx.confirm ?? defaultConfirm;
|
|
56
|
+
const ok = await confirm(`Write PUGI.md at ${pugiMdPath} (${rendered.wordCount} words, style: ${ctx.style})? [y/N] `);
|
|
57
|
+
if (!ok) {
|
|
58
|
+
const envelope = {
|
|
59
|
+
command: 'init-scan',
|
|
60
|
+
status: 'aborted',
|
|
61
|
+
pugiMdPath,
|
|
62
|
+
reportPath,
|
|
63
|
+
style: ctx.style,
|
|
64
|
+
message: 'Aborted by operator before write.',
|
|
65
|
+
};
|
|
66
|
+
ctx.writeOutput(envelope, formatHuman(envelope));
|
|
67
|
+
return envelope;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
atomicWrite(pugiMdPath, rendered.content);
|
|
71
|
+
writeReport(reportPath, detection, rendered.wordCount, ctx.style);
|
|
72
|
+
const envelope = {
|
|
73
|
+
command: 'init-scan',
|
|
74
|
+
status: 'written',
|
|
75
|
+
pugiMdPath,
|
|
76
|
+
reportPath,
|
|
77
|
+
detection,
|
|
78
|
+
wordCount: rendered.wordCount,
|
|
79
|
+
style: ctx.style,
|
|
80
|
+
};
|
|
81
|
+
ctx.writeOutput(envelope, formatHuman(envelope));
|
|
82
|
+
return envelope;
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
86
|
+
const envelope = {
|
|
87
|
+
command: 'init-scan',
|
|
88
|
+
status: 'error',
|
|
89
|
+
pugiMdPath,
|
|
90
|
+
reportPath,
|
|
91
|
+
style: ctx.style,
|
|
92
|
+
message,
|
|
93
|
+
};
|
|
94
|
+
ctx.writeOutput(envelope, formatHuman(envelope));
|
|
95
|
+
return envelope;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
/* ------------------------------------------------------------------ */
|
|
99
|
+
/* Atomic write */
|
|
100
|
+
/* ------------------------------------------------------------------ */
|
|
101
|
+
function atomicWrite(targetPath, content) {
|
|
102
|
+
const dir = dirname(targetPath);
|
|
103
|
+
mkdirSync(dir, { recursive: true });
|
|
104
|
+
const tmpName = `.pugi-md-${process.pid}-${Date.now()}.tmp`;
|
|
105
|
+
const tmpPath = join(tmpdir(), tmpName);
|
|
106
|
+
try {
|
|
107
|
+
writeFileSync(tmpPath, content, { encoding: 'utf8', mode: 0o644 });
|
|
108
|
+
renameSync(tmpPath, targetPath);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
// EXDEV — cross-device link: tmpdir lives on a different volume
|
|
113
|
+
// than the workspace. Retry with the tmpfile inside the workspace.
|
|
114
|
+
cleanupTmp(tmpPath);
|
|
115
|
+
const code = error.code;
|
|
116
|
+
if (code !== 'EXDEV') {
|
|
117
|
+
// Fall through to the in-dir fallback for any rename failure so
|
|
118
|
+
// we keep the contract: PUGI.md is either fully written or not
|
|
119
|
+
// written. The direct writeFileSync at the end is the final
|
|
120
|
+
// safety net.
|
|
121
|
+
}
|
|
122
|
+
const localTmpPath = join(dir, `.pugi-md-${process.pid}-${Date.now()}.tmp`);
|
|
123
|
+
try {
|
|
124
|
+
writeFileSync(localTmpPath, content, { encoding: 'utf8', mode: 0o644 });
|
|
125
|
+
renameSync(localTmpPath, targetPath);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
cleanupTmp(localTmpPath);
|
|
130
|
+
}
|
|
131
|
+
// Final fallback — direct write. We sacrifice the atomicity guarantee
|
|
132
|
+
// here but keep behavioural correctness when both the tmp + in-dir
|
|
133
|
+
// strategies fail (rare; usually FS quirks).
|
|
134
|
+
writeFileSync(targetPath, content, { encoding: 'utf8', mode: 0o644 });
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
function cleanupTmp(path) {
|
|
138
|
+
try {
|
|
139
|
+
unlinkSync(path);
|
|
140
|
+
}
|
|
141
|
+
catch {
|
|
142
|
+
// best effort
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
function writeReport(reportPath, detection, wordCount, style) {
|
|
146
|
+
mkdirSync(dirname(reportPath), { recursive: true });
|
|
147
|
+
const payload = {
|
|
148
|
+
generatedAt: new Date().toISOString(),
|
|
149
|
+
style,
|
|
150
|
+
wordCap: STYLE_WORD_CAPS[style],
|
|
151
|
+
wordCount,
|
|
152
|
+
detection,
|
|
153
|
+
};
|
|
154
|
+
writeFileSync(reportPath, JSON.stringify(payload, null, 2), {
|
|
155
|
+
encoding: 'utf8',
|
|
156
|
+
mode: 0o600,
|
|
157
|
+
});
|
|
158
|
+
try {
|
|
159
|
+
chmodSync(reportPath, 0o600);
|
|
160
|
+
}
|
|
161
|
+
catch {
|
|
162
|
+
// Some filesystems (FAT, NTFS over CIFS) reject chmod; the mode
|
|
163
|
+
// option on writeFileSync is the primary guard. Best-effort here.
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
/* ------------------------------------------------------------------ */
|
|
167
|
+
/* Confirm prompt */
|
|
168
|
+
/* ------------------------------------------------------------------ */
|
|
169
|
+
async function defaultConfirm(question) {
|
|
170
|
+
if (process.stdin.isTTY !== true) {
|
|
171
|
+
return true;
|
|
172
|
+
}
|
|
173
|
+
const rl = createInterface({
|
|
174
|
+
input: process.stdin,
|
|
175
|
+
output: process.stderr,
|
|
176
|
+
});
|
|
177
|
+
return new Promise((resolveAnswer) => {
|
|
178
|
+
let settled = false;
|
|
179
|
+
const settle = (value) => {
|
|
180
|
+
if (settled)
|
|
181
|
+
return;
|
|
182
|
+
settled = true;
|
|
183
|
+
rl.close();
|
|
184
|
+
resolveAnswer(value);
|
|
185
|
+
};
|
|
186
|
+
rl.once('close', () => settle(false));
|
|
187
|
+
rl.question(question, (answer) => {
|
|
188
|
+
settle(/^y(es)?$/i.test(answer.trim()));
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
/* ------------------------------------------------------------------ */
|
|
193
|
+
/* Human-readable formatting */
|
|
194
|
+
/* ------------------------------------------------------------------ */
|
|
195
|
+
function formatHuman(env) {
|
|
196
|
+
switch (env.status) {
|
|
197
|
+
case 'written':
|
|
198
|
+
return [
|
|
199
|
+
`PUGI.md written to ${env.pugiMdPath}`,
|
|
200
|
+
` Style: ${env.style}`,
|
|
201
|
+
` Word count: ${env.wordCount ?? 0}`,
|
|
202
|
+
` Report: ${env.reportPath}`,
|
|
203
|
+
].join('\n');
|
|
204
|
+
case 'refused_existing':
|
|
205
|
+
return env.message ?? 'PUGI.md already exists. Use --force to overwrite.';
|
|
206
|
+
case 'aborted':
|
|
207
|
+
return env.message ?? 'Aborted.';
|
|
208
|
+
case 'error':
|
|
209
|
+
return `pugi init scan failed: ${env.message ?? 'unknown error'}`;
|
|
210
|
+
default:
|
|
211
|
+
return 'pugi init scan complete.';
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
/* ------------------------------------------------------------------ */
|
|
215
|
+
/* Helpers consumed by cli.ts */
|
|
216
|
+
/* ------------------------------------------------------------------ */
|
|
217
|
+
/** Parse `--style=<minimal|standard|detailed>` (or `--style minimal`). */
|
|
218
|
+
export function parseInitStyle(args) {
|
|
219
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
220
|
+
const arg = args[i];
|
|
221
|
+
if (typeof arg !== 'string')
|
|
222
|
+
continue;
|
|
223
|
+
if (arg.startsWith('--style=')) {
|
|
224
|
+
const value = arg.slice('--style='.length);
|
|
225
|
+
const parsed = coerceStyle(value);
|
|
226
|
+
if (parsed)
|
|
227
|
+
return parsed;
|
|
228
|
+
}
|
|
229
|
+
else if (arg === '--style') {
|
|
230
|
+
const next = args[i + 1];
|
|
231
|
+
if (typeof next === 'string') {
|
|
232
|
+
const parsed = coerceStyle(next);
|
|
233
|
+
if (parsed)
|
|
234
|
+
return parsed;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
return DEFAULT_PUGI_MD_STYLE;
|
|
239
|
+
}
|
|
240
|
+
function coerceStyle(value) {
|
|
241
|
+
if (value === 'minimal' || value === 'standard' || value === 'detailed')
|
|
242
|
+
return value;
|
|
243
|
+
return null;
|
|
244
|
+
}
|
|
245
|
+
/** Test surface: read back the on-disk report. */
|
|
246
|
+
export function readInitReport(reportPath) {
|
|
247
|
+
try {
|
|
248
|
+
return JSON.parse(readFileSync(reportPath, 'utf8'));
|
|
249
|
+
}
|
|
250
|
+
catch {
|
|
251
|
+
return null;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
//# sourceMappingURL=init.js.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `pugi lsp <op> <file> [args...]` —
|
|
2
|
+
* `pugi lsp <op> <file> [args...]` — Phase 1.
|
|
3
3
|
*
|
|
4
4
|
* Direct LSP queries from the CLI surface. Operators use this for
|
|
5
5
|
* debugging and scripting; the agent loop reaches the same operations
|
|
@@ -8,10 +8,10 @@
|
|
|
8
8
|
*
|
|
9
9
|
* Supported subcommands:
|
|
10
10
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
11
|
+
* pugi lsp hover <file> <line> <col> [--lang ts|js|py|go|rust]
|
|
12
|
+
* pugi lsp definition <file> <line> <col> [--lang ...]
|
|
13
|
+
* pugi lsp references <file> <line> <col> [--lang ...]
|
|
14
|
+
* pugi lsp diagnostics <file> [--lang ...]
|
|
15
15
|
*
|
|
16
16
|
* When `--lang` is omitted we infer from the file extension. An unknown
|
|
17
17
|
* extension surfaces `language_unsupported` so the operator can specify
|
|
@@ -20,21 +20,68 @@
|
|
|
20
20
|
* Lifecycle: we spawn an LSP server per invocation and stop it before
|
|
21
21
|
* returning. This is slow on cold start (TS server takes ~2-3s the
|
|
22
22
|
* first time) but the single-shot scripting path doesn't need a
|
|
23
|
-
* persistent daemon. Future work
|
|
23
|
+
* persistent daemon. Future work wires a per-REPL daemon.
|
|
24
24
|
*
|
|
25
25
|
* Brand voice: ASCII only, no emoji, no banned words.
|
|
26
26
|
*/
|
|
27
|
-
import {
|
|
28
|
-
import {
|
|
27
|
+
import { inspectLspServers, startLspClient } from '../../core/lsp/client.js';
|
|
28
|
+
import { languageForFile as inferLanguage } from '../../core/lsp/language-detect.js';
|
|
29
|
+
import { loadSettings } from '../../core/settings.js';
|
|
29
30
|
export async function runLspCommand(args, opts) {
|
|
30
31
|
const [op, file, ...rest] = args;
|
|
31
|
-
if (!op
|
|
32
|
+
if (!op) {
|
|
32
33
|
return usage();
|
|
33
34
|
}
|
|
35
|
+
// β7 L9: introspection subcommand. `pugi lsp servers` reports the
|
|
36
|
+
// language matrix — binary-on-PATH + enabled-via-settings — so the
|
|
37
|
+
// operator can debug `lsp_unavailable` vs `lsp_disabled` without
|
|
38
|
+
// re-running an actual hover.
|
|
39
|
+
if (op === 'servers' || op === 'status') {
|
|
40
|
+
const settings = loadSettings(opts.cwd);
|
|
41
|
+
const lspSettings = settings.lsp;
|
|
42
|
+
const rows = inspectLspServers(lspSettings);
|
|
43
|
+
if (opts.json) {
|
|
44
|
+
return { ok: true, text: JSON.stringify(rows, null, 2), exitCode: 0 };
|
|
45
|
+
}
|
|
46
|
+
const lines = ['language\tcommand\tavailable\tenabled'];
|
|
47
|
+
for (const r of rows) {
|
|
48
|
+
lines.push(`${r.language}\t${r.command}\t${r.available ? 'yes' : 'no'}\t${r.enabled ? 'yes' : 'no'}`);
|
|
49
|
+
}
|
|
50
|
+
return { ok: true, text: lines.join('\n'), exitCode: 0 };
|
|
51
|
+
}
|
|
52
|
+
if (!file) {
|
|
53
|
+
return usage();
|
|
54
|
+
}
|
|
55
|
+
// β7 L6: `find_definition` convenience subcommand. Identical wire to
|
|
56
|
+
// `definition` but takes `<symbol>` instead of <line> <col>. We grep
|
|
57
|
+
// the workspace for the first hit on `<symbol>` (whole-word match) to
|
|
58
|
+
// recover a position, then route through LSP. Falls back cleanly when
|
|
59
|
+
// the symbol is not present in the file.
|
|
60
|
+
if (op === 'find_definition' || op === 'find-definition') {
|
|
61
|
+
const { lang: explicitLangFD, positional: positionalFD } = pullLangFlag(rest);
|
|
62
|
+
const symbol = positionalFD[0];
|
|
63
|
+
if (!symbol) {
|
|
64
|
+
return {
|
|
65
|
+
ok: false,
|
|
66
|
+
text: 'Usage: pugi lsp find_definition <file> <symbol> [--lang ts|js|py|go|rust]',
|
|
67
|
+
exitCode: 2,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
const lang = explicitLangFD ?? inferLanguage(file);
|
|
71
|
+
if (!lang) {
|
|
72
|
+
return {
|
|
73
|
+
ok: false,
|
|
74
|
+
text: `cannot infer language from ${file}; pass --lang ts|js|py|go|rust. ` +
|
|
75
|
+
`Supported extensions: .ts/.tsx, .js/.jsx/.mjs, .py, .go, .rs`,
|
|
76
|
+
exitCode: 2,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
return await findDefinition(file, symbol, lang, opts);
|
|
80
|
+
}
|
|
34
81
|
if (!['hover', 'definition', 'references', 'diagnostics'].includes(op)) {
|
|
35
82
|
return {
|
|
36
83
|
ok: false,
|
|
37
|
-
text: `unknown lsp operation: ${op}. Supported: hover, definition, references, diagnostics`,
|
|
84
|
+
text: `unknown lsp operation: ${op}. Supported: hover, definition, references, diagnostics, find_definition, servers`,
|
|
38
85
|
exitCode: 2,
|
|
39
86
|
};
|
|
40
87
|
}
|
|
@@ -48,7 +95,30 @@ export async function runLspCommand(args, opts) {
|
|
|
48
95
|
exitCode: 2,
|
|
49
96
|
};
|
|
50
97
|
}
|
|
51
|
-
const
|
|
98
|
+
const settings = loadSettings(opts.cwd);
|
|
99
|
+
// `pugi lsp check <file>` — manual probe of
|
|
100
|
+
// the post-edit diagnostics pipeline. Identical output to what the
|
|
101
|
+
// model sees appended to its tool envelope after a successful
|
|
102
|
+
// edit/write. Lets operators dry-run the auto-diagnostic surface
|
|
103
|
+
// without dispatching an actual edit.
|
|
104
|
+
if (op === 'check') {
|
|
105
|
+
const { runPostEditDiagnostics } = await import('../../core/lsp/post-edit-diagnostics.js');
|
|
106
|
+
const result = await runPostEditDiagnostics(file, {
|
|
107
|
+
cwd: opts.cwd,
|
|
108
|
+
...(settings.lsp ? { lspSettings: settings.lsp } : {}),
|
|
109
|
+
});
|
|
110
|
+
if (opts.json) {
|
|
111
|
+
return { ok: true, text: JSON.stringify(result, null, 2), exitCode: 0 };
|
|
112
|
+
}
|
|
113
|
+
if (result.skip) {
|
|
114
|
+
return { ok: true, text: `${file}: skipped (${result.reason})`, exitCode: 0 };
|
|
115
|
+
}
|
|
116
|
+
return { ok: true, text: result.tail, exitCode: 0 };
|
|
117
|
+
}
|
|
118
|
+
const clientResult = await startLspClient(lang, {
|
|
119
|
+
cwd: opts.cwd,
|
|
120
|
+
...(settings.lsp ? { lspSettings: settings.lsp } : {}),
|
|
121
|
+
});
|
|
52
122
|
if (!clientResult.ok) {
|
|
53
123
|
return {
|
|
54
124
|
ok: false,
|
|
@@ -57,7 +127,7 @@ export async function runLspCommand(args, opts) {
|
|
|
57
127
|
};
|
|
58
128
|
}
|
|
59
129
|
const client = clientResult.value;
|
|
60
|
-
// R1 fix (2026-05-26, PR
|
|
130
|
+
// R1 fix (2026-05-26, PR r1, P2 #12): propagate SIGINT/SIGTERM
|
|
61
131
|
// to the LSP child process. Without this, ^C in the middle of a
|
|
62
132
|
// hung definition request would kill the CLI but leave the spawned
|
|
63
133
|
// language server orphaned (especially expensive for rust-analyzer
|
|
@@ -149,13 +219,123 @@ function usage() {
|
|
|
149
219
|
return {
|
|
150
220
|
ok: false,
|
|
151
221
|
text: 'Usage: pugi lsp <op> <file> [line] [col] [--lang ts|js|py|go|rust]\n' +
|
|
152
|
-
'
|
|
153
|
-
'
|
|
154
|
-
'
|
|
155
|
-
'
|
|
222
|
+
' pugi lsp hover <file> <line> <col>\n' +
|
|
223
|
+
' pugi lsp definition <file> <line> <col>\n' +
|
|
224
|
+
' pugi lsp references <file> <line> <col>\n' +
|
|
225
|
+
' pugi lsp diagnostics <file>\n' +
|
|
226
|
+
' pugi lsp check <file> (probe post-edit tail)\n' +
|
|
227
|
+
' pugi lsp find_definition <file> <symbol>\n' +
|
|
228
|
+
' pugi lsp servers',
|
|
156
229
|
exitCode: 2,
|
|
157
230
|
};
|
|
158
231
|
}
|
|
232
|
+
/**
|
|
233
|
+
* β7 L6: find_definition convenience.
|
|
234
|
+
*
|
|
235
|
+
* Reads the file, walks line-by-line looking for the first whole-word
|
|
236
|
+
* match of `<symbol>`, then runs `lsp definition` at that position.
|
|
237
|
+
* The result is a normal location array — empty when the LSP server
|
|
238
|
+
* can't resolve the symbol (typically because the workspace TS server
|
|
239
|
+
* hasn't indexed the import yet).
|
|
240
|
+
*
|
|
241
|
+
* "Whole-word match" uses the same identifier-boundary rules as Layer
|
|
242
|
+
* D's tokenizer: matches inside string literals / comments / partial
|
|
243
|
+
* identifiers are skipped. This keeps the result aligned with what the
|
|
244
|
+
* operator means by "where is foo defined" — not "any character sequence
|
|
245
|
+
* spelling foo".
|
|
246
|
+
*/
|
|
247
|
+
async function findDefinition(file, symbol, lang, opts) {
|
|
248
|
+
const { readFileSync, existsSync } = await import('node:fs');
|
|
249
|
+
const { resolve } = await import('node:path');
|
|
250
|
+
const abs = resolve(opts.cwd, file);
|
|
251
|
+
if (!existsSync(abs)) {
|
|
252
|
+
return { ok: false, text: `file not found: ${file}`, exitCode: 1 };
|
|
253
|
+
}
|
|
254
|
+
let body;
|
|
255
|
+
try {
|
|
256
|
+
body = readFileSync(abs, 'utf8');
|
|
257
|
+
}
|
|
258
|
+
catch (error) {
|
|
259
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
260
|
+
return { ok: false, text: `cannot read ${file}: ${detail}`, exitCode: 1 };
|
|
261
|
+
}
|
|
262
|
+
const position = locateIdentifier(body, symbol);
|
|
263
|
+
if (!position) {
|
|
264
|
+
return {
|
|
265
|
+
ok: false,
|
|
266
|
+
text: `symbol '${symbol}' not found in ${file}`,
|
|
267
|
+
exitCode: 1,
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
const settings = loadSettings(opts.cwd);
|
|
271
|
+
const clientResult = await startLspClient(lang, {
|
|
272
|
+
cwd: opts.cwd,
|
|
273
|
+
...(settings.lsp ? { lspSettings: settings.lsp } : {}),
|
|
274
|
+
});
|
|
275
|
+
if (!clientResult.ok) {
|
|
276
|
+
return { ok: false, text: `${clientResult.reason}: ${clientResult.detail}`, exitCode: 1 };
|
|
277
|
+
}
|
|
278
|
+
const client = clientResult.value;
|
|
279
|
+
try {
|
|
280
|
+
const result = await client.definition(file, position);
|
|
281
|
+
if (!result.ok) {
|
|
282
|
+
return { ok: false, text: `${result.reason}: ${result.detail}`, exitCode: 1 };
|
|
283
|
+
}
|
|
284
|
+
if (opts.json) {
|
|
285
|
+
return {
|
|
286
|
+
ok: true,
|
|
287
|
+
text: JSON.stringify({
|
|
288
|
+
symbol,
|
|
289
|
+
file,
|
|
290
|
+
sourcePosition: { line: position.line + 1, character: position.character + 1 },
|
|
291
|
+
definitions: result.value,
|
|
292
|
+
}, null, 2),
|
|
293
|
+
exitCode: 0,
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
if (result.value.length === 0) {
|
|
297
|
+
return { ok: true, text: `no definition for ${symbol}`, exitCode: 0 };
|
|
298
|
+
}
|
|
299
|
+
const lines = result.value.map((loc) => `${loc.path || loc.uri}:${loc.range.start.line + 1}:${loc.range.start.character + 1}`);
|
|
300
|
+
return { ok: true, text: lines.join('\n'), exitCode: 0 };
|
|
301
|
+
}
|
|
302
|
+
finally {
|
|
303
|
+
await client.stop();
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* Find the first whole-word occurrence of `symbol` in `body`. Returns
|
|
308
|
+
* the 0-based LSP position (line + character) or null when absent.
|
|
309
|
+
* Uses the same identifier boundary rule as Layer D's tokenizer to
|
|
310
|
+
* avoid matching inside larger identifiers.
|
|
311
|
+
*/
|
|
312
|
+
export function locateIdentifier(body, symbol) {
|
|
313
|
+
const lines = body.split('\n');
|
|
314
|
+
// Build the boundary regex once. We can't use `\b` directly because
|
|
315
|
+
// it treats `$` as a word boundary and TS/JS allow `$` in identifiers;
|
|
316
|
+
// implement the boundary check with a manual lookaround.
|
|
317
|
+
const isIdent = (ch) => {
|
|
318
|
+
if (!ch)
|
|
319
|
+
return false;
|
|
320
|
+
return /[A-Za-z0-9_$]/.test(ch);
|
|
321
|
+
};
|
|
322
|
+
for (let line = 0; line < lines.length; line += 1) {
|
|
323
|
+
const text = lines[line];
|
|
324
|
+
let from = 0;
|
|
325
|
+
while (from < text.length) {
|
|
326
|
+
const idx = text.indexOf(symbol, from);
|
|
327
|
+
if (idx === -1)
|
|
328
|
+
break;
|
|
329
|
+
const before = idx > 0 ? text[idx - 1] : undefined;
|
|
330
|
+
const after = idx + symbol.length < text.length ? text[idx + symbol.length] : undefined;
|
|
331
|
+
if (!isIdent(before) && !isIdent(after)) {
|
|
332
|
+
return { line, character: idx };
|
|
333
|
+
}
|
|
334
|
+
from = idx + symbol.length;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
return null;
|
|
338
|
+
}
|
|
159
339
|
function pullLangFlag(args) {
|
|
160
340
|
let lang;
|
|
161
341
|
const positional = [];
|
|
@@ -181,26 +361,8 @@ function pullLangFlag(args) {
|
|
|
181
361
|
function isLspLanguage(value) {
|
|
182
362
|
return value === 'ts' || value === 'js' || value === 'py' || value === 'go' || value === 'rust';
|
|
183
363
|
}
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
case '.tsx':
|
|
189
|
-
return 'ts';
|
|
190
|
-
case '.js':
|
|
191
|
-
case '.jsx':
|
|
192
|
-
case '.mjs':
|
|
193
|
-
case '.cjs':
|
|
194
|
-
return 'js';
|
|
195
|
-
case '.py':
|
|
196
|
-
case '.pyi':
|
|
197
|
-
return 'py';
|
|
198
|
-
case '.go':
|
|
199
|
-
return 'go';
|
|
200
|
-
case '.rs':
|
|
201
|
-
return 'rust';
|
|
202
|
-
default:
|
|
203
|
-
return undefined;
|
|
204
|
-
}
|
|
205
|
-
}
|
|
364
|
+
// single source of truth for ext → language
|
|
365
|
+
// lives in `core/lsp/language-detect.ts`. The CLI surface re-exports
|
|
366
|
+
// the lookup so existing call sites keep their import path.
|
|
367
|
+
export { inferLanguage };
|
|
206
368
|
//# sourceMappingURL=lsp.js.map
|