pi-usereq 0.7.0 → 0.11.0
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/.gitignore +2 -0
- package/CHANGELOG.md +24 -0
- package/README.md +1 -1
- package/docs/pi.dev/coding-agent-docs/custom-provider.md +42 -1
- package/docs/pi.dev/coding-agent-docs/extensions.md +302 -28
- package/docs/pi.dev/coding-agent-docs/keybindings.md +23 -1
- package/docs/pi.dev/coding-agent-docs/models.md +3 -0
- package/docs/pi.dev/coding-agent-docs/packages.md +2 -2
- package/docs/pi.dev/coding-agent-docs/prompt-templates.md +21 -0
- package/docs/pi.dev/coding-agent-docs/providers.md +1 -0
- package/docs/pi.dev/coding-agent-docs/rpc.md +36 -6
- package/docs/pi.dev/coding-agent-docs/sdk.md +30 -5
- package/docs/pi.dev/coding-agent-docs/session.md +1 -1
- package/docs/pi.dev/coding-agent-docs/settings.md +2 -1
- package/docs/pi.dev/coding-agent-docs/skills.md +12 -12
- package/docs/pi.dev/coding-agent-docs/tree.md +2 -0
- package/docs/pi.dev/coding-agent-docs/tui.md +31 -0
- package/docs/pi.dev/mom-docs/new.md +4 -4
- package/package.json +1 -1
- package/pi-usereq/docs/REFERENCES.md +5214 -24470
- package/pi-usereq/docs/REQUIREMENTS.md +307 -170
- package/pi-usereq/docs/WORKFLOW.md +334 -316
- package/pi.dev-src/pi-mono/agent/src/agent-loop.ts +683 -0
- package/pi.dev-src/pi-mono/agent/src/agent.ts +543 -0
- package/pi.dev-src/pi-mono/agent/src/index.ts +8 -0
- package/pi.dev-src/pi-mono/agent/src/proxy.ts +367 -0
- package/pi.dev-src/pi-mono/agent/src/types.ts +364 -0
- package/pi.dev-src/pi-mono/agent/test/agent-loop.test.ts +1181 -0
- package/pi.dev-src/pi-mono/agent/test/agent.test.ts +463 -0
- package/pi.dev-src/pi-mono/agent/test/e2e.test.ts +404 -0
- package/pi.dev-src/pi-mono/agent/test/utils/calculate.ts +32 -0
- package/pi.dev-src/pi-mono/agent/test/utils/get-current-time.ts +46 -0
- package/pi.dev-src/pi-mono/agent/vitest.config.ts +9 -0
- package/pi.dev-src/pi-mono/ai/bedrock-provider.d.ts +1 -0
- package/pi.dev-src/pi-mono/ai/bedrock-provider.js +1 -0
- package/pi.dev-src/pi-mono/ai/scripts/generate-models.ts +1635 -0
- package/pi.dev-src/pi-mono/ai/scripts/generate-test-image.ts +34 -0
- package/pi.dev-src/pi-mono/ai/src/api-registry.ts +98 -0
- package/pi.dev-src/pi-mono/ai/src/bedrock-provider.ts +6 -0
- package/pi.dev-src/pi-mono/ai/src/cli.ts +133 -0
- package/pi.dev-src/pi-mono/ai/src/env-api-keys.ts +134 -0
- package/pi.dev-src/pi-mono/ai/src/index.ts +35 -0
- package/pi.dev-src/pi-mono/ai/src/models.generated.ts +14928 -0
- package/pi.dev-src/pi-mono/ai/src/models.ts +82 -0
- package/pi.dev-src/pi-mono/ai/src/oauth.ts +1 -0
- package/pi.dev-src/pi-mono/ai/src/providers/amazon-bedrock.ts +929 -0
- package/pi.dev-src/pi-mono/ai/src/providers/anthropic.ts +1125 -0
- package/pi.dev-src/pi-mono/ai/src/providers/azure-openai-responses.ts +253 -0
- package/pi.dev-src/pi-mono/ai/src/providers/faux.ts +499 -0
- package/pi.dev-src/pi-mono/ai/src/providers/github-copilot-headers.ts +37 -0
- package/pi.dev-src/pi-mono/ai/src/providers/google-gemini-cli.ts +996 -0
- package/pi.dev-src/pi-mono/ai/src/providers/google-shared.ts +354 -0
- package/pi.dev-src/pi-mono/ai/src/providers/google-vertex.ts +543 -0
- package/pi.dev-src/pi-mono/ai/src/providers/google.ts +500 -0
- package/pi.dev-src/pi-mono/ai/src/providers/mistral.ts +629 -0
- package/pi.dev-src/pi-mono/ai/src/providers/openai-codex-responses.ts +984 -0
- package/pi.dev-src/pi-mono/ai/src/providers/openai-completions.ts +1063 -0
- package/pi.dev-src/pi-mono/ai/src/providers/openai-responses-shared.ts +539 -0
- package/pi.dev-src/pi-mono/ai/src/providers/openai-responses.ts +264 -0
- package/pi.dev-src/pi-mono/ai/src/providers/register-builtins.ts +433 -0
- package/pi.dev-src/pi-mono/ai/src/providers/simple-options.ts +47 -0
- package/pi.dev-src/pi-mono/ai/src/providers/transform-messages.ts +220 -0
- package/pi.dev-src/pi-mono/ai/src/stream.ts +59 -0
- package/pi.dev-src/pi-mono/ai/src/types.ts +417 -0
- package/pi.dev-src/pi-mono/ai/src/utils/event-stream.ts +87 -0
- package/pi.dev-src/pi-mono/ai/src/utils/hash.ts +13 -0
- package/pi.dev-src/pi-mono/ai/src/utils/headers.ts +7 -0
- package/pi.dev-src/pi-mono/ai/src/utils/json-parse.ts +124 -0
- package/pi.dev-src/pi-mono/ai/src/utils/overflow.ts +138 -0
- package/pi.dev-src/pi-mono/ai/src/utils/sanitize-unicode.ts +25 -0
- package/pi.dev-src/pi-mono/ai/src/utils/typebox-helpers.ts +24 -0
- package/pi.dev-src/pi-mono/ai/src/utils/validation.ts +324 -0
- package/pi.dev-src/pi-mono/ai/test/abort.test.ts +260 -0
- package/pi.dev-src/pi-mono/ai/test/anthropic-oauth.test.ts +99 -0
- package/pi.dev-src/pi-mono/ai/test/anthropic-opus-4-7-smoke.test.ts +72 -0
- package/pi.dev-src/pi-mono/ai/test/anthropic-sse-parsing.test.ts +113 -0
- package/pi.dev-src/pi-mono/ai/test/anthropic-thinking-disable.test.ts +152 -0
- package/pi.dev-src/pi-mono/ai/test/anthropic-tool-name-normalization.test.ts +205 -0
- package/pi.dev-src/pi-mono/ai/test/azure-utils.ts +28 -0
- package/pi.dev-src/pi-mono/ai/test/bedrock-endpoint-resolution.test.ts +131 -0
- package/pi.dev-src/pi-mono/ai/test/bedrock-models.test.ts +66 -0
- package/pi.dev-src/pi-mono/ai/test/bedrock-thinking-payload.test.ts +107 -0
- package/pi.dev-src/pi-mono/ai/test/bedrock-utils.ts +18 -0
- package/pi.dev-src/pi-mono/ai/test/cache-retention.test.ts +335 -0
- package/pi.dev-src/pi-mono/ai/test/context-overflow.test.ts +714 -0
- package/pi.dev-src/pi-mono/ai/test/cross-provider-handoff.test.ts +458 -0
- package/pi.dev-src/pi-mono/ai/test/data/red-circle.png +0 -0
- package/pi.dev-src/pi-mono/ai/test/empty.test.ts +742 -0
- package/pi.dev-src/pi-mono/ai/test/faux-provider.test.ts +597 -0
- package/pi.dev-src/pi-mono/ai/test/fireworks-models.test.ts +49 -0
- package/pi.dev-src/pi-mono/ai/test/github-copilot-anthropic.test.ts +108 -0
- package/pi.dev-src/pi-mono/ai/test/github-copilot-oauth.test.ts +196 -0
- package/pi.dev-src/pi-mono/ai/test/google-gemini-cli-claude-thinking-header.test.ts +103 -0
- package/pi.dev-src/pi-mono/ai/test/google-gemini-cli-empty-stream.test.ts +108 -0
- package/pi.dev-src/pi-mono/ai/test/google-gemini-cli-retry-delay.test.ts +53 -0
- package/pi.dev-src/pi-mono/ai/test/google-shared-convert-tools.test.ts +187 -0
- package/pi.dev-src/pi-mono/ai/test/google-shared-gemini3-unsigned-tool-call.test.ts +172 -0
- package/pi.dev-src/pi-mono/ai/test/google-shared-image-tool-result-routing.test.ts +124 -0
- package/pi.dev-src/pi-mono/ai/test/google-thinking-disable.test.ts +183 -0
- package/pi.dev-src/pi-mono/ai/test/google-thinking-signature.test.ts +38 -0
- package/pi.dev-src/pi-mono/ai/test/google-tool-call-missing-args.test.ts +105 -0
- package/pi.dev-src/pi-mono/ai/test/google-vertex-api-key-resolution.test.ts +144 -0
- package/pi.dev-src/pi-mono/ai/test/image-tool-result.test.ts +481 -0
- package/pi.dev-src/pi-mono/ai/test/interleaved-thinking.test.ts +145 -0
- package/pi.dev-src/pi-mono/ai/test/lazy-module-load.test.ts +102 -0
- package/pi.dev-src/pi-mono/ai/test/mistral-reasoning-mode.test.ts +66 -0
- package/pi.dev-src/pi-mono/ai/test/mistral-tool-schema.test.ts +61 -0
- package/pi.dev-src/pi-mono/ai/test/oauth.ts +89 -0
- package/pi.dev-src/pi-mono/ai/test/openai-codex-cache-affinity-e2e.test.ts +36 -0
- package/pi.dev-src/pi-mono/ai/test/openai-codex-stream.test.ts +686 -0
- package/pi.dev-src/pi-mono/ai/test/openai-completions-cache-control-format.test.ts +189 -0
- package/pi.dev-src/pi-mono/ai/test/openai-completions-prompt-cache.test.ts +198 -0
- package/pi.dev-src/pi-mono/ai/test/openai-completions-thinking-as-text.test.ts +214 -0
- package/pi.dev-src/pi-mono/ai/test/openai-completions-tool-choice.test.ts +560 -0
- package/pi.dev-src/pi-mono/ai/test/openai-completions-tool-result-images.test.ts +101 -0
- package/pi.dev-src/pi-mono/ai/test/openai-responses-cache-affinity-e2e.test.ts +32 -0
- package/pi.dev-src/pi-mono/ai/test/openai-responses-copilot-provider.test.ts +128 -0
- package/pi.dev-src/pi-mono/ai/test/openai-responses-foreign-toolcall-id.test.ts +66 -0
- package/pi.dev-src/pi-mono/ai/test/openai-responses-partial-json-cleanup.test.ts +101 -0
- package/pi.dev-src/pi-mono/ai/test/openai-responses-reasoning-replay-e2e.test.ts +292 -0
- package/pi.dev-src/pi-mono/ai/test/openai-responses-tool-result-images.test.ts +200 -0
- package/pi.dev-src/pi-mono/ai/test/openrouter-cache-write-repro.test.ts +78 -0
- package/pi.dev-src/pi-mono/ai/test/overflow.test.ts +64 -0
- package/pi.dev-src/pi-mono/ai/test/responseid.test.ts +145 -0
- package/pi.dev-src/pi-mono/ai/test/stream.test.ts +1405 -0
- package/pi.dev-src/pi-mono/ai/test/supports-xhigh.test.ts +34 -0
- package/pi.dev-src/pi-mono/ai/test/tokens.test.ts +304 -0
- package/pi.dev-src/pi-mono/ai/test/tool-call-id-normalization.test.ts +291 -0
- package/pi.dev-src/pi-mono/ai/test/tool-call-without-result.test.ts +313 -0
- package/pi.dev-src/pi-mono/ai/test/total-tokens.test.ts +695 -0
- package/pi.dev-src/pi-mono/ai/test/transform-messages-copilot-openai-to-anthropic.test.ts +191 -0
- package/pi.dev-src/pi-mono/ai/test/unicode-surrogate.test.ts +754 -0
- package/pi.dev-src/pi-mono/ai/test/validation.test.ts +116 -0
- package/pi.dev-src/pi-mono/ai/test/xhigh.test.ts +71 -0
- package/pi.dev-src/pi-mono/ai/test/zen.test.ts +25 -0
- package/pi.dev-src/pi-mono/ai/vitest.config.ts +9 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/antigravity-image-gen.ts +418 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/auto-commit-on-exit.ts +49 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/bash-spawn-hook.ts +30 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/bookmark.ts +50 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/built-in-tool-renderer.ts +249 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/claude-rules.ts +86 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/commands.ts +72 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/confirm-destructive.ts +59 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/custom-compaction.ts +127 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/custom-footer.ts +64 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/custom-header.ts +73 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/custom-provider-anthropic/index.ts +604 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/custom-provider-anthropic/package-lock.json +24 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/custom-provider-gitlab-duo/index.ts +349 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/custom-provider-gitlab-duo/test.ts +82 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/custom-provider-qwen-cli/index.ts +345 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/dirty-repo-guard.ts +56 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/doom-overlay/doom/build.sh +152 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/doom-overlay/doom/doomgeneric_pi.c +72 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/doom-overlay/doom-component.ts +132 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/doom-overlay/doom-engine.ts +173 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/doom-overlay/doom-keys.ts +104 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/doom-overlay/index.ts +74 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/doom-overlay/wad-finder.ts +51 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/dynamic-resources/SKILL.md +8 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/dynamic-resources/dynamic.json +79 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/dynamic-resources/dynamic.md +5 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/dynamic-resources/index.ts +15 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/dynamic-tools.ts +74 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/event-bus.ts +43 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/file-trigger.ts +41 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/git-checkpoint.ts +53 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/github-issue-autocomplete.ts +185 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/handoff.ts +153 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/hello.ts +26 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/hidden-thinking-label.ts +53 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/inline-bash.ts +94 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/input-transform.ts +43 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/interactive-shell.ts +196 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/mac-system-theme.ts +47 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/message-renderer.ts +59 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/minimal-mode.ts +426 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/modal-editor.ts +85 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/model-status.ts +31 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/notify.ts +55 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/overlay-qa-tests.ts +1348 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/overlay-test.ts +150 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/permission-gate.ts +34 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/pirate.ts +47 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/plan-mode/index.ts +340 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/plan-mode/utils.ts +168 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/preset.ts +430 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/prompt-customizer.ts +97 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/protected-paths.ts +30 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/provider-payload.ts +18 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/qna.ts +122 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/question.ts +264 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/questionnaire.ts +427 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/rainbow-editor.ts +88 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/reload-runtime.ts +37 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/rpc-demo.ts +118 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/sandbox/index.ts +321 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/sandbox/package-lock.json +92 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/send-user-message.ts +97 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/session-name.ts +27 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/shutdown-command.ts +63 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/snake.ts +343 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/space-invaders.ts +560 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/ssh.ts +220 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/status-line.ts +32 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/structured-output.ts +65 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/subagent/agents/planner.md +37 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/subagent/agents/reviewer.md +35 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/subagent/agents/scout.md +50 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/subagent/agents/worker.md +24 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/subagent/agents.ts +126 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/subagent/index.ts +987 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/subagent/prompts/implement-and-review.md +10 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/subagent/prompts/implement.md +10 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/subagent/prompts/scout-and-plan.md +9 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/summarize.ts +206 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/system-prompt-header.ts +17 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/tic-tac-toe.ts +1008 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/timed-confirm.ts +70 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/titlebar-spinner.ts +58 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/todo.ts +297 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/tool-override.ts +144 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/tools.ts +141 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/trigger-compact.ts +50 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/truncated-tool.ts +195 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/widget-placement.ts +9 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/with-deps/index.ts +32 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/with-deps/package-lock.json +31 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/working-indicator.ts +123 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/extensions/working-message-test.ts +25 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/rpc-extension-ui.ts +632 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/sdk/01-minimal.ts +22 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/sdk/02-custom-model.ts +49 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/sdk/03-custom-prompt.ts +62 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/sdk/04-skills.ts +55 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/sdk/05-tools.ts +44 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/sdk/06-extensions.ts +90 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/sdk/07-context-files.ts +42 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/sdk/08-prompt-templates.ts +51 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/sdk/09-api-keys-and-oauth.ts +48 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/sdk/10-settings.ts +53 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/sdk/11-sessions.ts +48 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/sdk/12-full-control.ts +73 -0
- package/pi.dev-src/pi-mono/coding-agent/examples/sdk/13-session-runtime.ts +67 -0
- package/pi.dev-src/pi-mono/coding-agent/scripts/migrate-sessions.sh +93 -0
- package/pi.dev-src/pi-mono/coding-agent/src/bun/cli.ts +6 -0
- package/pi.dev-src/pi-mono/coding-agent/src/bun/register-bedrock.ts +4 -0
- package/pi.dev-src/pi-mono/coding-agent/src/cli/args.ts +335 -0
- package/pi.dev-src/pi-mono/coding-agent/src/cli/config-selector.ts +52 -0
- package/pi.dev-src/pi-mono/coding-agent/src/cli/file-processor.ts +100 -0
- package/pi.dev-src/pi-mono/coding-agent/src/cli/initial-message.ts +43 -0
- package/pi.dev-src/pi-mono/coding-agent/src/cli/list-models.ts +110 -0
- package/pi.dev-src/pi-mono/coding-agent/src/cli/session-picker.ts +52 -0
- package/pi.dev-src/pi-mono/coding-agent/src/cli.ts +17 -0
- package/pi.dev-src/pi-mono/coding-agent/src/config.ts +261 -0
- package/pi.dev-src/pi-mono/coding-agent/src/core/agent-session-runtime.ts +394 -0
- package/pi.dev-src/pi-mono/coding-agent/src/core/agent-session-services.ts +196 -0
- package/pi.dev-src/pi-mono/coding-agent/src/core/agent-session.ts +3096 -0
- package/pi.dev-src/pi-mono/coding-agent/src/core/auth-storage.ts +493 -0
- package/pi.dev-src/pi-mono/coding-agent/src/core/bash-executor.ts +154 -0
- package/pi.dev-src/pi-mono/coding-agent/src/core/compaction/branch-summarization.ts +355 -0
- package/pi.dev-src/pi-mono/coding-agent/src/core/compaction/compaction.ts +839 -0
- package/pi.dev-src/pi-mono/coding-agent/src/core/compaction/index.ts +7 -0
- package/pi.dev-src/pi-mono/coding-agent/src/core/compaction/utils.ts +170 -0
- package/pi.dev-src/pi-mono/coding-agent/src/core/defaults.ts +3 -0
- package/pi.dev-src/pi-mono/coding-agent/src/core/diagnostics.ts +15 -0
- package/pi.dev-src/pi-mono/coding-agent/src/core/event-bus.ts +33 -0
- package/pi.dev-src/pi-mono/coding-agent/src/core/exec.ts +107 -0
- package/pi.dev-src/pi-mono/coding-agent/src/core/export-html/ansi-to-html.ts +258 -0
- package/pi.dev-src/pi-mono/coding-agent/src/core/export-html/index.ts +314 -0
- package/pi.dev-src/pi-mono/coding-agent/src/core/export-html/template.css +1017 -0
- package/pi.dev-src/pi-mono/coding-agent/src/core/export-html/template.html +55 -0
- package/pi.dev-src/pi-mono/coding-agent/src/core/export-html/template.js +1770 -0
- package/pi.dev-src/pi-mono/coding-agent/src/core/export-html/tool-renderer.ts +158 -0
- package/pi.dev-src/pi-mono/coding-agent/src/core/export-html/vendor/highlight.min.js +1213 -0
- package/pi.dev-src/pi-mono/coding-agent/src/core/export-html/vendor/marked.min.js +6 -0
- package/pi.dev-src/pi-mono/coding-agent/src/core/extensions/index.ts +171 -0
- package/pi.dev-src/pi-mono/coding-agent/src/core/extensions/loader.ts +606 -0
- package/pi.dev-src/pi-mono/coding-agent/src/core/extensions/runner.ts +1019 -0
- package/pi.dev-src/pi-mono/coding-agent/src/core/extensions/types.ts +1542 -0
- package/pi.dev-src/pi-mono/coding-agent/src/core/extensions/wrapper.ts +30 -0
- package/pi.dev-src/pi-mono/coding-agent/src/core/footer-data-provider.ts +354 -0
- package/pi.dev-src/pi-mono/coding-agent/src/core/index.ts +77 -0
- package/pi.dev-src/pi-mono/coding-agent/src/core/keybindings.ts +370 -0
- package/pi.dev-src/pi-mono/coding-agent/src/core/messages.ts +195 -0
- package/pi.dev-src/pi-mono/coding-agent/src/core/model-registry.ts +858 -0
- package/pi.dev-src/pi-mono/coding-agent/src/core/model-resolver.ts +629 -0
- package/pi.dev-src/pi-mono/coding-agent/src/core/output-guard.ts +74 -0
- package/pi.dev-src/pi-mono/coding-agent/src/core/package-manager.ts +2408 -0
- package/pi.dev-src/pi-mono/coding-agent/src/core/prompt-templates.ts +296 -0
- package/pi.dev-src/pi-mono/coding-agent/src/core/resolve-config-value.ts +142 -0
- package/pi.dev-src/pi-mono/coding-agent/src/core/resource-loader.ts +917 -0
- package/pi.dev-src/pi-mono/coding-agent/src/core/sdk.ts +380 -0
- package/pi.dev-src/pi-mono/coding-agent/src/core/session-cwd.ts +59 -0
- package/pi.dev-src/pi-mono/coding-agent/src/core/session-manager.ts +1425 -0
- package/pi.dev-src/pi-mono/coding-agent/src/core/settings-manager.ts +999 -0
- package/pi.dev-src/pi-mono/coding-agent/src/core/skills.ts +508 -0
- package/pi.dev-src/pi-mono/coding-agent/src/core/slash-commands.ts +39 -0
- package/pi.dev-src/pi-mono/coding-agent/src/core/source-info.ts +40 -0
- package/pi.dev-src/pi-mono/coding-agent/src/core/system-prompt.ts +172 -0
- package/pi.dev-src/pi-mono/coding-agent/src/core/telemetry.ts +13 -0
- package/pi.dev-src/pi-mono/coding-agent/src/core/timings.ts +31 -0
- package/pi.dev-src/pi-mono/coding-agent/src/core/tools/bash.ts +448 -0
- package/pi.dev-src/pi-mono/coding-agent/src/core/tools/edit-diff.ts +445 -0
- package/pi.dev-src/pi-mono/coding-agent/src/core/tools/edit.ts +487 -0
- package/pi.dev-src/pi-mono/coding-agent/src/core/tools/file-mutation-queue.ts +39 -0
- package/pi.dev-src/pi-mono/coding-agent/src/core/tools/find.ts +370 -0
- package/pi.dev-src/pi-mono/coding-agent/src/core/tools/grep.ts +384 -0
- package/pi.dev-src/pi-mono/coding-agent/src/core/tools/index.ts +196 -0
- package/pi.dev-src/pi-mono/coding-agent/src/core/tools/ls.ts +229 -0
- package/pi.dev-src/pi-mono/coding-agent/src/core/tools/path-utils.ts +94 -0
- package/pi.dev-src/pi-mono/coding-agent/src/core/tools/read.ts +273 -0
- package/pi.dev-src/pi-mono/coding-agent/src/core/tools/render-utils.ts +64 -0
- package/pi.dev-src/pi-mono/coding-agent/src/core/tools/tool-definition-wrapper.ts +45 -0
- package/pi.dev-src/pi-mono/coding-agent/src/core/tools/truncate.ts +265 -0
- package/pi.dev-src/pi-mono/coding-agent/src/core/tools/write.ts +281 -0
- package/pi.dev-src/pi-mono/coding-agent/src/index.ts +352 -0
- package/pi.dev-src/pi-mono/coding-agent/src/main.ts +731 -0
- package/pi.dev-src/pi-mono/coding-agent/src/migrations.ts +314 -0
- package/pi.dev-src/pi-mono/coding-agent/src/modes/index.ts +9 -0
- package/pi.dev-src/pi-mono/coding-agent/src/modes/interactive/assets/clankolas.png +0 -0
- package/pi.dev-src/pi-mono/coding-agent/src/modes/interactive/components/armin.ts +382 -0
- package/pi.dev-src/pi-mono/coding-agent/src/modes/interactive/components/assistant-message.ts +147 -0
- package/pi.dev-src/pi-mono/coding-agent/src/modes/interactive/components/bash-execution.ts +218 -0
- package/pi.dev-src/pi-mono/coding-agent/src/modes/interactive/components/bordered-loader.ts +68 -0
- package/pi.dev-src/pi-mono/coding-agent/src/modes/interactive/components/branch-summary-message.ts +58 -0
- package/pi.dev-src/pi-mono/coding-agent/src/modes/interactive/components/compaction-summary-message.ts +59 -0
- package/pi.dev-src/pi-mono/coding-agent/src/modes/interactive/components/config-selector.ts +592 -0
- package/pi.dev-src/pi-mono/coding-agent/src/modes/interactive/components/countdown-timer.ts +38 -0
- package/pi.dev-src/pi-mono/coding-agent/src/modes/interactive/components/custom-editor.ts +80 -0
- package/pi.dev-src/pi-mono/coding-agent/src/modes/interactive/components/custom-message.ts +99 -0
- package/pi.dev-src/pi-mono/coding-agent/src/modes/interactive/components/daxnuts.ts +164 -0
- package/pi.dev-src/pi-mono/coding-agent/src/modes/interactive/components/diff.ts +147 -0
- package/pi.dev-src/pi-mono/coding-agent/src/modes/interactive/components/dynamic-border.ts +25 -0
- package/pi.dev-src/pi-mono/coding-agent/src/modes/interactive/components/earendil-announcement.ts +53 -0
- package/pi.dev-src/pi-mono/coding-agent/src/modes/interactive/components/extension-editor.ts +147 -0
- package/pi.dev-src/pi-mono/coding-agent/src/modes/interactive/components/extension-input.ts +87 -0
- package/pi.dev-src/pi-mono/coding-agent/src/modes/interactive/components/extension-selector.ts +107 -0
- package/pi.dev-src/pi-mono/coding-agent/src/modes/interactive/components/footer.ts +220 -0
- package/pi.dev-src/pi-mono/coding-agent/src/modes/interactive/components/index.ts +32 -0
- package/pi.dev-src/pi-mono/coding-agent/src/modes/interactive/components/keybinding-hints.ts +24 -0
- package/pi.dev-src/pi-mono/coding-agent/src/modes/interactive/components/login-dialog.ts +178 -0
- package/pi.dev-src/pi-mono/coding-agent/src/modes/interactive/components/model-selector.ts +338 -0
- package/pi.dev-src/pi-mono/coding-agent/src/modes/interactive/components/oauth-selector.ts +121 -0
- package/pi.dev-src/pi-mono/coding-agent/src/modes/interactive/components/scoped-models-selector.ts +355 -0
- package/pi.dev-src/pi-mono/coding-agent/src/modes/interactive/components/session-selector-search.ts +194 -0
- package/pi.dev-src/pi-mono/coding-agent/src/modes/interactive/components/session-selector.ts +1026 -0
- package/pi.dev-src/pi-mono/coding-agent/src/modes/interactive/components/settings-selector.ts +456 -0
- package/pi.dev-src/pi-mono/coding-agent/src/modes/interactive/components/show-images-selector.ts +50 -0
- package/pi.dev-src/pi-mono/coding-agent/src/modes/interactive/components/skill-invocation-message.ts +55 -0
- package/pi.dev-src/pi-mono/coding-agent/src/modes/interactive/components/theme-selector.ts +67 -0
- package/pi.dev-src/pi-mono/coding-agent/src/modes/interactive/components/thinking-selector.ts +74 -0
- package/pi.dev-src/pi-mono/coding-agent/src/modes/interactive/components/tool-execution.ts +352 -0
- package/pi.dev-src/pi-mono/coding-agent/src/modes/interactive/components/tree-selector.ts +1246 -0
- package/pi.dev-src/pi-mono/coding-agent/src/modes/interactive/components/user-message-selector.ts +155 -0
- package/pi.dev-src/pi-mono/coding-agent/src/modes/interactive/components/user-message.ts +35 -0
- package/pi.dev-src/pi-mono/coding-agent/src/modes/interactive/components/visual-truncate.ts +50 -0
- package/pi.dev-src/pi-mono/coding-agent/src/modes/interactive/interactive-mode.ts +5155 -0
- package/pi.dev-src/pi-mono/coding-agent/src/modes/interactive/theme/dark.json +85 -0
- package/pi.dev-src/pi-mono/coding-agent/src/modes/interactive/theme/light.json +84 -0
- package/pi.dev-src/pi-mono/coding-agent/src/modes/interactive/theme/theme-schema.json +335 -0
- package/pi.dev-src/pi-mono/coding-agent/src/modes/interactive/theme/theme.ts +1141 -0
- package/pi.dev-src/pi-mono/coding-agent/src/modes/print-mode.ts +158 -0
- package/pi.dev-src/pi-mono/coding-agent/src/modes/rpc/jsonl.ts +58 -0
- package/pi.dev-src/pi-mono/coding-agent/src/modes/rpc/rpc-client.ts +515 -0
- package/pi.dev-src/pi-mono/coding-agent/src/modes/rpc/rpc-mode.ts +745 -0
- package/pi.dev-src/pi-mono/coding-agent/src/modes/rpc/rpc-types.ts +264 -0
- package/pi.dev-src/pi-mono/coding-agent/src/package-manager-cli.ts +270 -0
- package/pi.dev-src/pi-mono/coding-agent/src/utils/changelog.ts +99 -0
- package/pi.dev-src/pi-mono/coding-agent/src/utils/child-process.ts +86 -0
- package/pi.dev-src/pi-mono/coding-agent/src/utils/clipboard-image.ts +300 -0
- package/pi.dev-src/pi-mono/coding-agent/src/utils/clipboard-native.ts +22 -0
- package/pi.dev-src/pi-mono/coding-agent/src/utils/clipboard.ts +81 -0
- package/pi.dev-src/pi-mono/coding-agent/src/utils/exif-orientation.ts +183 -0
- package/pi.dev-src/pi-mono/coding-agent/src/utils/frontmatter.ts +39 -0
- package/pi.dev-src/pi-mono/coding-agent/src/utils/fs-watch.ts +30 -0
- package/pi.dev-src/pi-mono/coding-agent/src/utils/git.ts +192 -0
- package/pi.dev-src/pi-mono/coding-agent/src/utils/image-convert.ts +41 -0
- package/pi.dev-src/pi-mono/coding-agent/src/utils/image-resize.ts +176 -0
- package/pi.dev-src/pi-mono/coding-agent/src/utils/mime.ts +30 -0
- package/pi.dev-src/pi-mono/coding-agent/src/utils/paths.ts +20 -0
- package/pi.dev-src/pi-mono/coding-agent/src/utils/photon.ts +139 -0
- package/pi.dev-src/pi-mono/coding-agent/src/utils/shell.ts +210 -0
- package/pi.dev-src/pi-mono/coding-agent/src/utils/sleep.ts +18 -0
- package/pi.dev-src/pi-mono/coding-agent/src/utils/tools-manager.ts +287 -0
- package/pi.dev-src/pi-mono/coding-agent/test/agent-session-auto-compaction-queue.test.ts +437 -0
- package/pi.dev-src/pi-mono/coding-agent/test/agent-session-branching.test.ts +156 -0
- package/pi.dev-src/pi-mono/coding-agent/test/agent-session-compaction.test.ts +208 -0
- package/pi.dev-src/pi-mono/coding-agent/test/agent-session-concurrent.test.ts +618 -0
- package/pi.dev-src/pi-mono/coding-agent/test/agent-session-dynamic-provider.test.ts +117 -0
- package/pi.dev-src/pi-mono/coding-agent/test/agent-session-dynamic-tools.test.ts +182 -0
- package/pi.dev-src/pi-mono/coding-agent/test/agent-session-retry.test.ts +318 -0
- package/pi.dev-src/pi-mono/coding-agent/test/agent-session-runtime-events.test.ts +208 -0
- package/pi.dev-src/pi-mono/coding-agent/test/agent-session-stats.test.ts +143 -0
- package/pi.dev-src/pi-mono/coding-agent/test/agent-session-tree-navigation.test.ts +323 -0
- package/pi.dev-src/pi-mono/coding-agent/test/args.test.ts +328 -0
- package/pi.dev-src/pi-mono/coding-agent/test/assistant-message.test.ts +57 -0
- package/pi.dev-src/pi-mono/coding-agent/test/auth-storage.test.ts +462 -0
- package/pi.dev-src/pi-mono/coding-agent/test/bash-close-hang-windows.test.ts +126 -0
- package/pi.dev-src/pi-mono/coding-agent/test/bash-execution-width.test.ts +80 -0
- package/pi.dev-src/pi-mono/coding-agent/test/block-images.test.ts +120 -0
- package/pi.dev-src/pi-mono/coding-agent/test/clipboard-image-bmp-conversion.test.ts +88 -0
- package/pi.dev-src/pi-mono/coding-agent/test/clipboard-image.test.ts +136 -0
- package/pi.dev-src/pi-mono/coding-agent/test/compaction-extensions-example.test.ts +66 -0
- package/pi.dev-src/pi-mono/coding-agent/test/compaction-extensions.test.ts +415 -0
- package/pi.dev-src/pi-mono/coding-agent/test/compaction-serialization.test.ts +79 -0
- package/pi.dev-src/pi-mono/coding-agent/test/compaction-summary-reasoning.test.ts +118 -0
- package/pi.dev-src/pi-mono/coding-agent/test/compaction-thinking-model.test.ts +223 -0
- package/pi.dev-src/pi-mono/coding-agent/test/compaction.test.ts +550 -0
- package/pi.dev-src/pi-mono/coding-agent/test/config.test.ts +30 -0
- package/pi.dev-src/pi-mono/coding-agent/test/edit-tool-legacy-input.test.ts +116 -0
- package/pi.dev-src/pi-mono/coding-agent/test/edit-tool-no-full-redraw.test.ts +235 -0
- package/pi.dev-src/pi-mono/coding-agent/test/export-html-whitespace.test.ts +12 -0
- package/pi.dev-src/pi-mono/coding-agent/test/export-html-xss.test.ts +28 -0
- package/pi.dev-src/pi-mono/coding-agent/test/extensions-discovery.test.ts +463 -0
- package/pi.dev-src/pi-mono/coding-agent/test/extensions-input-event.test.ts +112 -0
- package/pi.dev-src/pi-mono/coding-agent/test/extensions-runner.test.ts +805 -0
- package/pi.dev-src/pi-mono/coding-agent/test/file-mutation-queue.test.ts +163 -0
- package/pi.dev-src/pi-mono/coding-agent/test/fixtures/assistant-message-with-thinking-code.json +33 -0
- package/pi.dev-src/pi-mono/coding-agent/test/fixtures/before-compaction.jsonl +1003 -0
- package/pi.dev-src/pi-mono/coding-agent/test/fixtures/empty-agent/.gitkeep +0 -0
- package/pi.dev-src/pi-mono/coding-agent/test/fixtures/empty-cwd/.gitkeep +0 -0
- package/pi.dev-src/pi-mono/coding-agent/test/fixtures/large-session.jsonl +1019 -0
- package/pi.dev-src/pi-mono/coding-agent/test/fixtures/skills/consecutive-hyphens/SKILL.md +8 -0
- package/pi.dev-src/pi-mono/coding-agent/test/fixtures/skills/disable-model-invocation/SKILL.md +9 -0
- package/pi.dev-src/pi-mono/coding-agent/test/fixtures/skills/invalid-name-chars/SKILL.md +8 -0
- package/pi.dev-src/pi-mono/coding-agent/test/fixtures/skills/invalid-yaml/SKILL.md +8 -0
- package/pi.dev-src/pi-mono/coding-agent/test/fixtures/skills/long-name/SKILL.md +8 -0
- package/pi.dev-src/pi-mono/coding-agent/test/fixtures/skills/missing-description/SKILL.md +7 -0
- package/pi.dev-src/pi-mono/coding-agent/test/fixtures/skills/multiline-description/SKILL.md +11 -0
- package/pi.dev-src/pi-mono/coding-agent/test/fixtures/skills/name-mismatch/SKILL.md +8 -0
- package/pi.dev-src/pi-mono/coding-agent/test/fixtures/skills/nested/child-skill/SKILL.md +8 -0
- package/pi.dev-src/pi-mono/coding-agent/test/fixtures/skills/no-frontmatter/SKILL.md +3 -0
- package/pi.dev-src/pi-mono/coding-agent/test/fixtures/skills/root-skill-preferred/SKILL.md +3 -0
- package/pi.dev-src/pi-mono/coding-agent/test/fixtures/skills/root-skill-preferred/nested-child/SKILL.md +3 -0
- package/pi.dev-src/pi-mono/coding-agent/test/fixtures/skills/unknown-field/SKILL.md +10 -0
- package/pi.dev-src/pi-mono/coding-agent/test/fixtures/skills/valid-skill/SKILL.md +8 -0
- package/pi.dev-src/pi-mono/coding-agent/test/fixtures/skills-collision/first/calendar/SKILL.md +8 -0
- package/pi.dev-src/pi-mono/coding-agent/test/fixtures/skills-collision/second/calendar/SKILL.md +8 -0
- package/pi.dev-src/pi-mono/coding-agent/test/footer-data-provider.test.ts +265 -0
- package/pi.dev-src/pi-mono/coding-agent/test/footer-width.test.ts +115 -0
- package/pi.dev-src/pi-mono/coding-agent/test/frontmatter.test.ts +60 -0
- package/pi.dev-src/pi-mono/coding-agent/test/git-ssh-url.test.ts +78 -0
- package/pi.dev-src/pi-mono/coding-agent/test/git-update.test.ts +411 -0
- package/pi.dev-src/pi-mono/coding-agent/test/image-processing.test.ts +145 -0
- package/pi.dev-src/pi-mono/coding-agent/test/image-resize-callers.test.ts +53 -0
- package/pi.dev-src/pi-mono/coding-agent/test/initial-message.test.ts +48 -0
- package/pi.dev-src/pi-mono/coding-agent/test/interactive-mode-anthropic-warning.test.ts +73 -0
- package/pi.dev-src/pi-mono/coding-agent/test/interactive-mode-clone-command.test.ts +77 -0
- package/pi.dev-src/pi-mono/coding-agent/test/interactive-mode-compaction.test.ts +57 -0
- package/pi.dev-src/pi-mono/coding-agent/test/interactive-mode-import-command.test.ts +144 -0
- package/pi.dev-src/pi-mono/coding-agent/test/interactive-mode-status.test.ts +635 -0
- package/pi.dev-src/pi-mono/coding-agent/test/interactive-mode-suspend.test.ts +149 -0
- package/pi.dev-src/pi-mono/coding-agent/test/keybindings-migration.test.ts +88 -0
- package/pi.dev-src/pi-mono/coding-agent/test/model-registry.test.ts +1209 -0
- package/pi.dev-src/pi-mono/coding-agent/test/model-resolver.test.ts +436 -0
- package/pi.dev-src/pi-mono/coding-agent/test/package-command-paths.test.ts +144 -0
- package/pi.dev-src/pi-mono/coding-agent/test/package-manager-ssh.test.ts +97 -0
- package/pi.dev-src/pi-mono/coding-agent/test/package-manager.test.ts +1795 -0
- package/pi.dev-src/pi-mono/coding-agent/test/path-utils.test.ts +162 -0
- package/pi.dev-src/pi-mono/coding-agent/test/plan-mode-utils.test.ts +261 -0
- package/pi.dev-src/pi-mono/coding-agent/test/print-mode.test.ts +140 -0
- package/pi.dev-src/pi-mono/coding-agent/test/prompt-templates.test.ts +515 -0
- package/pi.dev-src/pi-mono/coding-agent/test/resource-loader.test.ts +576 -0
- package/pi.dev-src/pi-mono/coding-agent/test/rpc-client-clone.test.ts +29 -0
- package/pi.dev-src/pi-mono/coding-agent/test/rpc-example.ts +86 -0
- package/pi.dev-src/pi-mono/coding-agent/test/rpc-jsonl.test.ts +65 -0
- package/pi.dev-src/pi-mono/coding-agent/test/rpc-prompt-response-semantics.test.ts +285 -0
- package/pi.dev-src/pi-mono/coding-agent/test/rpc.test.ts +321 -0
- package/pi.dev-src/pi-mono/coding-agent/test/sdk-codex-cache-probe-tool-loop.ts +364 -0
- package/pi.dev-src/pi-mono/coding-agent/test/sdk-openrouter-attribution.test.ts +181 -0
- package/pi.dev-src/pi-mono/coding-agent/test/sdk-session-manager.test.ts +95 -0
- package/pi.dev-src/pi-mono/coding-agent/test/sdk-skills.test.ts +109 -0
- package/pi.dev-src/pi-mono/coding-agent/test/session-cwd.test.ts +91 -0
- package/pi.dev-src/pi-mono/coding-agent/test/session-info-modified-timestamp.test.ts +83 -0
- package/pi.dev-src/pi-mono/coding-agent/test/session-manager/build-context.test.ts +268 -0
- package/pi.dev-src/pi-mono/coding-agent/test/session-manager/custom-session-id.test.ts +109 -0
- package/pi.dev-src/pi-mono/coding-agent/test/session-manager/file-operations.test.ts +208 -0
- package/pi.dev-src/pi-mono/coding-agent/test/session-manager/labels.test.ts +198 -0
- package/pi.dev-src/pi-mono/coding-agent/test/session-manager/migration.test.ts +78 -0
- package/pi.dev-src/pi-mono/coding-agent/test/session-manager/save-entry.test.ts +55 -0
- package/pi.dev-src/pi-mono/coding-agent/test/session-manager/tree-traversal.test.ts +533 -0
- package/pi.dev-src/pi-mono/coding-agent/test/session-selector-path-delete.test.ts +315 -0
- package/pi.dev-src/pi-mono/coding-agent/test/session-selector-rename.test.ts +111 -0
- package/pi.dev-src/pi-mono/coding-agent/test/session-selector-search.test.ts +195 -0
- package/pi.dev-src/pi-mono/coding-agent/test/settings-manager-bug.test.ts +147 -0
- package/pi.dev-src/pi-mono/coding-agent/test/settings-manager.test.ts +319 -0
- package/pi.dev-src/pi-mono/coding-agent/test/skills.test.ts +432 -0
- package/pi.dev-src/pi-mono/coding-agent/test/stdout-cleanliness.test.ts +103 -0
- package/pi.dev-src/pi-mono/coding-agent/test/streaming-render-debug.ts +97 -0
- package/pi.dev-src/pi-mono/coding-agent/test/suite/agent-session-bash-persistence.test.ts +242 -0
- package/pi.dev-src/pi-mono/coding-agent/test/suite/agent-session-compaction.test.ts +343 -0
- package/pi.dev-src/pi-mono/coding-agent/test/suite/agent-session-model-extension.test.ts +325 -0
- package/pi.dev-src/pi-mono/coding-agent/test/suite/agent-session-prompt.test.ts +323 -0
- package/pi.dev-src/pi-mono/coding-agent/test/suite/agent-session-queue.test.ts +422 -0
- package/pi.dev-src/pi-mono/coding-agent/test/suite/agent-session-retry-events.test.ts +358 -0
- package/pi.dev-src/pi-mono/coding-agent/test/suite/agent-session-runtime.test.ts +553 -0
- package/pi.dev-src/pi-mono/coding-agent/test/suite/harness.ts +208 -0
- package/pi.dev-src/pi-mono/coding-agent/test/suite/regressions/2023-queued-slash-command-followup.test.ts +80 -0
- package/pi.dev-src/pi-mono/coding-agent/test/suite/regressions/2753-reload-stale-resource-settings.test.ts +100 -0
- package/pi.dev-src/pi-mono/coding-agent/test/suite/regressions/2781-skill-collision-precedence.test.ts +120 -0
- package/pi.dev-src/pi-mono/coding-agent/test/suite/regressions/2791-fswatch-error-crash.test.ts +106 -0
- package/pi.dev-src/pi-mono/coding-agent/test/suite/regressions/2835-tools-allowlist-filters-extension-tools.test.ts +94 -0
- package/pi.dev-src/pi-mono/coding-agent/test/suite/regressions/2860-replaced-session-context.test.ts +274 -0
- package/pi.dev-src/pi-mono/coding-agent/test/suite/regressions/3217-scoped-model-order.test.ts +104 -0
- package/pi.dev-src/pi-mono/coding-agent/test/suite/regressions/3302-find-path-glob.test.ts +72 -0
- package/pi.dev-src/pi-mono/coding-agent/test/suite/regressions/3303-find-nested-gitignore.test.ts +83 -0
- package/pi.dev-src/pi-mono/coding-agent/test/suite/regressions/3317-network-connection-lost-retry.test.ts +33 -0
- package/pi.dev-src/pi-mono/coding-agent/test/system-prompt.test.ts +102 -0
- package/pi.dev-src/pi-mono/coding-agent/test/test-harness.test.ts +321 -0
- package/pi.dev-src/pi-mono/coding-agent/test/test-harness.ts +446 -0
- package/pi.dev-src/pi-mono/coding-agent/test/test-theme-colors.ts +246 -0
- package/pi.dev-src/pi-mono/coding-agent/test/theme-export.test.ts +104 -0
- package/pi.dev-src/pi-mono/coding-agent/test/tool-execution-component.test.ts +331 -0
- package/pi.dev-src/pi-mono/coding-agent/test/tools.test.ts +889 -0
- package/pi.dev-src/pi-mono/coding-agent/test/tree-selector.test.ts +655 -0
- package/pi.dev-src/pi-mono/coding-agent/test/trigger-compact-extension.test.ts +57 -0
- package/pi.dev-src/pi-mono/coding-agent/test/truncate-to-width.test.ts +81 -0
- package/pi.dev-src/pi-mono/coding-agent/test/user-message.test.ts +25 -0
- package/pi.dev-src/pi-mono/coding-agent/test/utilities.ts +316 -0
- package/pi.dev-src/pi-mono/coding-agent/tsconfig.examples.json +16 -0
- package/pi.dev-src/pi-mono/coding-agent/vitest.config.ts +26 -0
- package/pi.dev-src/pi-mono/mom/dev.sh +30 -0
- package/pi.dev-src/pi-mono/mom/docker.sh +95 -0
- package/pi.dev-src/pi-mono/mom/scripts/migrate-timestamps.ts +121 -0
- package/pi.dev-src/pi-mono/mom/src/agent.ts +882 -0
- package/pi.dev-src/pi-mono/mom/src/context.ts +180 -0
- package/pi.dev-src/pi-mono/mom/src/download.ts +117 -0
- package/pi.dev-src/pi-mono/mom/src/events.ts +442 -0
- package/pi.dev-src/pi-mono/mom/src/fs-watch.ts +30 -0
- package/pi.dev-src/pi-mono/mom/src/log.ts +271 -0
- package/pi.dev-src/pi-mono/mom/src/main.ts +367 -0
- package/pi.dev-src/pi-mono/mom/src/sandbox.ts +221 -0
- package/pi.dev-src/pi-mono/mom/src/slack.ts +623 -0
- package/pi.dev-src/pi-mono/mom/src/store.ts +234 -0
- package/pi.dev-src/pi-mono/mom/src/tools/attach.ts +47 -0
- package/pi.dev-src/pi-mono/mom/src/tools/bash.ts +97 -0
- package/pi.dev-src/pi-mono/mom/src/tools/edit.ts +165 -0
- package/pi.dev-src/pi-mono/mom/src/tools/index.ts +19 -0
- package/pi.dev-src/pi-mono/mom/src/tools/read.ts +159 -0
- package/pi.dev-src/pi-mono/mom/src/tools/truncate.ts +236 -0
- package/pi.dev-src/pi-mono/mom/src/tools/write.ts +45 -0
- package/pi.dev-src/pi-mono/mom/test/events.test.ts +50 -0
- package/pi.dev-src/pi-mono/pods/scripts/model_run.sh +83 -0
- package/pi.dev-src/pi-mono/pods/scripts/pod_setup.sh +336 -0
- package/pi.dev-src/pi-mono/pods/src/cli.ts +360 -0
- package/pi.dev-src/pi-mono/pods/src/commands/models.ts +753 -0
- package/pi.dev-src/pi-mono/pods/src/commands/pods.ts +205 -0
- package/pi.dev-src/pi-mono/pods/src/commands/prompt.ts +84 -0
- package/pi.dev-src/pi-mono/pods/src/config.ts +80 -0
- package/pi.dev-src/pi-mono/pods/src/index.ts +2 -0
- package/pi.dev-src/pi-mono/pods/src/model-configs.ts +111 -0
- package/pi.dev-src/pi-mono/pods/src/models.json +295 -0
- package/pi.dev-src/pi-mono/pods/src/ssh.ts +151 -0
- package/pi.dev-src/pi-mono/pods/src/types.ts +27 -0
- package/pi.dev-src/pi-mono/tui/src/autocomplete.ts +783 -0
- package/pi.dev-src/pi-mono/tui/src/components/box.ts +137 -0
- package/pi.dev-src/pi-mono/tui/src/components/cancellable-loader.ts +40 -0
- package/pi.dev-src/pi-mono/tui/src/components/editor.ts +2280 -0
- package/pi.dev-src/pi-mono/tui/src/components/image.ts +104 -0
- package/pi.dev-src/pi-mono/tui/src/components/input.ts +503 -0
- package/pi.dev-src/pi-mono/tui/src/components/loader.ts +86 -0
- package/pi.dev-src/pi-mono/tui/src/components/markdown.ts +852 -0
- package/pi.dev-src/pi-mono/tui/src/components/select-list.ts +229 -0
- package/pi.dev-src/pi-mono/tui/src/components/settings-list.ts +250 -0
- package/pi.dev-src/pi-mono/tui/src/components/spacer.ts +28 -0
- package/pi.dev-src/pi-mono/tui/src/components/text.ts +106 -0
- package/pi.dev-src/pi-mono/tui/src/components/truncated-text.ts +65 -0
- package/pi.dev-src/pi-mono/tui/src/editor-component.ts +74 -0
- package/pi.dev-src/pi-mono/tui/src/fuzzy.ts +133 -0
- package/pi.dev-src/pi-mono/tui/src/index.ts +106 -0
- package/pi.dev-src/pi-mono/tui/src/keybindings.ts +244 -0
- package/pi.dev-src/pi-mono/tui/src/keys.ts +1400 -0
- package/pi.dev-src/pi-mono/tui/src/kill-ring.ts +46 -0
- package/pi.dev-src/pi-mono/tui/src/stdin-buffer.ts +386 -0
- package/pi.dev-src/pi-mono/tui/src/terminal-image.ts +414 -0
- package/pi.dev-src/pi-mono/tui/src/terminal.ts +373 -0
- package/pi.dev-src/pi-mono/tui/src/tui.ts +1243 -0
- package/pi.dev-src/pi-mono/tui/src/undo-stack.ts +28 -0
- package/pi.dev-src/pi-mono/tui/src/utils.ts +1085 -0
- package/pi.dev-src/pi-mono/tui/test/autocomplete.test.ts +542 -0
- package/pi.dev-src/pi-mono/tui/test/bug-regression-isimageline-startswith-bug.test.ts +237 -0
- package/pi.dev-src/pi-mono/tui/test/chat-simple.ts +129 -0
- package/pi.dev-src/pi-mono/tui/test/editor.test.ts +3812 -0
- package/pi.dev-src/pi-mono/tui/test/fuzzy.test.ts +98 -0
- package/pi.dev-src/pi-mono/tui/test/image-test.ts +56 -0
- package/pi.dev-src/pi-mono/tui/test/input.test.ts +580 -0
- package/pi.dev-src/pi-mono/tui/test/key-tester.ts +105 -0
- package/pi.dev-src/pi-mono/tui/test/keybindings.test.ts +38 -0
- package/pi.dev-src/pi-mono/tui/test/keys.test.ts +614 -0
- package/pi.dev-src/pi-mono/tui/test/markdown.test.ts +1236 -0
- package/pi.dev-src/pi-mono/tui/test/overlay-non-capturing.test.ts +609 -0
- package/pi.dev-src/pi-mono/tui/test/overlay-options.test.ts +538 -0
- package/pi.dev-src/pi-mono/tui/test/overlay-short-content.test.ts +56 -0
- package/pi.dev-src/pi-mono/tui/test/regression-regional-indicator-width.test.ts +52 -0
- package/pi.dev-src/pi-mono/tui/test/select-list.test.ts +116 -0
- package/pi.dev-src/pi-mono/tui/test/stdin-buffer.test.ts +422 -0
- package/pi.dev-src/pi-mono/tui/test/terminal-image.test.ts +279 -0
- package/pi.dev-src/pi-mono/tui/test/test-themes.ts +38 -0
- package/pi.dev-src/pi-mono/tui/test/truncate-to-width.test.ts +62 -0
- package/pi.dev-src/pi-mono/tui/test/truncated-text.test.ts +129 -0
- package/pi.dev-src/pi-mono/tui/test/tui-cell-size-input.test.ts +81 -0
- package/pi.dev-src/pi-mono/tui/test/tui-overlay-style-leak.test.ts +72 -0
- package/pi.dev-src/pi-mono/tui/test/tui-render.test.ts +509 -0
- package/pi.dev-src/pi-mono/tui/test/viewport-overwrite-repro.ts +106 -0
- package/pi.dev-src/pi-mono/tui/test/virtual-terminal.ts +218 -0
- package/pi.dev-src/pi-mono/tui/test/wrap-ansi.test.ts +207 -0
- package/pi.dev-src/pi-mono/tui/vitest.config.ts +7 -0
- package/pi.dev-src/pi-mono/web-ui/example/index.html +13 -0
- package/pi.dev-src/pi-mono/web-ui/example/src/app.css +1 -0
- package/pi.dev-src/pi-mono/web-ui/example/src/custom-messages.ts +99 -0
- package/pi.dev-src/pi-mono/web-ui/example/src/main.ts +421 -0
- package/pi.dev-src/pi-mono/web-ui/example/tsconfig.json +23 -0
- package/pi.dev-src/pi-mono/web-ui/example/vite.config.ts +6 -0
- package/pi.dev-src/pi-mono/web-ui/scripts/count-prompt-tokens.ts +88 -0
- package/pi.dev-src/pi-mono/web-ui/src/ChatPanel.ts +209 -0
- package/pi.dev-src/pi-mono/web-ui/src/app.css +68 -0
- package/pi.dev-src/pi-mono/web-ui/src/components/AgentInterface.ts +403 -0
- package/pi.dev-src/pi-mono/web-ui/src/components/AttachmentTile.ts +107 -0
- package/pi.dev-src/pi-mono/web-ui/src/components/ConsoleBlock.ts +72 -0
- package/pi.dev-src/pi-mono/web-ui/src/components/CustomProviderCard.ts +100 -0
- package/pi.dev-src/pi-mono/web-ui/src/components/ExpandableSection.ts +46 -0
- package/pi.dev-src/pi-mono/web-ui/src/components/Input.ts +113 -0
- package/pi.dev-src/pi-mono/web-ui/src/components/MessageEditor.ts +405 -0
- package/pi.dev-src/pi-mono/web-ui/src/components/MessageList.ts +98 -0
- package/pi.dev-src/pi-mono/web-ui/src/components/Messages.ts +383 -0
- package/pi.dev-src/pi-mono/web-ui/src/components/ProviderKeyInput.ts +153 -0
- package/pi.dev-src/pi-mono/web-ui/src/components/SandboxedIframe.ts +626 -0
- package/pi.dev-src/pi-mono/web-ui/src/components/StreamingMessageContainer.ts +103 -0
- package/pi.dev-src/pi-mono/web-ui/src/components/ThinkingBlock.ts +43 -0
- package/pi.dev-src/pi-mono/web-ui/src/components/message-renderer-registry.ts +28 -0
- package/pi.dev-src/pi-mono/web-ui/src/components/sandbox/ArtifactsRuntimeProvider.ts +220 -0
- package/pi.dev-src/pi-mono/web-ui/src/components/sandbox/AttachmentsRuntimeProvider.ts +66 -0
- package/pi.dev-src/pi-mono/web-ui/src/components/sandbox/ConsoleRuntimeProvider.ts +186 -0
- package/pi.dev-src/pi-mono/web-ui/src/components/sandbox/FileDownloadRuntimeProvider.ts +110 -0
- package/pi.dev-src/pi-mono/web-ui/src/components/sandbox/RuntimeMessageBridge.ts +82 -0
- package/pi.dev-src/pi-mono/web-ui/src/components/sandbox/RuntimeMessageRouter.ts +216 -0
- package/pi.dev-src/pi-mono/web-ui/src/components/sandbox/SandboxRuntimeProvider.ts +52 -0
- package/pi.dev-src/pi-mono/web-ui/src/dialogs/ApiKeyPromptDialog.ts +75 -0
- package/pi.dev-src/pi-mono/web-ui/src/dialogs/AttachmentOverlay.ts +636 -0
- package/pi.dev-src/pi-mono/web-ui/src/dialogs/CustomProviderDialog.ts +274 -0
- package/pi.dev-src/pi-mono/web-ui/src/dialogs/ModelSelector.ts +370 -0
- package/pi.dev-src/pi-mono/web-ui/src/dialogs/PersistentStorageDialog.ts +144 -0
- package/pi.dev-src/pi-mono/web-ui/src/dialogs/ProvidersModelsTab.ts +212 -0
- package/pi.dev-src/pi-mono/web-ui/src/dialogs/SessionListDialog.ts +150 -0
- package/pi.dev-src/pi-mono/web-ui/src/dialogs/SettingsDialog.ts +218 -0
- package/pi.dev-src/pi-mono/web-ui/src/index.ts +120 -0
- package/pi.dev-src/pi-mono/web-ui/src/prompts/prompts.ts +282 -0
- package/pi.dev-src/pi-mono/web-ui/src/storage/app-storage.ts +60 -0
- package/pi.dev-src/pi-mono/web-ui/src/storage/backends/indexeddb-storage-backend.ts +193 -0
- package/pi.dev-src/pi-mono/web-ui/src/storage/store.ts +33 -0
- package/pi.dev-src/pi-mono/web-ui/src/storage/stores/custom-providers-store.ts +62 -0
- package/pi.dev-src/pi-mono/web-ui/src/storage/stores/provider-keys-store.ts +33 -0
- package/pi.dev-src/pi-mono/web-ui/src/storage/stores/sessions-store.ts +136 -0
- package/pi.dev-src/pi-mono/web-ui/src/storage/stores/settings-store.ts +34 -0
- package/pi.dev-src/pi-mono/web-ui/src/storage/types.ts +206 -0
- package/pi.dev-src/pi-mono/web-ui/src/tools/artifacts/ArtifactElement.ts +14 -0
- package/pi.dev-src/pi-mono/web-ui/src/tools/artifacts/ArtifactPill.ts +26 -0
- package/pi.dev-src/pi-mono/web-ui/src/tools/artifacts/Console.ts +93 -0
- package/pi.dev-src/pi-mono/web-ui/src/tools/artifacts/DocxArtifact.ts +213 -0
- package/pi.dev-src/pi-mono/web-ui/src/tools/artifacts/ExcelArtifact.ts +231 -0
- package/pi.dev-src/pi-mono/web-ui/src/tools/artifacts/GenericArtifact.ts +117 -0
- package/pi.dev-src/pi-mono/web-ui/src/tools/artifacts/HtmlArtifact.ts +195 -0
- package/pi.dev-src/pi-mono/web-ui/src/tools/artifacts/ImageArtifact.ts +116 -0
- package/pi.dev-src/pi-mono/web-ui/src/tools/artifacts/MarkdownArtifact.ts +82 -0
- package/pi.dev-src/pi-mono/web-ui/src/tools/artifacts/PdfArtifact.ts +201 -0
- package/pi.dev-src/pi-mono/web-ui/src/tools/artifacts/SvgArtifact.ts +119 -0
- package/pi.dev-src/pi-mono/web-ui/src/tools/artifacts/TextArtifact.ts +148 -0
- package/pi.dev-src/pi-mono/web-ui/src/tools/artifacts/artifacts-tool-renderer.ts +310 -0
- package/pi.dev-src/pi-mono/web-ui/src/tools/artifacts/artifacts.ts +713 -0
- package/pi.dev-src/pi-mono/web-ui/src/tools/artifacts/index.ts +7 -0
- package/pi.dev-src/pi-mono/web-ui/src/tools/extract-document.ts +275 -0
- package/pi.dev-src/pi-mono/web-ui/src/tools/index.ts +46 -0
- package/pi.dev-src/pi-mono/web-ui/src/tools/javascript-repl.ts +293 -0
- package/pi.dev-src/pi-mono/web-ui/src/tools/renderer-registry.ts +130 -0
- package/pi.dev-src/pi-mono/web-ui/src/tools/renderers/BashRenderer.ts +52 -0
- package/pi.dev-src/pi-mono/web-ui/src/tools/renderers/CalculateRenderer.ts +58 -0
- package/pi.dev-src/pi-mono/web-ui/src/tools/renderers/DefaultRenderer.ts +103 -0
- package/pi.dev-src/pi-mono/web-ui/src/tools/renderers/GetCurrentTimeRenderer.ts +92 -0
- package/pi.dev-src/pi-mono/web-ui/src/tools/types.ts +15 -0
- package/pi.dev-src/pi-mono/web-ui/src/utils/attachment-utils.ts +472 -0
- package/pi.dev-src/pi-mono/web-ui/src/utils/auth-token.ts +22 -0
- package/pi.dev-src/pi-mono/web-ui/src/utils/format.ts +42 -0
- package/pi.dev-src/pi-mono/web-ui/src/utils/i18n.ts +653 -0
- package/pi.dev-src/pi-mono/web-ui/src/utils/model-discovery.ts +277 -0
- package/pi.dev-src/pi-mono/web-ui/src/utils/proxy-utils.ts +139 -0
- package/pi.dev-src/pi-mono/web-ui/src/utils/test-sessions.ts +2357 -0
- package/pi.dev-src/pi-mono/web-ui/tsconfig.json +7 -0
- package/scripts/lib/extension-debug-harness.ts +3 -10
- package/scripts/lib/recording-extension-api.ts +71 -2
- package/scripts/pi-usereq-debug.sh +1 -1
- package/scripts/tool-args-to-params.ts +4 -15
- package/src/cli.ts +24 -68
- package/src/core/agent-tool-json.ts +53 -239
- package/src/core/compress-payload.ts +4 -74
- package/src/core/compress.ts +26 -15
- package/src/core/config.ts +468 -42
- package/src/core/debug-runtime.ts +542 -0
- package/src/core/extension-status.ts +314 -59
- package/src/core/find-constructs.ts +3 -3
- package/src/core/find-payload.ts +26 -132
- package/src/core/path-context.ts +346 -81
- package/src/core/pi-notify.ts +115 -5
- package/src/core/pi-usereq-tools.ts +57 -19
- package/src/core/prompt-command-catalog.ts +46 -0
- package/src/core/prompt-command-runtime.ts +1816 -0
- package/src/core/prompt-command-state.ts +211 -0
- package/src/core/prompts.ts +173 -41
- package/src/core/reference-payload.ts +5 -63
- package/src/core/resources.ts +61 -5
- package/src/core/runtime-project-paths.ts +21 -1
- package/src/core/settings-menu.ts +12 -6
- package/src/core/source-analyzer.ts +27 -7
- package/src/core/token-counter.ts +6 -76
- package/src/core/tool-runner.ts +38 -317
- package/src/index.ts +2047 -947
- package/src/resources/instructions/git_commit.md +16 -0
- package/src/resources/instructions/git_read-only.md +2 -0
- package/src/resources/prompts/analyze.md +12 -13
- package/src/resources/prompts/change.md +21 -59
- package/src/resources/prompts/check.md +14 -16
- package/src/resources/prompts/cover.md +21 -59
- package/src/resources/prompts/create.md +5 -4
- package/src/resources/prompts/fix.md +20 -58
- package/src/resources/prompts/flowchart.md +14 -49
- package/src/resources/prompts/implement.md +14 -50
- package/src/resources/prompts/new.md +20 -58
- package/src/resources/prompts/readme.md +14 -50
- package/src/resources/prompts/recreate.md +14 -45
- package/src/resources/prompts/refactor.md +20 -58
- package/src/resources/prompts/references.md +7 -43
- package/src/resources/prompts/renumber.md +8 -39
- package/src/resources/prompts/workflow.md +14 -50
- package/src/resources/prompts/write.md +5 -4
- package/tests/attended-results-scenarios.ts +30 -136
- package/tests/cli-command-option-parity.test.ts +148 -192
- package/tests/debug-extension-harness.test.ts +77 -70
- package/tests/extension-registration.test.ts +3205 -658
- package/tests/fixtures_attended_results/project/compress-line-numbers.json +1 -1
- package/tests/fixtures_attended_results/project/compress.json +1 -1
- package/tests/fixtures_attended_results/project/find-line-numbers.json +1 -1
- package/tests/fixtures_attended_results/project/find.json +1 -1
- package/tests/fixtures_attended_results/project/references.json +2 -2
- package/tests/fixtures_attended_results/standalone/files-compress/fixture_go.go.json +1 -1
- package/tests/fixtures_attended_results/standalone/files-compress-line-numbers/fixture_go.go.json +1 -1
- package/tests/fixtures_attended_results/standalone/files-find/fixture_go.go.json +1 -1
- package/tests/fixtures_attended_results/standalone/files-find-line-numbers/fixture_go.go.json +1 -1
- package/tests/fixtures_attended_results/standalone/files-references/fixture_c.c.json +1 -1
- package/tests/fixtures_attended_results/standalone/files-references/fixture_cpp.cpp.json +1 -1
- package/tests/fixtures_attended_results/standalone/files-references/fixture_csharp.cs.json +1 -1
- package/tests/fixtures_attended_results/standalone/files-references/fixture_elixir.ex.json +1 -1
- package/tests/fixtures_attended_results/standalone/files-references/fixture_go.go.json +1 -1
- package/tests/fixtures_attended_results/standalone/files-references/fixture_haskell.hs.json +1 -1
- package/tests/fixtures_attended_results/standalone/files-references/fixture_java.java.json +1 -1
- package/tests/fixtures_attended_results/standalone/files-references/fixture_javascript.js.json +1 -1
- package/tests/fixtures_attended_results/standalone/files-references/fixture_kotlin.kt.json +1 -1
- package/tests/fixtures_attended_results/standalone/files-references/fixture_lua.lua.json +1 -1
- package/tests/fixtures_attended_results/standalone/files-references/fixture_perl.pl.json +1 -1
- package/tests/fixtures_attended_results/standalone/files-references/fixture_php.php.json +1 -1
- package/tests/fixtures_attended_results/standalone/files-references/fixture_python.py.json +1 -1
- package/tests/fixtures_attended_results/standalone/files-references/fixture_ruby.rb.json +1 -1
- package/tests/fixtures_attended_results/standalone/files-references/fixture_rust.rs.json +1 -1
- package/tests/fixtures_attended_results/standalone/files-references/fixture_scala.scala.json +1 -1
- package/tests/fixtures_attended_results/standalone/files-references/fixture_shell.sh.json +1 -1
- package/tests/fixtures_attended_results/standalone/files-references/fixture_swift.swift.json +1 -1
- package/tests/fixtures_attended_results/standalone/files-references/fixture_typescript.ts.json +1 -1
- package/tests/fixtures_attended_results/standalone/files-references/fixture_zig.zig.json +1 -1
- package/tests/helpers.ts +24 -15
- package/tests/oracle-project.test.ts +29 -30
- package/tests/oracle-standalone.test.ts +27 -1
- package/tests/prompt-rendering.test.ts +40 -9
- package/tests/python_oracle_stubs/tiktoken.py +76 -0
package/src/index.ts
CHANGED
|
@@ -8,8 +8,9 @@
|
|
|
8
8
|
* @brief Declares the extension version string.
|
|
9
9
|
* @details The value is exported for external inspection and packaging metadata alignment. Access complexity is O(1).
|
|
10
10
|
*/
|
|
11
|
-
export const VERSION = "0.
|
|
11
|
+
export const VERSION = "0.11.0";
|
|
12
12
|
|
|
13
|
+
import fs from "node:fs";
|
|
13
14
|
import path from "node:path";
|
|
14
15
|
import type {
|
|
15
16
|
AgentEndEvent,
|
|
@@ -18,47 +19,26 @@ import type {
|
|
|
18
19
|
ExtensionContext,
|
|
19
20
|
ToolInfo,
|
|
20
21
|
} from "@mariozechner/pi-coding-agent";
|
|
22
|
+
import { Text } from "@mariozechner/pi-tui";
|
|
21
23
|
import { Type } from "@sinclair/typebox";
|
|
22
24
|
import {
|
|
23
|
-
|
|
24
|
-
TOKEN_COUNTER_ENCODING,
|
|
25
|
-
type TokenToolPayload,
|
|
26
|
-
} from "./core/token-counter.js";
|
|
27
|
-
import {
|
|
28
|
-
buildDocsCheckToolPayload,
|
|
29
|
-
buildGitCheckToolPayload,
|
|
30
|
-
buildPathQueryToolPayload,
|
|
31
|
-
buildStaticCheckToolPayload,
|
|
32
|
-
buildStructuredToolExecuteResult,
|
|
33
|
-
buildToolExecutionSection,
|
|
34
|
-
buildWorktreeMutationToolPayload,
|
|
35
|
-
buildWorktreeNameToolPayload,
|
|
25
|
+
buildMonolithicToolExecuteResult,
|
|
36
26
|
normalizeToolFailure,
|
|
37
27
|
} from "./core/agent-tool-json.js";
|
|
28
|
+
import type { FindToolScope } from "./core/find-payload.js";
|
|
38
29
|
import {
|
|
39
|
-
|
|
40
|
-
buildReferenceToolPayload,
|
|
41
|
-
type ReferenceToolPayload,
|
|
42
|
-
} from "./core/reference-payload.js";
|
|
43
|
-
import {
|
|
44
|
-
buildCompressToolExecutionStderr,
|
|
45
|
-
buildCompressToolPayload,
|
|
46
|
-
type CompressToolPayload,
|
|
47
|
-
} from "./core/compress-payload.js";
|
|
48
|
-
import {
|
|
49
|
-
buildFindToolExecutionStderr,
|
|
50
|
-
buildFindToolPayload,
|
|
51
|
-
type FindToolPayload,
|
|
52
|
-
type FindToolScope,
|
|
53
|
-
} from "./core/find-payload.js";
|
|
54
|
-
import {
|
|
30
|
+
DEFAULT_GIT_WORKTREE_PREFIX,
|
|
55
31
|
DEFAULT_SRC_DIRS,
|
|
32
|
+
createStaticCheckLanguageConfig,
|
|
56
33
|
getDefaultConfig,
|
|
34
|
+
getDefaultStaticCheckConfig,
|
|
57
35
|
getProjectConfigPath,
|
|
58
36
|
loadConfig,
|
|
59
37
|
normalizeConfigPaths,
|
|
38
|
+
resolveEffectiveGitWorktreeEnabled,
|
|
60
39
|
saveConfig,
|
|
61
40
|
type StaticCheckEntry,
|
|
41
|
+
type StaticCheckLanguageConfig,
|
|
62
42
|
type UseReqConfig,
|
|
63
43
|
} from "./core/config.js";
|
|
64
44
|
import {
|
|
@@ -69,47 +49,101 @@ import {
|
|
|
69
49
|
DEFAULT_PI_NOTIFY_SOUND_LOW_CMD,
|
|
70
50
|
DEFAULT_PI_NOTIFY_SOUND_MID_CMD,
|
|
71
51
|
cyclePiNotifySoundLevel,
|
|
52
|
+
formatPiNotifyControlSequenceText,
|
|
72
53
|
formatPiNotifyPushoverStatus,
|
|
73
54
|
formatPiNotifyStatus,
|
|
55
|
+
hasPiNotifyPushoverCredentials,
|
|
74
56
|
normalizePiNotifyCommand,
|
|
75
57
|
normalizePiNotifyPushoverCredential,
|
|
76
58
|
normalizePiNotifyPushoverPriority,
|
|
77
59
|
normalizePiNotifyTemplateValue,
|
|
60
|
+
parsePiNotifyControlSequenceText,
|
|
78
61
|
runPiNotifyEffects,
|
|
79
62
|
type PiNotifyEventRequest,
|
|
80
63
|
type PiNotifyPushoverPriority,
|
|
81
64
|
type PiNotifySoundLevel,
|
|
82
65
|
} from "./core/pi-notify.js";
|
|
83
|
-
import {
|
|
84
|
-
|
|
66
|
+
import {
|
|
67
|
+
bootstrapRuntimePathState,
|
|
68
|
+
formatRuntimePathForDisplay,
|
|
69
|
+
getRuntimeBasePath,
|
|
70
|
+
getRuntimeContextPath,
|
|
71
|
+
setRuntimeGitPath,
|
|
72
|
+
} from "./core/path-context.js";
|
|
85
73
|
import { showPiUsereqSettingsMenu, type PiUsereqSettingsMenuChoice } from "./core/settings-menu.js";
|
|
86
74
|
import {
|
|
75
|
+
comparePiUsereqStartupToolNames,
|
|
76
|
+
PI_USEREQ_CUSTOM_TOOL_NAMES,
|
|
77
|
+
PI_USEREQ_EMBEDDED_TOOL_NAMES,
|
|
87
78
|
PI_USEREQ_STARTUP_TOOL_SET,
|
|
88
79
|
isPiUsereqEmbeddedToolName,
|
|
89
80
|
normalizeEnabledPiUsereqTools,
|
|
81
|
+
type PiUsereqStartupToolName,
|
|
90
82
|
} from "./core/pi-usereq-tools.js";
|
|
91
83
|
import { renderPrompt } from "./core/prompts.js";
|
|
92
|
-
import {
|
|
84
|
+
import {
|
|
85
|
+
abortPromptCommandExecution,
|
|
86
|
+
activatePromptCommandExecution,
|
|
87
|
+
classifyPromptCommandOutcome,
|
|
88
|
+
finalizePromptCommandExecution,
|
|
89
|
+
getPromptCommandErrorContext,
|
|
90
|
+
preparePromptCommandExecution,
|
|
91
|
+
restorePromptCommandExecution,
|
|
92
|
+
type PromptCommandExecutionPlan,
|
|
93
|
+
} from "./core/prompt-command-runtime.js";
|
|
94
|
+
import {
|
|
95
|
+
DEBUG_PROMPT_NAMES,
|
|
96
|
+
DEBUG_WORKFLOW_STATES,
|
|
97
|
+
DEFAULT_DEBUG_LOG_FILE,
|
|
98
|
+
DEFAULT_DEBUG_LOG_ON_STATUS,
|
|
99
|
+
DEFAULT_DEBUG_STATUS_CHANGES,
|
|
100
|
+
DEFAULT_DEBUG_WORKFLOW_EVENTS,
|
|
101
|
+
logDebugPromptEvent,
|
|
102
|
+
logDebugPromptWorkflowEvent,
|
|
103
|
+
logDebugToolExecution,
|
|
104
|
+
normalizeDebugEnabledPrompts,
|
|
105
|
+
normalizeDebugEnabledTools,
|
|
106
|
+
normalizeDebugLogFile,
|
|
107
|
+
normalizeDebugLogOnStatus,
|
|
108
|
+
normalizeDebugStatusChanges,
|
|
109
|
+
normalizeDebugWorkflowEvents,
|
|
110
|
+
shouldLogDebugPromptWorkflowState,
|
|
111
|
+
type DebugLogOnStatus,
|
|
112
|
+
type DebugWorkflowState,
|
|
113
|
+
} from "./core/debug-runtime.js";
|
|
114
|
+
import { PROMPT_COMMAND_NAMES } from "./core/prompt-command-catalog.js";
|
|
115
|
+
import { resolveRuntimeGitPath } from "./core/runtime-project-paths.js";
|
|
116
|
+
import {
|
|
117
|
+
readPersistedPromptCommandRuntimeState,
|
|
118
|
+
writePersistedPromptCommandRuntimeState,
|
|
119
|
+
} from "./core/prompt-command-state.js";
|
|
120
|
+
import { ensureBundledResourcesAccessible, readBundledPromptDescription } from "./core/resources.js";
|
|
121
|
+
import { ReqError } from "./core/errors.js";
|
|
93
122
|
import {
|
|
94
123
|
PI_USEREQ_STATUS_HOOK_NAMES,
|
|
95
124
|
createPiUsereqStatusController,
|
|
96
125
|
disposePiUsereqStatusController,
|
|
126
|
+
isStaleExtensionContextError,
|
|
97
127
|
renderPiUsereqStatus,
|
|
98
128
|
setPiUsereqStatusConfig,
|
|
129
|
+
setPiUsereqWorkflowState,
|
|
130
|
+
shouldPreservePromptCommandStateOnShutdown,
|
|
99
131
|
updateExtensionStatus,
|
|
100
132
|
type PiUsereqStatusController,
|
|
101
133
|
type PiUsereqStatusHookName,
|
|
102
134
|
} from "./core/extension-status.js";
|
|
103
135
|
import {
|
|
104
|
-
|
|
136
|
+
runCompress,
|
|
137
|
+
runFilesCompress,
|
|
138
|
+
runFilesReferences,
|
|
139
|
+
runFilesSearch,
|
|
105
140
|
runFilesStaticCheck,
|
|
106
|
-
|
|
107
|
-
runGitCheck,
|
|
108
|
-
runGitPath,
|
|
109
|
-
runGitWtCreate,
|
|
110
|
-
runGitWtDelete,
|
|
111
|
-
runGitWtName,
|
|
141
|
+
runFilesTokens,
|
|
112
142
|
runProjectStaticCheck,
|
|
143
|
+
runReferences,
|
|
144
|
+
runSearch,
|
|
145
|
+
runTokens,
|
|
146
|
+
type ToolResult,
|
|
113
147
|
} from "./core/tool-runner.js";
|
|
114
148
|
import { LANGUAGE_TAGS } from "./core/find-constructs.js";
|
|
115
149
|
import {
|
|
@@ -117,29 +151,6 @@ import {
|
|
|
117
151
|
} from "./core/static-check.js";
|
|
118
152
|
import { makeRelativeIfContainsProject, shellSplit } from "./core/utils.js";
|
|
119
153
|
|
|
120
|
-
/**
|
|
121
|
-
* @brief Lists bundled prompt commands exposed by the extension.
|
|
122
|
-
* @details Each entry maps to a `req-<name>` command that renders a bundled prompt and sends it to the active session. Access complexity is O(1).
|
|
123
|
-
*/
|
|
124
|
-
const PROMPT_NAMES = [
|
|
125
|
-
"analyze",
|
|
126
|
-
"change",
|
|
127
|
-
"check",
|
|
128
|
-
"cover",
|
|
129
|
-
"create",
|
|
130
|
-
"fix",
|
|
131
|
-
"flowchart",
|
|
132
|
-
"implement",
|
|
133
|
-
"new",
|
|
134
|
-
"readme",
|
|
135
|
-
"recreate",
|
|
136
|
-
"refactor",
|
|
137
|
-
"references",
|
|
138
|
-
"renumber",
|
|
139
|
-
"workflow",
|
|
140
|
-
"write",
|
|
141
|
-
] as const;
|
|
142
|
-
|
|
143
154
|
/**
|
|
144
155
|
* @brief Describes the optional shortcut-registration surface used by pi-usereq.
|
|
145
156
|
* @details Narrows the runtime API to the documented `registerShortcut(...)`
|
|
@@ -164,7 +175,68 @@ interface PiShortcutRegistrar {
|
|
|
164
175
|
* @return {string} Absolute project base path.
|
|
165
176
|
*/
|
|
166
177
|
function getProjectBase(cwd: string): string {
|
|
167
|
-
|
|
178
|
+
const runtimeBasePath = getRuntimeBasePath(cwd);
|
|
179
|
+
if (fs.existsSync(runtimeBasePath)) {
|
|
180
|
+
return runtimeBasePath;
|
|
181
|
+
}
|
|
182
|
+
bootstrapRuntimePathState(cwd, {
|
|
183
|
+
gitPath: resolveRuntimeGitPath(cwd),
|
|
184
|
+
});
|
|
185
|
+
return getRuntimeBasePath(cwd);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* @brief Resolves a safe process working directory for extension-load paths.
|
|
190
|
+
* @details Returns `process.cwd()` when available and falls back to absolute `PWD`, `HOME`, or `/` when the current shell directory has been deleted. Runtime is O(1). No external state is mutated.
|
|
191
|
+
* @return {string} Absolute fallback-safe process working directory.
|
|
192
|
+
*/
|
|
193
|
+
function getProcessCwdSafe(): string {
|
|
194
|
+
try {
|
|
195
|
+
return process.cwd();
|
|
196
|
+
} catch {
|
|
197
|
+
const fallbackCwd = process.env.PWD ?? process.env.HOME ?? path.sep;
|
|
198
|
+
return path.isAbsolute(fallbackCwd)
|
|
199
|
+
? fallbackCwd
|
|
200
|
+
: path.resolve(path.sep, fallbackCwd);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* @brief Resolves the live working directory used for bootstrap-sensitive flows.
|
|
206
|
+
* @details Prefers the supplied cwd when it still exists. Otherwise reuses the tracked runtime context path when it remains live, then the tracked runtime base path, and finally a process-safe cwd so deleted worktree paths retained by stale contexts cannot poison later prompt preflight or lifecycle bootstrap. Runtime is O(1) plus bounded filesystem probes. No external state is mutated.
|
|
207
|
+
* @param[in] cwd {string} Candidate context cwd.
|
|
208
|
+
* @return {string} Existing absolute cwd used for bootstrap work.
|
|
209
|
+
*/
|
|
210
|
+
function resolveLiveBootstrapCwd(cwd: string): string {
|
|
211
|
+
const normalizedCwd = path.resolve(cwd);
|
|
212
|
+
if (fs.existsSync(normalizedCwd)) {
|
|
213
|
+
return normalizedCwd;
|
|
214
|
+
}
|
|
215
|
+
const processCwd = getProcessCwdSafe();
|
|
216
|
+
const runtimeContextPath = getRuntimeContextPath(processCwd);
|
|
217
|
+
if (fs.existsSync(runtimeContextPath)) {
|
|
218
|
+
return runtimeContextPath;
|
|
219
|
+
}
|
|
220
|
+
const runtimeBasePath = getRuntimeBasePath(processCwd);
|
|
221
|
+
return fs.existsSync(runtimeBasePath) ? runtimeBasePath : processCwd;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* @brief Best-effort synchronizes one context `cwd` mirror with bootstrap reality.
|
|
226
|
+
* @details Applies the resolved live cwd to the supplied context when writable and ignores stale or read-only mirrors so command bootstrap can continue using authoritative filesystem probes. Runtime is O(1). Side effects are limited to optional `ctx.cwd` mutation.
|
|
227
|
+
* @param[in,out] ctx {{ cwd?: string }} Mutable context-like object.
|
|
228
|
+
* @param[in] cwd {string} Resolved live cwd.
|
|
229
|
+
* @return {void} No return value.
|
|
230
|
+
*/
|
|
231
|
+
function syncContextCwdMirror(ctx: { cwd?: string }, cwd: string): void {
|
|
232
|
+
if (ctx.cwd === cwd) {
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
try {
|
|
236
|
+
Reflect.set(ctx, "cwd", cwd);
|
|
237
|
+
} catch {
|
|
238
|
+
// Ignore stale or read-only context mirrors.
|
|
239
|
+
}
|
|
168
240
|
}
|
|
169
241
|
|
|
170
242
|
/**
|
|
@@ -194,188 +266,152 @@ function saveProjectConfig(cwd: string, config: UseReqConfig): void {
|
|
|
194
266
|
|
|
195
267
|
/**
|
|
196
268
|
* @brief Formats the current project config path for top-level menu display.
|
|
197
|
-
* @details Resolves `<base-path>/.pi-usereq
|
|
269
|
+
* @details Resolves `<base-path>/.pi-usereq.json` from the cwd-derived project base, reuses the shared runtime-path formatter, and rewrites a leading POSIX `$HOME` token to `~` for the `Show configuration` row only. Runtime is O(p) in path length. No external state is mutated.
|
|
198
270
|
* @param[in] cwd {string} Current working directory.
|
|
199
271
|
* @return {string} `~`-relative or absolute config path display value.
|
|
200
272
|
* @satisfies REQ-162
|
|
201
273
|
*/
|
|
202
274
|
function formatProjectConfigPathForMenu(cwd: string): string {
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
}
|
|
207
|
-
return displayPath.startsWith("$HOME/") ? `~/${displayPath.slice("$HOME/".length)}` : displayPath;
|
|
275
|
+
return formatRuntimePathForDisplay(
|
|
276
|
+
getProjectConfigPath(getProjectBase(cwd)),
|
|
277
|
+
);
|
|
208
278
|
}
|
|
209
279
|
|
|
210
280
|
/**
|
|
211
|
-
* @brief
|
|
212
|
-
* @details
|
|
213
|
-
* @param[in]
|
|
214
|
-
* @
|
|
215
|
-
* @
|
|
281
|
+
* @brief Builds the standardized terminal rows appended to every configuration menu.
|
|
282
|
+
* @details Returns the canonical value-less `Reset defaults` row so all configuration menus and descendant selector menus share the same terminal ordering contract without rendering `Save and close`. Runtime is O(1). No external state is mutated.
|
|
283
|
+
* @param[in] options {{ resetDefaultsDescription: string }} Menu-specific terminal-row metadata.
|
|
284
|
+
* @return {PiUsereqSettingsMenuChoice[]} Ordered terminal menu rows.
|
|
285
|
+
* @satisfies REQ-193
|
|
216
286
|
*/
|
|
217
|
-
function
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
.join("/")
|
|
229
|
-
.replace(/^\.?\/?/, "")
|
|
230
|
-
.replace(/\/+$/, "");
|
|
231
|
-
const excludedDirectoryPaths = [...new Set([
|
|
232
|
-
"tests/fixtures",
|
|
233
|
-
testsDirRel ? `${testsDirRel}/fixtures` : "fixtures",
|
|
234
|
-
])];
|
|
235
|
-
const selectedPaths = collectSourceFiles(selectionDirectoryPaths, projectBase)
|
|
236
|
-
.filter((filePath) => {
|
|
237
|
-
const relativePath = path.relative(projectBase, filePath).split(path.sep).join("/");
|
|
238
|
-
return !excludedDirectoryPaths.some((excludedDirectoryPath) => {
|
|
239
|
-
return relativePath === excludedDirectoryPath || relativePath.startsWith(`${excludedDirectoryPath}/`);
|
|
240
|
-
});
|
|
241
|
-
})
|
|
242
|
-
.map((filePath) => path.relative(projectBase, filePath).split(path.sep).join("/"));
|
|
243
|
-
return {
|
|
244
|
-
selectionDirectoryPaths,
|
|
245
|
-
excludedDirectoryPaths,
|
|
246
|
-
selectedPaths,
|
|
247
|
-
};
|
|
287
|
+
function buildTerminalSettingsMenuChoices(options: {
|
|
288
|
+
resetDefaultsDescription: string;
|
|
289
|
+
}): PiUsereqSettingsMenuChoice[] {
|
|
290
|
+
return [
|
|
291
|
+
{
|
|
292
|
+
id: "reset-defaults",
|
|
293
|
+
label: "Reset defaults",
|
|
294
|
+
value: "",
|
|
295
|
+
description: options.resetDefaultsDescription,
|
|
296
|
+
},
|
|
297
|
+
];
|
|
248
298
|
}
|
|
249
299
|
|
|
250
300
|
/**
|
|
251
|
-
* @brief
|
|
252
|
-
* @details
|
|
253
|
-
* @param[in] payload {TokenToolPayload} Structured token payload.
|
|
254
|
-
* @return {string} Newline-delimited execution diagnostics.
|
|
301
|
+
* @brief Describes one pending reset value change shown in confirmation menus.
|
|
302
|
+
* @details Stores the row label plus its previous and next values so reset-confirmation submenus can expose machine-readable and human-verifiable change previews. The interface is compile-time only and introduces no runtime cost.
|
|
255
303
|
*/
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
const errorLines = payload.files
|
|
261
|
-
.filter((entry) => entry.status === "error" && entry.error_message)
|
|
262
|
-
.map((entry) => `error: ${entry.canonical_path}: ${entry.error_message!}`);
|
|
263
|
-
return [...skippedLines, ...errorLines].join("\n");
|
|
304
|
+
interface ResetConfirmationChange {
|
|
305
|
+
label: string;
|
|
306
|
+
previousValue: string;
|
|
307
|
+
nextValue: string;
|
|
264
308
|
}
|
|
265
309
|
|
|
266
310
|
/**
|
|
267
|
-
* @brief
|
|
268
|
-
* @details
|
|
269
|
-
* @param[in]
|
|
270
|
-
* @
|
|
271
|
-
* @
|
|
311
|
+
* @brief Formats one reset-confirmation value pair for menu display.
|
|
312
|
+
* @details Serializes the previous and next values into a deterministic `previous -> next` preview string used by confirmation submenus. Runtime is O(n) in combined value length. No external state is mutated.
|
|
313
|
+
* @param[in] previousValue {string} Current persisted value.
|
|
314
|
+
* @param[in] nextValue {string} Candidate default value.
|
|
315
|
+
* @return {string} Rendered preview string.
|
|
272
316
|
*/
|
|
273
|
-
function
|
|
274
|
-
|
|
275
|
-
): {
|
|
276
|
-
content: Array<{ type: "text"; text: string }>;
|
|
277
|
-
details: TokenToolPayload & { execution: { code: number; stderr: string } };
|
|
278
|
-
} {
|
|
279
|
-
const details = {
|
|
280
|
-
summary: payload.summary,
|
|
281
|
-
files: payload.files,
|
|
282
|
-
execution: {
|
|
283
|
-
code: payload.summary.counted_file_count > 0 ? 0 : 1,
|
|
284
|
-
stderr: buildTokenToolExecutionStderr(payload),
|
|
285
|
-
},
|
|
286
|
-
};
|
|
287
|
-
return buildStructuredToolExecuteResult(details);
|
|
317
|
+
function formatResetConfirmationValue(previousValue: string, nextValue: string): string {
|
|
318
|
+
return `${previousValue} -> ${nextValue}`;
|
|
288
319
|
}
|
|
289
320
|
|
|
290
321
|
/**
|
|
291
|
-
* @brief Builds the
|
|
292
|
-
* @details
|
|
293
|
-
* @param[in]
|
|
294
|
-
* @
|
|
322
|
+
* @brief Builds the shared settings-menu choices for one reset-confirmation submenu.
|
|
323
|
+
* @details Renders each pending changed value as a disabled preview row, appends explicit approve and abort actions, and falls back to one disabled no-op row when no values would change. Runtime is O(n) in changed-value count. No external state is mutated.
|
|
324
|
+
* @param[in] changes {ResetConfirmationChange[]} Changed-value preview rows.
|
|
325
|
+
* @param[in] approveDescription {string} Description for the approve action.
|
|
326
|
+
* @param[in] abortDescription {string} Description for the abort action.
|
|
327
|
+
* @return {PiUsereqSettingsMenuChoice[]} Reset-confirmation submenu choices.
|
|
295
328
|
*/
|
|
296
|
-
function
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
/**
|
|
328
|
-
* @brief Builds the agent-oriented execute result returned by references tools.
|
|
329
|
-
* @details Mirrors the structured references payload into both the text `content` channel and the machine-readable `details` channel while isolating execution metadata under `execution`. Runtime is O(n) in payload size. No side effects occur.
|
|
330
|
-
* @param[in] payload {ReferenceToolPayload} Structured references payload.
|
|
331
|
-
* @return {{ content: Array<{ type: "text"; text: string }>; details: ReferenceToolPayload & { execution: { code: number; stderr: string } } }} References-tool execute result.
|
|
332
|
-
* @satisfies REQ-076, REQ-077, REQ-078, REQ-079, REQ-099, REQ-102
|
|
333
|
-
*/
|
|
334
|
-
function buildReferenceToolExecuteResult(
|
|
335
|
-
payload: ReferenceToolPayload,
|
|
336
|
-
): {
|
|
337
|
-
content: Array<{ type: "text"; text: string }>;
|
|
338
|
-
details: ReferenceToolPayload & { execution: { code: number; stderr: string } };
|
|
339
|
-
} {
|
|
340
|
-
const details = {
|
|
341
|
-
summary: payload.summary,
|
|
342
|
-
repository: payload.repository,
|
|
343
|
-
files: payload.files,
|
|
344
|
-
execution: {
|
|
345
|
-
code: payload.summary.analyzed_file_count > 0 ? 0 : 1,
|
|
346
|
-
stderr: buildReferenceToolExecutionStderr(payload),
|
|
329
|
+
function buildResetConfirmationChoices(
|
|
330
|
+
changes: ResetConfirmationChange[],
|
|
331
|
+
approveDescription: string,
|
|
332
|
+
abortDescription: string,
|
|
333
|
+
): PiUsereqSettingsMenuChoice[] {
|
|
334
|
+
const previewRows = changes.length > 0
|
|
335
|
+
? changes.map((change, index) => ({
|
|
336
|
+
id: `reset-preview:${index}`,
|
|
337
|
+
label: change.label,
|
|
338
|
+
value: formatResetConfirmationValue(change.previousValue, change.nextValue),
|
|
339
|
+
description: `Reset ${change.label} from ${change.previousValue} to ${change.nextValue}.`,
|
|
340
|
+
disabled: true,
|
|
341
|
+
labelTone: "dim" as const,
|
|
342
|
+
valueTone: "dim" as const,
|
|
343
|
+
}))
|
|
344
|
+
: [{
|
|
345
|
+
id: "reset-preview:none",
|
|
346
|
+
label: "No value changes",
|
|
347
|
+
value: "unchanged",
|
|
348
|
+
description: "Approving the reset leaves the current values unchanged.",
|
|
349
|
+
disabled: true,
|
|
350
|
+
labelTone: "dim" as const,
|
|
351
|
+
valueTone: "dim" as const,
|
|
352
|
+
}];
|
|
353
|
+
return [
|
|
354
|
+
...previewRows,
|
|
355
|
+
{
|
|
356
|
+
id: "reset-approve",
|
|
357
|
+
label: "Approve reset",
|
|
358
|
+
value: `${changes.length} changes`,
|
|
359
|
+
description: approveDescription,
|
|
347
360
|
},
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
* @brief Builds the agent-oriented execute result returned by compression tools.
|
|
354
|
-
* @details Mirrors the structured compression payload into both the text `content` channel and the machine-readable `details` channel while isolating execution metadata under `execution`. Runtime is O(n) in payload size. No side effects occur.
|
|
355
|
-
* @param[in] payload {CompressToolPayload} Structured compression payload.
|
|
356
|
-
* @return {{ content: Array<{ type: "text"; text: string }>; details: CompressToolPayload & { execution: { code: number; stderr: string } } }} Compression-tool execute result.
|
|
357
|
-
* @satisfies REQ-081, REQ-082, REQ-083, REQ-084, REQ-085, REQ-087, REQ-088, REQ-099, REQ-102
|
|
358
|
-
*/
|
|
359
|
-
function buildCompressionToolExecuteResult(
|
|
360
|
-
payload: CompressToolPayload,
|
|
361
|
-
): {
|
|
362
|
-
content: Array<{ type: "text"; text: string }>;
|
|
363
|
-
details: CompressToolPayload & { execution: { code: number; stderr: string } };
|
|
364
|
-
} {
|
|
365
|
-
const details = {
|
|
366
|
-
summary: payload.summary,
|
|
367
|
-
repository: payload.repository,
|
|
368
|
-
files: payload.files,
|
|
369
|
-
execution: {
|
|
370
|
-
code: payload.summary.compressed_file_count > 0 ? 0 : 1,
|
|
371
|
-
stderr: buildCompressToolExecutionStderr(payload),
|
|
361
|
+
{
|
|
362
|
+
id: "reset-abort",
|
|
363
|
+
label: "Abort reset",
|
|
364
|
+
value: "keep current",
|
|
365
|
+
description: abortDescription,
|
|
372
366
|
},
|
|
373
|
-
|
|
374
|
-
|
|
367
|
+
];
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* @brief Opens one explicit reset-confirmation submenu.
|
|
372
|
+
* @details Uses the shared settings-menu renderer to show every changed value before reset application and returns `true` only when the user selects the explicit approval action. Runtime depends on user interaction count. Side effects are limited to transient custom-UI rendering.
|
|
373
|
+
* @param[in] ctx {ExtensionCommandContext} Active command context.
|
|
374
|
+
* @param[in] title {string} Confirmation submenu title.
|
|
375
|
+
* @param[in] changes {ResetConfirmationChange[]} Changed-value preview rows.
|
|
376
|
+
* @param[in] approveDescription {string} Description for the approve action.
|
|
377
|
+
* @param[in] abortDescription {string} Description for the abort action.
|
|
378
|
+
* @return {Promise<boolean>} `true` when the reset is explicitly approved.
|
|
379
|
+
*/
|
|
380
|
+
async function confirmResetChanges(
|
|
381
|
+
ctx: ExtensionCommandContext,
|
|
382
|
+
title: string,
|
|
383
|
+
changes: ResetConfirmationChange[],
|
|
384
|
+
approveDescription: string,
|
|
385
|
+
abortDescription: string,
|
|
386
|
+
): Promise<boolean> {
|
|
387
|
+
const choice = await showPiUsereqSettingsMenu(
|
|
388
|
+
ctx,
|
|
389
|
+
title,
|
|
390
|
+
buildResetConfirmationChoices(changes, approveDescription, abortDescription),
|
|
391
|
+
);
|
|
392
|
+
return choice === "reset-approve";
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* @brief Writes the already-persisted project configuration file text into the editor.
|
|
397
|
+
* @details Reads the current `.pi-usereq.json` file content from disk after the caller has saved any pending configuration changes and forwards that exact persisted text into the editor. Runtime is O(n) in serialized config size. Side effects include filesystem reads and editor-text mutation.
|
|
398
|
+
* @param[in] ctx {ExtensionCommandContext} Active command context.
|
|
399
|
+
* @param[in] cwd {string} Current working directory.
|
|
400
|
+
* @param[in] _config {UseReqConfig} Unused effective project configuration retained for stable call-site shape.
|
|
401
|
+
* @return {void} No return value.
|
|
402
|
+
* @satisfies REQ-031
|
|
403
|
+
*/
|
|
404
|
+
function writePersistedProjectConfigToEditor(
|
|
405
|
+
ctx: ExtensionCommandContext,
|
|
406
|
+
cwd: string,
|
|
407
|
+
_config: UseReqConfig,
|
|
408
|
+
): void {
|
|
409
|
+
const projectBase = getProjectBase(cwd);
|
|
410
|
+
ctx.ui.setEditorText(fs.readFileSync(getProjectConfigPath(projectBase), "utf8"));
|
|
375
411
|
}
|
|
376
412
|
|
|
377
413
|
/**
|
|
378
|
-
* @brief Maps
|
|
414
|
+
* @brief Maps search-tool language identifiers to stable registration labels.
|
|
379
415
|
* @details Preserves the canonical capitalization used by tool descriptions so supported-tag guidance remains deterministic across inspection snapshots. Access complexity is O(1).
|
|
380
416
|
*/
|
|
381
417
|
const FIND_TOOL_LANGUAGE_LABELS: Record<string, string> = {
|
|
@@ -402,7 +438,7 @@ const FIND_TOOL_LANGUAGE_LABELS: Record<string, string> = {
|
|
|
402
438
|
};
|
|
403
439
|
|
|
404
440
|
/**
|
|
405
|
-
* @brief Defines the stable language order used by
|
|
441
|
+
* @brief Defines the stable language order used by search-tool supported-tag guidance.
|
|
406
442
|
* @details Keeps registration descriptions aligned with the repository-supported language matrix and preserves deterministic inspection snapshots. Access complexity is O(1).
|
|
407
443
|
*/
|
|
408
444
|
const FIND_TOOL_LANGUAGE_ORDER = [
|
|
@@ -429,103 +465,445 @@ const FIND_TOOL_LANGUAGE_ORDER = [
|
|
|
429
465
|
] as const;
|
|
430
466
|
|
|
431
467
|
/**
|
|
432
|
-
* @brief Builds the supported-tag guidance lines embedded in
|
|
468
|
+
* @brief Builds the supported-tag guidance lines embedded in search-tool registrations.
|
|
433
469
|
* @details Emits one deterministic line per supported language containing its canonical registration label and sorted tag list so downstream agents can specialize requests without invoking the tool first. Runtime is O(l * t log t). No side effects occur.
|
|
434
470
|
* @return {string[]} Supported-tag guidance lines.
|
|
435
471
|
*/
|
|
436
|
-
function
|
|
472
|
+
function buildSearchToolSupportedTagGuidelines(): string[] {
|
|
437
473
|
return FIND_TOOL_LANGUAGE_ORDER
|
|
438
474
|
.filter((language) => language in LANGUAGE_TAGS)
|
|
439
475
|
.map((language) => `Supported tags [${FIND_TOOL_LANGUAGE_LABELS[language]}]: ${[...LANGUAGE_TAGS[language]!].sort().join(", ")}`);
|
|
440
476
|
}
|
|
441
477
|
|
|
442
478
|
/**
|
|
443
|
-
* @brief Builds the schema description for one
|
|
444
|
-
* @details Specializes the
|
|
445
|
-
* @param[in] scope {FindToolScope}
|
|
479
|
+
* @brief Builds the schema description for one search-tool registration.
|
|
480
|
+
* @details Specializes the explicit-file and configured-directory input contracts while documenting the monolithic markdown output channel and minimal execution details shape. Runtime is O(1). No side effects occur.
|
|
481
|
+
* @param[in] scope {FindToolScope} Search-tool scope.
|
|
446
482
|
* @return {string} Parameter-schema description.
|
|
447
483
|
*/
|
|
448
|
-
function
|
|
484
|
+
function buildSearchToolSchemaDescription(scope: FindToolScope): string {
|
|
449
485
|
const inputContract = scope === "explicit-files"
|
|
450
486
|
? "Input contract: tag + pattern + files[] + optional enableLineNumbers."
|
|
451
487
|
: "Input contract: tag + pattern + optional enableLineNumbers. Scope is the configured src-dir list resolved from the current project configuration.";
|
|
452
|
-
return `${inputContract} Output contract:
|
|
488
|
+
return `${inputContract} Output contract: monolithic markdown in content[0].text plus details.execution diagnostics. Regex matches construct names only.`;
|
|
453
489
|
}
|
|
454
490
|
|
|
455
491
|
/**
|
|
456
|
-
* @brief Builds the prompt-guideline set for one
|
|
457
|
-
* @details Encodes scope selection, output
|
|
458
|
-
* @param[in] scope {FindToolScope}
|
|
492
|
+
* @brief Builds the prompt-guideline set for one search-tool registration.
|
|
493
|
+
* @details Encodes scope selection, monolithic markdown output semantics, regex semantics, line-number behavior, tag-filter rules, and the full language-to-tag matrix as stable agent-oriented strings. Runtime is O(l * t log t). No side effects occur.
|
|
494
|
+
* @param[in] scope {FindToolScope} Search-tool scope.
|
|
459
495
|
* @return {string[]} Prompt-guideline strings.
|
|
460
496
|
*/
|
|
461
|
-
function
|
|
497
|
+
function buildSearchToolPromptGuidelines(scope: FindToolScope): string[] {
|
|
462
498
|
const scopeLine = scope === "explicit-files"
|
|
463
499
|
? "Scope: explicit source files selected by files[]; caller order is preserved; each item may be project-relative or absolute."
|
|
464
500
|
: "Scope: resolve src-dir from the current project configuration and scan the configured source surface from the current working directory.";
|
|
465
|
-
const outputLine = scope === "explicit-files"
|
|
466
|
-
? "Output contract: summary + repository + files + execution. Repository exposes requested file scope only when it adds dynamic search context; supported tags remain documented in registration metadata. File entries expose status, line ranges, file_doxygen, and matches; match entries expose symbol_kind, signature_text, line ranges, code_lines, stripped_source_text, and structured Doxygen fields."
|
|
467
|
-
: "Output contract: summary + repository + files + execution. Repository exposes source_directory_paths and file_canonical_paths when project-scope search context varies; supported tags remain documented in registration metadata. File entries expose status, line ranges, file_doxygen, and matches; match entries expose symbol_kind, signature_text, line ranges, code_lines, stripped_source_text, and structured Doxygen fields.";
|
|
468
501
|
return [
|
|
469
502
|
scopeLine,
|
|
470
|
-
|
|
503
|
+
"Output contract: monolithic markdown in content[0].text; details.execution preserves only exit code and residual diagnostics.",
|
|
471
504
|
"Regex rule: pattern is applied to construct names only with JavaScript RegExp search semantics; it never matches construct bodies; use ^...$ for exact-name matching.",
|
|
472
|
-
"Tag rule: tag is pipe-separated and case-insensitive; unsupported tags are ignored
|
|
473
|
-
"Line-number behavior: enableLineNumbers
|
|
474
|
-
"Failure contract: invalid tag filters, invalid regex patterns, unsupported extensions, unsupported tag-language combinations, no-match files, and analysis failures
|
|
475
|
-
...
|
|
505
|
+
"Tag rule: tag is pipe-separated and case-insensitive; unsupported tags are ignored.",
|
|
506
|
+
"Line-number behavior: enableLineNumbers toggles original source line prefixes inside fenced code blocks.",
|
|
507
|
+
"Failure contract: invalid tag filters, invalid regex patterns, unsupported extensions, unsupported tag-language combinations, no-match files, and analysis failures surface through details.execution diagnostics.",
|
|
508
|
+
...buildSearchToolSupportedTagGuidelines(),
|
|
476
509
|
];
|
|
477
510
|
}
|
|
478
511
|
|
|
479
512
|
/**
|
|
480
|
-
* @brief
|
|
481
|
-
* @details
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
} {
|
|
492
|
-
const stderr = buildFindToolExecutionStderr(payload);
|
|
493
|
-
const details = {
|
|
494
|
-
summary: payload.summary,
|
|
495
|
-
repository: payload.repository,
|
|
496
|
-
files: payload.files,
|
|
497
|
-
execution: {
|
|
498
|
-
code: payload.summary.search_status === "matched" ? 0 : 1,
|
|
499
|
-
stderr,
|
|
500
|
-
},
|
|
513
|
+
* @brief Describes the monolithic tool-result surface consumed by tool-row renderers.
|
|
514
|
+
* @details Narrows execute-result data to the primary text content block plus the minimal `details.execution` metadata returned by monolithic tool wrappers. The alias is compile-time only and introduces no runtime cost.
|
|
515
|
+
*/
|
|
516
|
+
type MonolithicToolRenderResult = {
|
|
517
|
+
content?: Array<{ type?: string; text?: string }>;
|
|
518
|
+
details?: {
|
|
519
|
+
execution?: {
|
|
520
|
+
code?: number;
|
|
521
|
+
stderr_lines?: string[];
|
|
522
|
+
stderr?: string;
|
|
523
|
+
};
|
|
501
524
|
};
|
|
502
|
-
|
|
525
|
+
};
|
|
526
|
+
|
|
527
|
+
/**
|
|
528
|
+
* @brief Extracts the primary monolithic text block from one tool result.
|
|
529
|
+
* @details Returns the first text content block when present and falls back to an empty string when the tool emitted no LLM-facing content. Runtime is O(1). No external state is mutated.
|
|
530
|
+
* @param[in] result {MonolithicToolRenderResult} Tool result wrapper.
|
|
531
|
+
* @return {string} Primary monolithic content text.
|
|
532
|
+
*/
|
|
533
|
+
function getMonolithicToolText(result: MonolithicToolRenderResult): string {
|
|
534
|
+
const firstBlock = result.content?.find((entry) => entry?.type === "text" && typeof entry.text === "string");
|
|
535
|
+
return typeof firstBlock?.text === "string" ? firstBlock.text : "";
|
|
503
536
|
}
|
|
504
537
|
|
|
505
538
|
/**
|
|
506
|
-
* @brief
|
|
507
|
-
* @details
|
|
508
|
-
* @param[in]
|
|
539
|
+
* @brief Reads the first residual execution error string from one monolithic tool result.
|
|
540
|
+
* @details Prefers the first `stderr_lines` entry when present and otherwise falls back to the first line of `stderr`. Runtime is O(1) plus first-line split cost. No external state is mutated.
|
|
541
|
+
* @param[in] result {MonolithicToolRenderResult} Tool result wrapper.
|
|
542
|
+
* @return {string | undefined} First residual execution error string.
|
|
543
|
+
*/
|
|
544
|
+
function getMonolithicToolErrorText(result: MonolithicToolRenderResult): string | undefined {
|
|
545
|
+
const stderrLines = result.details?.execution?.stderr_lines;
|
|
546
|
+
if (Array.isArray(stderrLines) && stderrLines.length > 0 && typeof stderrLines[0] === "string") {
|
|
547
|
+
return stderrLines[0];
|
|
548
|
+
}
|
|
549
|
+
const stderrText = result.details?.execution?.stderr;
|
|
550
|
+
if (typeof stderrText !== "string" || stderrText === "") {
|
|
551
|
+
return undefined;
|
|
552
|
+
}
|
|
553
|
+
return stderrText.split(/\r?\n/)[0] || undefined;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
/**
|
|
557
|
+
* @brief Formats one scalar or structural tool argument for compact render summaries.
|
|
558
|
+
* @details Truncates long strings, compresses arrays into short previews, and renders plain object arguments as key indexes so collapsed tool rows stay compact while still exposing the essential invocation shape. Runtime is O(n) in preview size. No external state is mutated.
|
|
559
|
+
* @param[in] value {unknown} Candidate tool argument value.
|
|
560
|
+
* @return {string | undefined} Compact preview string or `undefined` when the value carries no useful summary.
|
|
561
|
+
*/
|
|
562
|
+
function formatCompactToolArgumentValue(value: unknown): string | undefined {
|
|
563
|
+
if (value === undefined || value === null) {
|
|
564
|
+
return undefined;
|
|
565
|
+
}
|
|
566
|
+
if (typeof value === "string") {
|
|
567
|
+
const compactText = value.length > 28 ? `${value.slice(0, 25)}...` : value;
|
|
568
|
+
return JSON.stringify(compactText);
|
|
569
|
+
}
|
|
570
|
+
if (typeof value === "number" || typeof value === "bigint") {
|
|
571
|
+
return String(value);
|
|
572
|
+
}
|
|
573
|
+
if (typeof value === "boolean") {
|
|
574
|
+
return value ? "true" : "false";
|
|
575
|
+
}
|
|
576
|
+
if (Array.isArray(value)) {
|
|
577
|
+
if (value.length === 0) {
|
|
578
|
+
return undefined;
|
|
579
|
+
}
|
|
580
|
+
const renderedItems = value
|
|
581
|
+
.slice(0, 2)
|
|
582
|
+
.map((item) => formatCompactToolArgumentValue(item))
|
|
583
|
+
.filter((item): item is string => typeof item === "string");
|
|
584
|
+
if (renderedItems.length === 0) {
|
|
585
|
+
return `[${value.length}]`;
|
|
586
|
+
}
|
|
587
|
+
return value.length <= 2
|
|
588
|
+
? `[${renderedItems.join(", ")}]`
|
|
589
|
+
: `[${renderedItems.join(", ")}, ...+${value.length - 2}]`;
|
|
590
|
+
}
|
|
591
|
+
if (typeof value === "object") {
|
|
592
|
+
const keys = Object.keys(value as Record<string, unknown>);
|
|
593
|
+
if (keys.length === 0) {
|
|
594
|
+
return undefined;
|
|
595
|
+
}
|
|
596
|
+
return keys.length <= 2
|
|
597
|
+
? `{${keys.join(",")}}`
|
|
598
|
+
: `{${keys.slice(0, 2).join(",")},...+${keys.length - 2}}`;
|
|
599
|
+
}
|
|
600
|
+
return undefined;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
/**
|
|
604
|
+
* @brief Builds the compact invocation summary appended to collapsed tool rows.
|
|
605
|
+
* @details Renders only caller-supplied parameters that have stable, non-empty compact previews and joins them in insertion order so agents can infer how the tool was used without expanding the full result. Runtime is O(n) in argument count and preview size. No external state is mutated.
|
|
606
|
+
* @param[in] args {Record<string, unknown> | undefined} Current tool call arguments.
|
|
607
|
+
* @return {string} Compact invocation summary prefixed with one separating space, or the empty string when no useful preview exists.
|
|
608
|
+
*/
|
|
609
|
+
function buildCompactToolInvocationText(args: Record<string, unknown> | undefined): string {
|
|
610
|
+
if (!args) {
|
|
611
|
+
return "";
|
|
612
|
+
}
|
|
613
|
+
const entries = Object.entries(args)
|
|
614
|
+
.map(([key, value]) => {
|
|
615
|
+
const renderedValue = formatCompactToolArgumentValue(value);
|
|
616
|
+
return renderedValue === undefined ? undefined : `${key}=${renderedValue}`;
|
|
617
|
+
})
|
|
618
|
+
.filter((entry): entry is string => typeof entry === "string");
|
|
619
|
+
return entries.length === 0 ? "" : ` ${entries.join(" · ")}`;
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
/**
|
|
623
|
+
* @brief Builds the compact default text for one monolithic tool result row.
|
|
624
|
+
* @details Prefers the tool name, compact invocation preview, and success marker for collapsed rows, and falls back to residual execution diagnostics when the tool failed before completing successfully. Runtime is O(n) in compact argument-preview size. No external state is mutated.
|
|
625
|
+
* @param[in] toolName {string} Registered tool name.
|
|
626
|
+
* @param[in] result {MonolithicToolRenderResult} Tool result wrapper.
|
|
627
|
+
* @param[in] args {Record<string, unknown> | undefined} Current tool call arguments.
|
|
628
|
+
* @return {string} Compact single-line summary.
|
|
629
|
+
*/
|
|
630
|
+
function summarizeStructuredToolResult(
|
|
631
|
+
toolName: string,
|
|
632
|
+
result: MonolithicToolRenderResult,
|
|
633
|
+
args?: Record<string, unknown>,
|
|
634
|
+
): string {
|
|
635
|
+
const success = result.details?.execution?.code === undefined
|
|
636
|
+
? true
|
|
637
|
+
: result.details.execution.code === 0;
|
|
638
|
+
const prefix = success ? "✓" : "✗";
|
|
639
|
+
const invocationText = buildCompactToolInvocationText(args);
|
|
640
|
+
const errorText = getMonolithicToolErrorText(result);
|
|
641
|
+
if (!success && errorText) {
|
|
642
|
+
return `${prefix} ${toolName}${invocationText}: ${errorText}`;
|
|
643
|
+
}
|
|
644
|
+
return `${prefix} ${toolName}${invocationText}`;
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
/**
|
|
648
|
+
* @brief Builds a custom `renderResult` implementation for one monolithic tool.
|
|
649
|
+
* @details Reuses a mutable `Text` component when possible, keeps the default collapsed row compact with essential invocation parameters plus result status, and reveals the full monolithic content only when the tool row is expanded. Runtime is O(n) in expanded content length and compact argument-preview size. No external state is mutated.
|
|
650
|
+
* @param[in] toolName {string} Registered tool name.
|
|
651
|
+
* @return {(result: MonolithicToolRenderResult, options: { expanded?: boolean; isPartial?: boolean }, _theme: unknown, context: { args?: Record<string, unknown>; lastComponent?: unknown }) => Text} Custom result renderer.
|
|
652
|
+
* @satisfies REQ-210
|
|
653
|
+
*/
|
|
654
|
+
function buildStructuredToolRenderResult(toolName: string) {
|
|
655
|
+
return (
|
|
656
|
+
result: MonolithicToolRenderResult,
|
|
657
|
+
options: { expanded?: boolean; isPartial?: boolean },
|
|
658
|
+
_theme: unknown,
|
|
659
|
+
context: { args?: Record<string, unknown>; lastComponent?: unknown },
|
|
660
|
+
): Text => {
|
|
661
|
+
const textComponent = context.lastComponent instanceof Text
|
|
662
|
+
? context.lastComponent
|
|
663
|
+
: new Text("", 0, 0);
|
|
664
|
+
if (options.isPartial) {
|
|
665
|
+
textComponent.setText(`… ${toolName}`);
|
|
666
|
+
return textComponent;
|
|
667
|
+
}
|
|
668
|
+
const compactText = summarizeStructuredToolResult(toolName, result, context.args);
|
|
669
|
+
const fullText = getMonolithicToolText(result);
|
|
670
|
+
textComponent.setText(options.expanded && fullText !== "" ? fullText : compactText);
|
|
671
|
+
return textComponent;
|
|
672
|
+
};
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
/**
|
|
676
|
+
* @brief Executes one CLI-style runner for a monolithic agent tool.
|
|
677
|
+
* @details Reuses the standalone tool-runner contract, normalizes thrown failures into `ToolResult`, and wraps the selected stdout or stderr text into the monolithic content channel. Runtime is dominated by the delegated runner. Side effects depend on the selected tool.
|
|
678
|
+
* @param[in] operation {() => ToolResult} Runner callback.
|
|
679
|
+
* @return {ReturnType<typeof buildMonolithicToolExecuteResult>} Monolithic tool execute result.
|
|
680
|
+
*/
|
|
681
|
+
function executeMonolithicTool(operation: () => ToolResult): ReturnType<typeof buildMonolithicToolExecuteResult> {
|
|
682
|
+
try {
|
|
683
|
+
return buildMonolithicToolExecuteResult(operation());
|
|
684
|
+
} catch (error) {
|
|
685
|
+
return buildMonolithicToolExecuteResult(normalizeToolFailure(error));
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
/**
|
|
690
|
+
* @brief Starts delivery of one rendered prompt into the current active session.
|
|
691
|
+
* @details Prefers the replacement-session `sendUserMessage(...)` helper exposed by `withSession(...)` callbacks after session replacement so post-switch prompt delivery never reuses stale pre-switch session-bound extension objects. Returns the underlying delivery promise without awaiting it so callers can record the `running` workflow transition as soon as prompt handoff is accepted instead of waiting for the full agent turn to complete on runtimes whose async replacement-session helpers resolve only after `agent_end`. When pi later invalidates that replacement-session context during successful prompt-end restoration, the helper suppresses the documented stale-extension-context rejection because the prompt was already accepted and late rethrow would surface a false orchestration failure. Falls back to `pi.sendUserMessage(...)` only for non-replacement flows or runtimes that do not expose replacement-session helpers. Runtime is O(n) in prompt length. Side effects are limited to user-message delivery.
|
|
692
|
+
* @param[in] pi {ExtensionAPI} Handler-scoped extension API instance retained as the fallback dispatcher.
|
|
509
693
|
* @param[in] content {string} Rendered prompt markdown.
|
|
510
|
-
* @
|
|
511
|
-
* @
|
|
694
|
+
* @param[in] context {unknown} Optional replacement-session helper context.
|
|
695
|
+
* @return {Promise<void>} Promise representing eventual prompt-delivery completion.
|
|
696
|
+
* @satisfies REQ-004, REQ-067, REQ-068, REQ-227, REQ-281
|
|
512
697
|
*/
|
|
513
|
-
|
|
698
|
+
function deliverPromptCommand(
|
|
699
|
+
pi: ExtensionAPI,
|
|
700
|
+
content: string,
|
|
701
|
+
context?: unknown,
|
|
702
|
+
): Promise<void> {
|
|
703
|
+
const replacementContext = context as {
|
|
704
|
+
sendUserMessage?: (message: string) => Promise<void> | void;
|
|
705
|
+
} | undefined;
|
|
706
|
+
if (typeof replacementContext?.sendUserMessage === "function") {
|
|
707
|
+
return Promise.resolve(replacementContext.sendUserMessage(content)).catch((error) => {
|
|
708
|
+
if (isStaleExtensionContextError(error)) {
|
|
709
|
+
return;
|
|
710
|
+
}
|
|
711
|
+
throw error;
|
|
712
|
+
});
|
|
713
|
+
}
|
|
514
714
|
pi.sendUserMessage(content);
|
|
715
|
+
return Promise.resolve();
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
/**
|
|
719
|
+
* @brief Detects prompt-delivery failures that can be ignored after prompt ownership has moved past the command handler.
|
|
720
|
+
* @details Matches the documented stale-extension-context runtime error once prompt ownership has already moved beyond command-side preflight. The helper treats the failure as ignorable when the persisted prompt runtime state shows the same execution session as the active prompt run or when the persisted workflow state has already advanced beyond `checking|running`, because rethrowing at that point would incorrectly re-enter command-side abort logic after the prompt was already accepted. Runtime is O(n) in error-message length plus path length. No external state is mutated.
|
|
721
|
+
* @param[in] error {unknown} Candidate prompt-delivery failure.
|
|
722
|
+
* @param[in] workflowState {import("./core/extension-status.js").PiUsereqWorkflowState} Current shared prompt workflow state.
|
|
723
|
+
* @param[in] executionPlan {PromptCommandExecutionPlan | undefined} Prepared execution plan for the current prompt command.
|
|
724
|
+
* @return {boolean} `true` when the failure is a late stale-context delivery rejection that MUST be ignored.
|
|
725
|
+
* @satisfies REQ-208, REQ-280, REQ-281, REQ-282
|
|
726
|
+
*/
|
|
727
|
+
function shouldIgnoreLatePromptDeliveryFailure(
|
|
728
|
+
error: unknown,
|
|
729
|
+
workflowState: import("./core/extension-status.js").PiUsereqWorkflowState,
|
|
730
|
+
executionPlan?: PromptCommandExecutionPlan,
|
|
731
|
+
): boolean {
|
|
732
|
+
const persistedRuntimeState = readPersistedPromptCommandRuntimeState();
|
|
733
|
+
const effectiveWorkflowState = persistedRuntimeState.workflowState || workflowState;
|
|
734
|
+
const activeExecutionSessionFile = persistedRuntimeState.activePromptRequest?.executionSessionFile;
|
|
735
|
+
const promptRunAlreadyActive = executionPlan !== undefined
|
|
736
|
+
&& typeof activeExecutionSessionFile === "string"
|
|
737
|
+
&& path.resolve(activeExecutionSessionFile) === path.resolve(executionPlan.executionSessionFile);
|
|
738
|
+
return isStaleExtensionContextError(error)
|
|
739
|
+
&& (
|
|
740
|
+
promptRunAlreadyActive
|
|
741
|
+
|| (effectiveWorkflowState !== "checking" && effectiveWorkflowState !== "running")
|
|
742
|
+
);
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
/**
|
|
746
|
+
* @brief Appends one workflow-state debug entry for a bundled prompt when selected.
|
|
747
|
+
* @details Reuses the shared debug logger so `req-*` command handlers and prompt-end orchestration can record deterministic workflow transitions without duplicating JSON payload shaping. Runtime is O(n) in serialized payload size only when logging is enabled and O(1) otherwise. Side effects include debug-log file writes for matching enabled prompts.
|
|
748
|
+
* @param[in] projectBase {string} Absolute original project base path.
|
|
749
|
+
* @param[in] config {UseReqConfig} Effective project configuration.
|
|
750
|
+
* @param[in] promptName {import("./core/prompt-command-catalog.js").PromptCommandName} Bundled prompt name.
|
|
751
|
+
* @param[in] previousState {string} Prior workflow state.
|
|
752
|
+
* @param[in] nextState {import("./core/debug-runtime.js").DebugWorkflowState} Next workflow state.
|
|
753
|
+
* @return {void} No return value.
|
|
754
|
+
* @satisfies REQ-245, REQ-246, REQ-247
|
|
755
|
+
*/
|
|
756
|
+
function logPromptWorkflowStateChange(
|
|
757
|
+
projectBase: string,
|
|
758
|
+
config: UseReqConfig,
|
|
759
|
+
promptName: import("./core/prompt-command-catalog.js").PromptCommandName,
|
|
760
|
+
previousState: string,
|
|
761
|
+
nextState: import("./core/debug-runtime.js").DebugWorkflowState,
|
|
762
|
+
): void {
|
|
763
|
+
if (!shouldLogDebugPromptWorkflowState(config, nextState, promptName)) {
|
|
764
|
+
return;
|
|
765
|
+
}
|
|
766
|
+
logDebugPromptEvent(
|
|
767
|
+
projectBase,
|
|
768
|
+
config,
|
|
769
|
+
nextState,
|
|
770
|
+
promptName,
|
|
771
|
+
"workflow_state",
|
|
772
|
+
{ from_state: previousState, to_state: nextState },
|
|
773
|
+
{ success: true, workflow_state: nextState },
|
|
774
|
+
);
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
/**
|
|
778
|
+
* @brief Appends one dedicated prompt workflow debug entry when selected.
|
|
779
|
+
* @details Reuses the shared workflow-event logger so prompt activation, restoration, closure, and session-shutdown paths can emit higher-granularity orchestration diagnostics without duplicating JSON payload shaping. Runtime is O(n) in serialized payload size only when logging is enabled and O(1) otherwise. Side effects include debug-log file writes for matching enabled prompts.
|
|
780
|
+
* @param[in] projectBase {string} Absolute original project base path.
|
|
781
|
+
* @param[in] config {UseReqConfig} Effective project configuration.
|
|
782
|
+
* @param[in] workflowState {DebugWorkflowState} Current workflow state.
|
|
783
|
+
* @param[in] promptName {import("./core/prompt-command-catalog.js").PromptCommandName} Bundled prompt name.
|
|
784
|
+
* @param[in] action {string} Workflow debug action identifier.
|
|
785
|
+
* @param[in] input {unknown} Optional workflow debug input payload.
|
|
786
|
+
* @param[in] result {unknown} Optional workflow debug result payload.
|
|
787
|
+
* @param[in] isError {boolean} Workflow debug error flag.
|
|
788
|
+
* @return {void} No return value.
|
|
789
|
+
* @satisfies REQ-245, REQ-246, REQ-247, REQ-277
|
|
790
|
+
*/
|
|
791
|
+
function logPromptWorkflowEvent(
|
|
792
|
+
projectBase: string,
|
|
793
|
+
config: UseReqConfig,
|
|
794
|
+
workflowState: DebugWorkflowState,
|
|
795
|
+
promptName: import("./core/prompt-command-catalog.js").PromptCommandName,
|
|
796
|
+
action: string,
|
|
797
|
+
input?: unknown,
|
|
798
|
+
result?: unknown,
|
|
799
|
+
isError = false,
|
|
800
|
+
): void {
|
|
801
|
+
logDebugPromptWorkflowEvent(
|
|
802
|
+
projectBase,
|
|
803
|
+
config,
|
|
804
|
+
workflowState,
|
|
805
|
+
promptName,
|
|
806
|
+
action,
|
|
807
|
+
input,
|
|
808
|
+
result,
|
|
809
|
+
isError,
|
|
810
|
+
);
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
/**
|
|
814
|
+
* @brief Transitions one prompt workflow state and logs the transition immediately after the state update.
|
|
815
|
+
* @details Captures the previous workflow state, applies the new state through the shared status helper, and appends the gated `workflow_state` debug entry only after the transition has completed. Runtime is O(1). Side effects include status mutation, status-bar rendering, and optional debug-log writes.
|
|
816
|
+
* @param[in,out] statusController {PiUsereqStatusController} Mutable status controller.
|
|
817
|
+
* @param[in] ctx {ExtensionContext | ExtensionCommandContext} Active extension context.
|
|
818
|
+
* @param[in] projectBase {string} Absolute original project base path.
|
|
819
|
+
* @param[in] config {UseReqConfig} Effective project configuration.
|
|
820
|
+
* @param[in] promptName {import("./core/prompt-command-catalog.js").PromptCommandName} Bundled prompt name.
|
|
821
|
+
* @param[in] nextState {DebugWorkflowState} Next workflow state.
|
|
822
|
+
* @return {void} No return value.
|
|
823
|
+
*/
|
|
824
|
+
function transitionPromptWorkflowState(
|
|
825
|
+
statusController: PiUsereqStatusController,
|
|
826
|
+
ctx: ExtensionContext | ExtensionCommandContext,
|
|
827
|
+
projectBase: string,
|
|
828
|
+
config: UseReqConfig,
|
|
829
|
+
promptName: import("./core/prompt-command-catalog.js").PromptCommandName,
|
|
830
|
+
nextState: DebugWorkflowState,
|
|
831
|
+
): void {
|
|
832
|
+
const previousState = statusController.state.workflowState;
|
|
833
|
+
setPiUsereqWorkflowState(statusController, nextState, ctx);
|
|
834
|
+
if (previousState !== nextState) {
|
|
835
|
+
logPromptWorkflowStateChange(projectBase, config, promptName, previousState, nextState);
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
/**
|
|
840
|
+
* @brief Resolves the runtime slash-command description for one bundled prompt.
|
|
841
|
+
* @details Reads the bundled prompt front matter, extracts its normalized `description` field, and falls back to the historical generated label when the prompt metadata omits a description. Runtime is O(n) in prompt length. Side effects are limited to filesystem reads.
|
|
842
|
+
* @param[in] promptName {import("./core/prompt-command-catalog.js").PromptCommandName} Bundled prompt name.
|
|
843
|
+
* @return {string} Runtime command description.
|
|
844
|
+
*/
|
|
845
|
+
function resolvePromptCommandDescription(
|
|
846
|
+
promptName: import("./core/prompt-command-catalog.js").PromptCommandName,
|
|
847
|
+
): string {
|
|
848
|
+
return readBundledPromptDescription(promptName) || `Run pi-usereq prompt ${promptName}`;
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
/**
|
|
852
|
+
* @brief Resolves the original project base used for debug-log file writes.
|
|
853
|
+
* @details Prefers the active or pending prompt execution plan so tool-result logging during worktree-backed prompt runs persists into the original repository path instead of transient worktree directories. Runtime is O(1). No external state is mutated.
|
|
854
|
+
* @param[in] cwd {string} Current extension working directory.
|
|
855
|
+
* @param[in] statusController {PiUsereqStatusController} Mutable status controller.
|
|
856
|
+
* @return {string} Absolute original project base path for debug logging.
|
|
857
|
+
*/
|
|
858
|
+
function resolveDebugProjectBase(cwd: string, statusController: PiUsereqStatusController): string {
|
|
859
|
+
return statusController.state.activePromptRequest?.basePath
|
|
860
|
+
?? statusController.state.pendingPromptRequest?.basePath
|
|
861
|
+
?? getProjectBase(resolveLiveBootstrapCwd(cwd));
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
/**
|
|
865
|
+
* @brief Delivers one best-effort UI notification without failing on stale replacement contexts.
|
|
866
|
+
* @details Attempts to use the supplied extension context for UI notification delivery and suppresses the documented stale-extension-context runtime error raised after session replacement, because prompt-orchestration closure can outlive the context that initiated the switch. Runtime is O(n) in message length. Side effects are limited to user notification delivery when the context is still active.
|
|
867
|
+
* @param[in] ctx {ExtensionContext | ExtensionCommandContext | undefined} Candidate UI context.
|
|
868
|
+
* @param[in] message {string} Notification message.
|
|
869
|
+
* @param[in] level {"info" | "error"} Notification severity.
|
|
870
|
+
* @return {boolean} `true` when the notification was delivered and `false` when the context was already stale.
|
|
871
|
+
* @satisfies REQ-280
|
|
872
|
+
*/
|
|
873
|
+
function notifyContextSafely(
|
|
874
|
+
ctx: (ExtensionContext | ExtensionCommandContext) | undefined,
|
|
875
|
+
message: string,
|
|
876
|
+
level: "info" | "error",
|
|
877
|
+
): boolean {
|
|
878
|
+
if (ctx === undefined) {
|
|
879
|
+
return false;
|
|
880
|
+
}
|
|
881
|
+
try {
|
|
882
|
+
ctx.ui.notify(message, level);
|
|
883
|
+
return true;
|
|
884
|
+
} catch (error) {
|
|
885
|
+
if (isStaleExtensionContextError(error)) {
|
|
886
|
+
return false;
|
|
887
|
+
}
|
|
888
|
+
throw error;
|
|
889
|
+
}
|
|
515
890
|
}
|
|
516
891
|
|
|
517
892
|
/**
|
|
518
893
|
* @brief Returns the configurable active-tool inventory visible to the extension.
|
|
519
|
-
* @details Filters runtime tools against the canonical configurable-tool set,
|
|
894
|
+
* @details Filters runtime tools against the canonical configurable-tool set, keeps only builtin-backed embedded tools, and orders the result by the documented custom/files/embedded/default-disabled grouping. Runtime is O(t log t). No external state is mutated.
|
|
520
895
|
* @param[in] pi {ExtensionAPI} Active extension API instance.
|
|
521
896
|
* @return {ToolInfo[]} Sorted configurable tool descriptors.
|
|
522
|
-
* @satisfies REQ-007, REQ-063
|
|
897
|
+
* @satisfies REQ-007, REQ-063, REQ-231, REQ-232
|
|
523
898
|
*/
|
|
524
899
|
function getPiUsereqStartupTools(pi: ExtensionAPI): ToolInfo[] {
|
|
525
900
|
return pi.getAllTools()
|
|
526
901
|
.filter((tool) => PI_USEREQ_STARTUP_TOOL_SET.has(tool.name))
|
|
527
902
|
.filter((tool) => !isPiUsereqEmbeddedToolName(tool.name) || tool.sourceInfo?.source === "builtin")
|
|
528
|
-
.sort((left, right) =>
|
|
903
|
+
.sort((left, right) => comparePiUsereqStartupToolNames(
|
|
904
|
+
left.name as PiUsereqStartupToolName,
|
|
905
|
+
right.name as PiUsereqStartupToolName,
|
|
906
|
+
));
|
|
529
907
|
}
|
|
530
908
|
|
|
531
909
|
/**
|
|
@@ -569,22 +947,14 @@ function applyConfiguredPiUsereqTools(pi: ExtensionAPI, config: UseReqConfig): v
|
|
|
569
947
|
|
|
570
948
|
/**
|
|
571
949
|
* @brief Handles one intercepted pi lifecycle hook for pi-usereq status updates.
|
|
572
|
-
* @details Applies session-start-specific resource validation, project-config
|
|
573
|
-
* refresh, and startup-tool enablement before forwarding the originating hook
|
|
574
|
-
* name and payload into the shared `updateExtensionStatus(...)` pipeline.
|
|
575
|
-
* On `agent_end`, also dispatches configured command-notify, sound, and
|
|
576
|
-
* prompt-specific Pushover effects when the current run originates from a
|
|
577
|
-
* bundled prompt command. Runtime is dominated by configuration loading during
|
|
578
|
-
* `session_start`; all other hooks are O(1). Side effects include resource
|
|
579
|
-
* checks, active-tool mutation, status updates, live-ticker disposal on
|
|
580
|
-
* shutdown, optional child-process spawning, and outbound HTTPS requests.
|
|
950
|
+
* @details Applies session-start-specific resource validation, project-config refresh, startup-tool enablement, and selected debug-tool logging before forwarding the originating hook name and payload into the shared `updateExtensionStatus(...)` pipeline. Before `agent_start`, re-verifies any prepared prompt execution session switch. On `agent_end`, dispatches configured command-notify, sound, and prompt-specific Pushover effects, logs dedicated workflow-closure diagnostics, restores the original session-backed `base-path` for every matched worktree-backed completion by reusing persisted replacement-session command contexts when event contexts omit `switchSession()`, merges and deletes the worktree only for matched successful completions, tolerates stale replacement-session notification contexts after session replacement, retains the worktree plus notifies closure failure for interrupted or failed outcomes, logs selected prompt workflow transitions, and transitions workflow state through `merging`, `error`, and `idle` as required. On `session_shutdown`, captures pre-update prompt snapshots so workflow-shutdown diagnostics and same-runtime command continuation preserve the active prompt workflow state across switch-triggered rebinding, then disposes the shared controller. Runtime is dominated by configuration loading during `session_start` and git finalization during matched successful `agent_end` handling; all other hooks are O(1). Side effects include resource checks, active-tool mutation, active-session replacement, status updates, live-ticker disposal on shutdown, optional child-process spawning, outbound HTTPS requests, branch merges, worktree deletion, and optional debug-log writes.
|
|
581
951
|
* @param[in] pi {ExtensionAPI} Active extension API instance.
|
|
582
952
|
* @param[in,out] statusController {PiUsereqStatusController} Mutable status controller.
|
|
583
953
|
* @param[in] hookName {PiUsereqStatusHookName} Intercepted hook name.
|
|
584
954
|
* @param[in] event {unknown} Hook payload forwarded by pi.
|
|
585
955
|
* @param[in] ctx {ExtensionContext} Active extension context.
|
|
586
956
|
* @return {Promise<void>} Promise resolved when hook processing completes.
|
|
587
|
-
* @satisfies REQ-117, REQ-118, REQ-119, REQ-131, REQ-132, REQ-133, REQ-166, REQ-167, REQ-168, REQ-169, REQ-172, REQ-176, REQ-178, REQ-184, REQ-185, REQ-186, REQ-187
|
|
957
|
+
* @satisfies REQ-117, REQ-118, REQ-119, REQ-131, REQ-132, REQ-133, REQ-166, REQ-167, REQ-168, REQ-169, REQ-172, REQ-176, REQ-178, REQ-184, REQ-185, REQ-186, REQ-187, REQ-208, REQ-209, REQ-221, REQ-228, REQ-229, REQ-230, REQ-244, REQ-245, REQ-246, REQ-247, REQ-276, REQ-277, REQ-278, REQ-279, REQ-280
|
|
588
958
|
*/
|
|
589
959
|
async function handleExtensionStatusEvent(
|
|
590
960
|
pi: ExtensionAPI,
|
|
@@ -593,23 +963,65 @@ async function handleExtensionStatusEvent(
|
|
|
593
963
|
event: unknown,
|
|
594
964
|
ctx: ExtensionContext,
|
|
595
965
|
): Promise<void> {
|
|
596
|
-
const
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
?
|
|
600
|
-
promptName: statusController.state.activePromptRequest.promptName,
|
|
601
|
-
promptArgs: statusController.state.activePromptRequest.promptArgs,
|
|
602
|
-
basePath: path.resolve(ctx.cwd),
|
|
603
|
-
completionTimeMs: Math.max(0, Date.now() - statusController.state.runStartTimeMs),
|
|
604
|
-
}
|
|
966
|
+
const pendingPromptRequest = statusController.state.pendingPromptRequest;
|
|
967
|
+
const activePromptRequest = statusController.state.activePromptRequest;
|
|
968
|
+
const shutdownPromptRequest = hookName === "session_shutdown"
|
|
969
|
+
? activePromptRequest ?? pendingPromptRequest
|
|
605
970
|
: undefined;
|
|
971
|
+
const preservePromptCommandStateOnShutdown = hookName === "session_shutdown"
|
|
972
|
+
? shouldPreservePromptCommandStateOnShutdown(statusController.state)
|
|
973
|
+
: false;
|
|
606
974
|
if (hookName === "session_start") {
|
|
607
975
|
ensureBundledResourcesAccessible();
|
|
608
|
-
const
|
|
976
|
+
const startupCwd = resolveLiveBootstrapCwd(ctx.cwd);
|
|
977
|
+
syncContextCwdMirror(ctx, startupCwd);
|
|
978
|
+
bootstrapRuntimePathState(startupCwd, {
|
|
979
|
+
gitPath: resolveRuntimeGitPath(startupCwd),
|
|
980
|
+
});
|
|
981
|
+
const config = loadProjectConfig(startupCwd);
|
|
609
982
|
applyConfiguredPiUsereqTools(pi, config);
|
|
610
983
|
setPiUsereqStatusConfig(statusController, config);
|
|
611
984
|
}
|
|
985
|
+
if (hookName === "before_agent_start") {
|
|
986
|
+
const requestForActivation = activePromptRequest ?? pendingPromptRequest;
|
|
987
|
+
if (requestForActivation !== undefined) {
|
|
988
|
+
await activatePromptCommandExecution(requestForActivation, ctx);
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
const notifyRequest: PiNotifyEventRequest | undefined = hookName === "agent_end"
|
|
992
|
+
&& activePromptRequest !== undefined
|
|
993
|
+
&& statusController.state.runStartTimeMs !== undefined
|
|
994
|
+
? {
|
|
995
|
+
promptName: activePromptRequest.promptName,
|
|
996
|
+
promptArgs: activePromptRequest.promptArgs,
|
|
997
|
+
basePath: activePromptRequest.basePath,
|
|
998
|
+
completionTimeMs: Math.max(0, Date.now() - statusController.state.runStartTimeMs),
|
|
999
|
+
}
|
|
1000
|
+
: undefined;
|
|
612
1001
|
updateExtensionStatus(statusController, hookName, event, ctx);
|
|
1002
|
+
if (hookName === "tool_result" && statusController.config) {
|
|
1003
|
+
const toolEvent = event as {
|
|
1004
|
+
toolName?: string;
|
|
1005
|
+
input?: unknown;
|
|
1006
|
+
content?: unknown;
|
|
1007
|
+
details?: unknown;
|
|
1008
|
+
isError?: boolean;
|
|
1009
|
+
};
|
|
1010
|
+
if (typeof toolEvent.toolName === "string") {
|
|
1011
|
+
logDebugToolExecution(
|
|
1012
|
+
resolveDebugProjectBase(ctx.cwd, statusController),
|
|
1013
|
+
statusController.config,
|
|
1014
|
+
statusController.state.workflowState,
|
|
1015
|
+
toolEvent.toolName,
|
|
1016
|
+
toolEvent.input,
|
|
1017
|
+
{
|
|
1018
|
+
content: toolEvent.content,
|
|
1019
|
+
details: toolEvent.details,
|
|
1020
|
+
},
|
|
1021
|
+
toolEvent.isError === true,
|
|
1022
|
+
);
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
613
1025
|
if (hookName === "agent_end") {
|
|
614
1026
|
if (statusController.config) {
|
|
615
1027
|
runPiNotifyEffects(
|
|
@@ -618,9 +1030,190 @@ async function handleExtensionStatusEvent(
|
|
|
618
1030
|
notifyRequest,
|
|
619
1031
|
);
|
|
620
1032
|
}
|
|
621
|
-
|
|
1033
|
+
if (activePromptRequest !== undefined) {
|
|
1034
|
+
const debugConfig = statusController.config;
|
|
1035
|
+
let promptContext = ctx;
|
|
1036
|
+
const outcome = classifyPromptCommandOutcome(
|
|
1037
|
+
event as { messages: AgentEndEvent["messages"] },
|
|
1038
|
+
);
|
|
1039
|
+
const closureFailureMessage = outcome !== "completed"
|
|
1040
|
+
&& activePromptRequest.worktreeDir !== undefined
|
|
1041
|
+
? `ERROR: Prompt closure retained worktree ${activePromptRequest.worktreeDir} after ${outcome} outcome.`
|
|
1042
|
+
: undefined;
|
|
1043
|
+
const shouldFinalizeMatchedSuccess = outcome === "completed"
|
|
1044
|
+
&& statusController.state.workflowState === "running"
|
|
1045
|
+
&& activePromptRequest.worktreeDir !== undefined;
|
|
1046
|
+
if (debugConfig) {
|
|
1047
|
+
logPromptWorkflowEvent(
|
|
1048
|
+
activePromptRequest.basePath,
|
|
1049
|
+
debugConfig,
|
|
1050
|
+
statusController.state.workflowState,
|
|
1051
|
+
activePromptRequest.promptName,
|
|
1052
|
+
"workflow_closure",
|
|
1053
|
+
{
|
|
1054
|
+
hook: "agent_end",
|
|
1055
|
+
outcome,
|
|
1056
|
+
},
|
|
1057
|
+
{
|
|
1058
|
+
should_finalize_matched_success: shouldFinalizeMatchedSuccess,
|
|
1059
|
+
worktree_dir: activePromptRequest.worktreeDir,
|
|
1060
|
+
event_context_has_switch_session: typeof (ctx as ExtensionCommandContext).switchSession === "function",
|
|
1061
|
+
},
|
|
1062
|
+
);
|
|
1063
|
+
}
|
|
1064
|
+
if (shouldFinalizeMatchedSuccess) {
|
|
1065
|
+
if (debugConfig) {
|
|
1066
|
+
transitionPromptWorkflowState(
|
|
1067
|
+
statusController,
|
|
1068
|
+
promptContext,
|
|
1069
|
+
activePromptRequest.basePath,
|
|
1070
|
+
debugConfig,
|
|
1071
|
+
activePromptRequest.promptName,
|
|
1072
|
+
"merging",
|
|
1073
|
+
);
|
|
1074
|
+
} else {
|
|
1075
|
+
setPiUsereqWorkflowState(statusController, "merging", promptContext);
|
|
1076
|
+
}
|
|
1077
|
+
let finalization:
|
|
1078
|
+
| {
|
|
1079
|
+
mergeAttempted: boolean;
|
|
1080
|
+
mergeSucceeded: boolean;
|
|
1081
|
+
cleanupSucceeded: boolean;
|
|
1082
|
+
errorMessage?: string;
|
|
1083
|
+
activeContext?: unknown;
|
|
1084
|
+
}
|
|
1085
|
+
| undefined;
|
|
1086
|
+
try {
|
|
1087
|
+
finalization = await finalizePromptCommandExecution(
|
|
1088
|
+
activePromptRequest,
|
|
1089
|
+
promptContext,
|
|
1090
|
+
debugConfig
|
|
1091
|
+
? { config: debugConfig, workflowState: statusController.state.workflowState }
|
|
1092
|
+
: undefined,
|
|
1093
|
+
);
|
|
1094
|
+
promptContext = (finalization.activeContext ?? promptContext) as typeof ctx;
|
|
1095
|
+
} catch (error) {
|
|
1096
|
+
promptContext = (getPromptCommandErrorContext(error) ?? promptContext) as typeof ctx;
|
|
1097
|
+
let errorMessage = error instanceof Error ? error.message : String(error);
|
|
1098
|
+
let cleanupSucceeded = false;
|
|
1099
|
+
try {
|
|
1100
|
+
promptContext = (await restorePromptCommandExecution(
|
|
1101
|
+
activePromptRequest,
|
|
1102
|
+
promptContext,
|
|
1103
|
+
debugConfig
|
|
1104
|
+
? { config: debugConfig, workflowState: statusController.state.workflowState }
|
|
1105
|
+
: undefined,
|
|
1106
|
+
) ?? promptContext) as typeof ctx;
|
|
1107
|
+
cleanupSucceeded = true;
|
|
1108
|
+
} catch (restoreError) {
|
|
1109
|
+
promptContext = (getPromptCommandErrorContext(restoreError) ?? promptContext) as typeof ctx;
|
|
1110
|
+
errorMessage = restoreError instanceof Error ? restoreError.message : String(restoreError);
|
|
1111
|
+
}
|
|
1112
|
+
finalization = {
|
|
1113
|
+
mergeAttempted: false,
|
|
1114
|
+
mergeSucceeded: false,
|
|
1115
|
+
cleanupSucceeded,
|
|
1116
|
+
errorMessage,
|
|
1117
|
+
};
|
|
1118
|
+
}
|
|
1119
|
+
if (
|
|
1120
|
+
finalization.errorMessage
|
|
1121
|
+
&& (!finalization.cleanupSucceeded || !finalization.mergeSucceeded)
|
|
1122
|
+
) {
|
|
1123
|
+
if (debugConfig) {
|
|
1124
|
+
transitionPromptWorkflowState(
|
|
1125
|
+
statusController,
|
|
1126
|
+
promptContext,
|
|
1127
|
+
activePromptRequest.basePath,
|
|
1128
|
+
debugConfig,
|
|
1129
|
+
activePromptRequest.promptName,
|
|
1130
|
+
"error",
|
|
1131
|
+
);
|
|
1132
|
+
} else {
|
|
1133
|
+
setPiUsereqWorkflowState(statusController, "error", promptContext);
|
|
1134
|
+
}
|
|
1135
|
+
notifyContextSafely(promptContext, finalization.errorMessage, "error");
|
|
1136
|
+
}
|
|
1137
|
+
} else {
|
|
1138
|
+
try {
|
|
1139
|
+
promptContext = (await restorePromptCommandExecution(
|
|
1140
|
+
activePromptRequest,
|
|
1141
|
+
promptContext,
|
|
1142
|
+
debugConfig
|
|
1143
|
+
? { config: debugConfig, workflowState: statusController.state.workflowState }
|
|
1144
|
+
: undefined,
|
|
1145
|
+
) ?? promptContext) as typeof ctx;
|
|
1146
|
+
if (closureFailureMessage !== undefined) {
|
|
1147
|
+
notifyContextSafely(promptContext, closureFailureMessage, "error");
|
|
1148
|
+
}
|
|
1149
|
+
} catch (error) {
|
|
1150
|
+
promptContext = (getPromptCommandErrorContext(error) ?? promptContext) as typeof ctx;
|
|
1151
|
+
if (debugConfig) {
|
|
1152
|
+
transitionPromptWorkflowState(
|
|
1153
|
+
statusController,
|
|
1154
|
+
promptContext,
|
|
1155
|
+
activePromptRequest.basePath,
|
|
1156
|
+
debugConfig,
|
|
1157
|
+
activePromptRequest.promptName,
|
|
1158
|
+
"error",
|
|
1159
|
+
);
|
|
1160
|
+
} else {
|
|
1161
|
+
setPiUsereqWorkflowState(statusController, "error", promptContext);
|
|
1162
|
+
}
|
|
1163
|
+
notifyContextSafely(promptContext, error instanceof Error ? error.message : String(error), "error");
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
statusController.state.pendingPromptRequest = undefined;
|
|
1167
|
+
statusController.state.activePromptRequest = undefined;
|
|
1168
|
+
if (debugConfig) {
|
|
1169
|
+
transitionPromptWorkflowState(
|
|
1170
|
+
statusController,
|
|
1171
|
+
promptContext,
|
|
1172
|
+
activePromptRequest.basePath,
|
|
1173
|
+
debugConfig,
|
|
1174
|
+
activePromptRequest.promptName,
|
|
1175
|
+
"idle",
|
|
1176
|
+
);
|
|
1177
|
+
} else {
|
|
1178
|
+
setPiUsereqWorkflowState(statusController, "idle", promptContext);
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
622
1181
|
}
|
|
623
1182
|
if (hookName === "session_shutdown") {
|
|
1183
|
+
if (shutdownPromptRequest !== undefined && statusController.config) {
|
|
1184
|
+
logPromptWorkflowEvent(
|
|
1185
|
+
shutdownPromptRequest.basePath,
|
|
1186
|
+
statusController.config,
|
|
1187
|
+
preservePromptCommandStateOnShutdown
|
|
1188
|
+
? statusController.state.workflowState
|
|
1189
|
+
: "idle",
|
|
1190
|
+
shutdownPromptRequest.promptName,
|
|
1191
|
+
"workflow_session_shutdown",
|
|
1192
|
+
{
|
|
1193
|
+
reason: (event as { reason?: unknown }).reason,
|
|
1194
|
+
target_session_file: (event as { targetSessionFile?: unknown }).targetSessionFile,
|
|
1195
|
+
},
|
|
1196
|
+
{
|
|
1197
|
+
preserve_prompt_command_state: preservePromptCommandStateOnShutdown,
|
|
1198
|
+
pending_prompt_request: pendingPromptRequest !== undefined,
|
|
1199
|
+
active_prompt_request: activePromptRequest !== undefined,
|
|
1200
|
+
},
|
|
1201
|
+
);
|
|
1202
|
+
}
|
|
1203
|
+
if (!preservePromptCommandStateOnShutdown) {
|
|
1204
|
+
if (shutdownPromptRequest !== undefined && statusController.config) {
|
|
1205
|
+
transitionPromptWorkflowState(
|
|
1206
|
+
statusController,
|
|
1207
|
+
ctx,
|
|
1208
|
+
shutdownPromptRequest.basePath,
|
|
1209
|
+
statusController.config,
|
|
1210
|
+
shutdownPromptRequest.promptName,
|
|
1211
|
+
"idle",
|
|
1212
|
+
);
|
|
1213
|
+
} else {
|
|
1214
|
+
setPiUsereqWorkflowState(statusController, "idle", ctx);
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
624
1217
|
disposePiUsereqStatusController(statusController);
|
|
625
1218
|
}
|
|
626
1219
|
}
|
|
@@ -629,8 +1222,11 @@ async function handleExtensionStatusEvent(
|
|
|
629
1222
|
* @brief Registers shared wrappers for every supported pi lifecycle hook.
|
|
630
1223
|
* @details Installs one generic wrapper per intercepted hook so every resource,
|
|
631
1224
|
* session, agent, model, tool, bash, and input event is routed through the
|
|
632
|
-
* same extension-status update pipeline.
|
|
633
|
-
*
|
|
1225
|
+
* same extension-status update pipeline. The wrapper suppresses the documented
|
|
1226
|
+
* stale-extension-context error because pi can continue delivering late
|
|
1227
|
+
* lifecycle callbacks against contexts invalidated by session replacement after
|
|
1228
|
+
* prompt orchestration has already completed successfully. Runtime is O(h) in
|
|
1229
|
+
* registered hook count. Side effects include hook registration.
|
|
634
1230
|
* @param[in] pi {ExtensionAPI} Active extension API instance.
|
|
635
1231
|
* @param[in,out] statusController {PiUsereqStatusController} Mutable status controller.
|
|
636
1232
|
* @return {void} No return value.
|
|
@@ -646,7 +1242,13 @@ function registerExtensionStatusHooks(
|
|
|
646
1242
|
) => void;
|
|
647
1243
|
for (const hookName of PI_USEREQ_STATUS_HOOK_NAMES) {
|
|
648
1244
|
registerHook(hookName, async (event, ctx) => {
|
|
649
|
-
|
|
1245
|
+
try {
|
|
1246
|
+
await handleExtensionStatusEvent(pi, statusController, hookName, event, ctx);
|
|
1247
|
+
} catch (error) {
|
|
1248
|
+
if (!isStaleExtensionContextError(error)) {
|
|
1249
|
+
throw error;
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
650
1252
|
});
|
|
651
1253
|
}
|
|
652
1254
|
}
|
|
@@ -664,6 +1266,314 @@ function setConfiguredPiUsereqTools(pi: ExtensionAPI, config: UseReqConfig, enab
|
|
|
664
1266
|
applyConfiguredPiUsereqTools(pi, config);
|
|
665
1267
|
}
|
|
666
1268
|
|
|
1269
|
+
/**
|
|
1270
|
+
* @brief Returns the canonical debug-tool toggle order.
|
|
1271
|
+
* @details Reuses the documented configurable-tool ordering so debug toggles list extension-owned tools before embedded tools and remain deterministic across sessions. Runtime is O(t log t). No external state is mutated.
|
|
1272
|
+
* @return {PiUsereqStartupToolName[]} Ordered debug-tool toggle names.
|
|
1273
|
+
* @satisfies REQ-242
|
|
1274
|
+
*/
|
|
1275
|
+
function getDebugToolToggleNames(): PiUsereqStartupToolName[] {
|
|
1276
|
+
return [...PI_USEREQ_CUSTOM_TOOL_NAMES, ...PI_USEREQ_EMBEDDED_TOOL_NAMES].sort(comparePiUsereqStartupToolNames);
|
|
1277
|
+
}
|
|
1278
|
+
|
|
1279
|
+
/**
|
|
1280
|
+
* @brief Restores the debug configuration subtree to its documented defaults.
|
|
1281
|
+
* @details Resets global debug enablement, log path, workflow-state filter, dedicated workflow-event logging, and selected tool plus prompt debug toggles without mutating unrelated settings. Runtime is O(1). Side effect: mutates `config`.
|
|
1282
|
+
* @param[in,out] config {UseReqConfig} Mutable configuration object.
|
|
1283
|
+
* @return {void} No return value.
|
|
1284
|
+
* @satisfies REQ-236, REQ-237, REQ-238, REQ-239, REQ-195, REQ-277
|
|
1285
|
+
*/
|
|
1286
|
+
function resetDebugConfigToDefaults(config: UseReqConfig): void {
|
|
1287
|
+
config.DEBUG_ENABLED = "disable";
|
|
1288
|
+
config.DEBUG_LOG_FILE = DEFAULT_DEBUG_LOG_FILE;
|
|
1289
|
+
config.DEBUG_STATUS_CHANGES = DEFAULT_DEBUG_STATUS_CHANGES;
|
|
1290
|
+
config.DEBUG_WORKFLOW_EVENTS = DEFAULT_DEBUG_WORKFLOW_EVENTS;
|
|
1291
|
+
config.DEBUG_LOG_ON_STATUS = DEFAULT_DEBUG_LOG_ON_STATUS;
|
|
1292
|
+
config.DEBUG_ENABLED_TOOLS = [];
|
|
1293
|
+
config.DEBUG_ENABLED_PROMPTS = [];
|
|
1294
|
+
}
|
|
1295
|
+
|
|
1296
|
+
/**
|
|
1297
|
+
* @brief Formats the top-level Debug summary value.
|
|
1298
|
+
* @details Emits the current global debug mode plus compact selected-tool and selected-prompt counts for right-aligned menu display. Runtime is O(n) in configured selector count. No external state is mutated.
|
|
1299
|
+
* @param[in] config {UseReqConfig} Effective project configuration.
|
|
1300
|
+
* @return {string} Compact debug summary string.
|
|
1301
|
+
*/
|
|
1302
|
+
function formatDebugMenuSummary(config: UseReqConfig): string {
|
|
1303
|
+
const toolCount = normalizeDebugEnabledTools(config.DEBUG_ENABLED_TOOLS).length;
|
|
1304
|
+
const promptCount = normalizeDebugEnabledPrompts(config.DEBUG_ENABLED_PROMPTS).length;
|
|
1305
|
+
return config.DEBUG_ENABLED === "enable"
|
|
1306
|
+
? `enable • ${toolCount} tools • ${promptCount} prompts`
|
|
1307
|
+
: "disable";
|
|
1308
|
+
}
|
|
1309
|
+
|
|
1310
|
+
/**
|
|
1311
|
+
* @brief Builds one debug-menu row with optional disabled styling.
|
|
1312
|
+
* @details Applies dim styling and disables selection whenever global debug is off for all rows except the global `Debug` toggle row. Runtime is O(1). No external state is mutated.
|
|
1313
|
+
* @param[in] choice {PiUsereqSettingsMenuChoice} Base debug-menu row.
|
|
1314
|
+
* @param[in] debugEnabled {boolean} Whether global debug is enabled.
|
|
1315
|
+
* @return {PiUsereqSettingsMenuChoice} Styled debug-menu row.
|
|
1316
|
+
* @satisfies REQ-241
|
|
1317
|
+
*/
|
|
1318
|
+
function buildDebugMenuChoice(
|
|
1319
|
+
choice: PiUsereqSettingsMenuChoice,
|
|
1320
|
+
debugEnabled: boolean,
|
|
1321
|
+
): PiUsereqSettingsMenuChoice {
|
|
1322
|
+
if (debugEnabled || choice.id === "debug-enabled") {
|
|
1323
|
+
return choice;
|
|
1324
|
+
}
|
|
1325
|
+
return {
|
|
1326
|
+
...choice,
|
|
1327
|
+
disabled: true,
|
|
1328
|
+
labelTone: "dim",
|
|
1329
|
+
valueTone: "dim",
|
|
1330
|
+
};
|
|
1331
|
+
}
|
|
1332
|
+
|
|
1333
|
+
/**
|
|
1334
|
+
* @brief Opens the workflow-state filter selector used by the Debug submenu.
|
|
1335
|
+
* @details Exposes `any` plus each canonical workflow state through the shared settings-menu renderer and returns the selected normalized filter or `undefined` when the user cancels the submenu. Runtime depends on user interaction count. Side effects are limited to transient custom-UI rendering.
|
|
1336
|
+
* @param[in] ctx {ExtensionCommandContext} Active command context.
|
|
1337
|
+
* @param[in] currentValue {DebugLogOnStatus} Current persisted workflow-state filter.
|
|
1338
|
+
* @return {Promise<DebugLogOnStatus | undefined>} Selected workflow-state filter or `undefined` when cancelled.
|
|
1339
|
+
*/
|
|
1340
|
+
async function selectDebugLogOnStatus(
|
|
1341
|
+
ctx: ExtensionCommandContext,
|
|
1342
|
+
currentValue: DebugLogOnStatus,
|
|
1343
|
+
): Promise<DebugLogOnStatus | undefined> {
|
|
1344
|
+
const choice = await showPiUsereqSettingsMenu(ctx, "Log on status", [
|
|
1345
|
+
{
|
|
1346
|
+
id: "any",
|
|
1347
|
+
label: "any",
|
|
1348
|
+
value: currentValue === "any" ? "selected" : "",
|
|
1349
|
+
description: "Write matching debug entries regardless of the current workflow state.",
|
|
1350
|
+
},
|
|
1351
|
+
...DEBUG_WORKFLOW_STATES.map((workflowState) => ({
|
|
1352
|
+
id: workflowState,
|
|
1353
|
+
label: workflowState,
|
|
1354
|
+
value: currentValue === workflowState ? "selected" : "",
|
|
1355
|
+
description: `Write matching debug entries only while workflow state is ${workflowState}.`,
|
|
1356
|
+
})),
|
|
1357
|
+
...buildTerminalSettingsMenuChoices({
|
|
1358
|
+
resetDefaultsDescription: "Restore the documented default workflow-state filter.",
|
|
1359
|
+
}),
|
|
1360
|
+
], { initialSelectedId: currentValue });
|
|
1361
|
+
if (!choice) {
|
|
1362
|
+
return undefined;
|
|
1363
|
+
}
|
|
1364
|
+
if (choice === "reset-defaults") {
|
|
1365
|
+
return DEFAULT_DEBUG_LOG_ON_STATUS;
|
|
1366
|
+
}
|
|
1367
|
+
return normalizeDebugLogOnStatus(choice);
|
|
1368
|
+
}
|
|
1369
|
+
|
|
1370
|
+
/**
|
|
1371
|
+
* @brief Builds the shared settings-menu choices for debug logging configuration.
|
|
1372
|
+
* @details Serializes global debug controls plus workflow-state, dedicated workflow-event, per-tool, and per-prompt toggles into one submenu, deriving inventories from the canonical tool and prompt lists and dimming locked rows while debug is disabled. Runtime is O(t + p). No external state is mutated.
|
|
1373
|
+
* @param[in] config {UseReqConfig} Effective project configuration.
|
|
1374
|
+
* @return {PiUsereqSettingsMenuChoice[]} Ordered debug-menu choices.
|
|
1375
|
+
* @satisfies REQ-240, REQ-241, REQ-242, REQ-243, REQ-193, REQ-277
|
|
1376
|
+
*/
|
|
1377
|
+
function buildDebugMenuChoices(config: UseReqConfig): PiUsereqSettingsMenuChoice[] {
|
|
1378
|
+
const debugEnabled = config.DEBUG_ENABLED === "enable";
|
|
1379
|
+
const enabledTools = new Set(normalizeDebugEnabledTools(config.DEBUG_ENABLED_TOOLS));
|
|
1380
|
+
const enabledPrompts = new Set(normalizeDebugEnabledPrompts(config.DEBUG_ENABLED_PROMPTS));
|
|
1381
|
+
return [
|
|
1382
|
+
{
|
|
1383
|
+
id: "debug-enabled",
|
|
1384
|
+
label: "Debug",
|
|
1385
|
+
value: config.DEBUG_ENABLED,
|
|
1386
|
+
description: "Enable or disable all debug logging behavior and unlock the remaining Debug rows.",
|
|
1387
|
+
},
|
|
1388
|
+
buildDebugMenuChoice(
|
|
1389
|
+
{
|
|
1390
|
+
id: "debug-log-file",
|
|
1391
|
+
label: "Log file",
|
|
1392
|
+
value: config.DEBUG_LOG_FILE,
|
|
1393
|
+
description: "Edit the JSON debug log file path. Relative paths resolve against the original project base.",
|
|
1394
|
+
},
|
|
1395
|
+
debugEnabled,
|
|
1396
|
+
),
|
|
1397
|
+
buildDebugMenuChoice(
|
|
1398
|
+
{
|
|
1399
|
+
id: "debug-log-on-status",
|
|
1400
|
+
label: "Log on status",
|
|
1401
|
+
value: config.DEBUG_LOG_ON_STATUS,
|
|
1402
|
+
description: "Select whether matching debug entries are written for one explicit workflow state or for any workflow state.",
|
|
1403
|
+
},
|
|
1404
|
+
debugEnabled,
|
|
1405
|
+
),
|
|
1406
|
+
buildDebugMenuChoice(
|
|
1407
|
+
{
|
|
1408
|
+
id: "debug-status-changes",
|
|
1409
|
+
label: "Status changes",
|
|
1410
|
+
value: normalizeDebugStatusChanges(config.DEBUG_STATUS_CHANGES),
|
|
1411
|
+
description: "Enable or disable `workflow_state` debug entries for prompt-orchestration transitions.",
|
|
1412
|
+
},
|
|
1413
|
+
debugEnabled,
|
|
1414
|
+
),
|
|
1415
|
+
buildDebugMenuChoice(
|
|
1416
|
+
{
|
|
1417
|
+
id: "debug-workflow-events",
|
|
1418
|
+
label: "Workflow events",
|
|
1419
|
+
value: normalizeDebugWorkflowEvents(config.DEBUG_WORKFLOW_EVENTS),
|
|
1420
|
+
description: "Enable or disable dedicated workflow debug entries for activation, restoration, closure, and session-shutdown diagnostics.",
|
|
1421
|
+
},
|
|
1422
|
+
debugEnabled,
|
|
1423
|
+
),
|
|
1424
|
+
...getDebugToolToggleNames().map((toolName) => buildDebugMenuChoice(
|
|
1425
|
+
{
|
|
1426
|
+
id: `debug-tool:${toolName}`,
|
|
1427
|
+
label: toolName,
|
|
1428
|
+
value: enabledTools.has(toolName) ? "enable" : "disable",
|
|
1429
|
+
description: PI_USEREQ_CUSTOM_TOOL_NAMES.includes(toolName as never)
|
|
1430
|
+
? `Toggle debug logging for custom tool ${toolName}.`
|
|
1431
|
+
: `Toggle debug logging for embedded tool ${toolName}.`,
|
|
1432
|
+
},
|
|
1433
|
+
debugEnabled,
|
|
1434
|
+
)),
|
|
1435
|
+
...DEBUG_PROMPT_NAMES.map((promptName) => buildDebugMenuChoice(
|
|
1436
|
+
{
|
|
1437
|
+
id: `debug-prompt:${promptName}`,
|
|
1438
|
+
label: promptName,
|
|
1439
|
+
value: enabledPrompts.has(promptName) ? "enable" : "disable",
|
|
1440
|
+
description: `Toggle prompt-orchestration debug logging for /${promptName}.`,
|
|
1441
|
+
},
|
|
1442
|
+
debugEnabled,
|
|
1443
|
+
)),
|
|
1444
|
+
...buildTerminalSettingsMenuChoices({
|
|
1445
|
+
resetDefaultsDescription: "Restore the documented default Debug configuration.",
|
|
1446
|
+
}),
|
|
1447
|
+
];
|
|
1448
|
+
}
|
|
1449
|
+
|
|
1450
|
+
/**
|
|
1451
|
+
* @brief Runs the interactive Debug submenu.
|
|
1452
|
+
* @details Lets the user toggle global debug enablement, edit debug file and workflow filters, toggle dedicated workflow-event logging, mutate per-tool and per-prompt debug selectors, and restore subtree defaults while preserving row focus across re-renders. Runtime depends on user interaction count. Side effects include UI updates and config mutation.
|
|
1453
|
+
* @param[in] ctx {ExtensionCommandContext} Active command context.
|
|
1454
|
+
* @param[in,out] config {UseReqConfig} Mutable configuration object.
|
|
1455
|
+
* @return {Promise<void>} Promise resolved when the submenu closes.
|
|
1456
|
+
* @satisfies REQ-236, REQ-237, REQ-238, REQ-239, REQ-240, REQ-241, REQ-242, REQ-243, REQ-192, REQ-193, REQ-195, REQ-277
|
|
1457
|
+
*/
|
|
1458
|
+
async function configureDebugMenu(
|
|
1459
|
+
ctx: ExtensionCommandContext,
|
|
1460
|
+
config: UseReqConfig,
|
|
1461
|
+
onConfigChange: () => void,
|
|
1462
|
+
): Promise<void> {
|
|
1463
|
+
let focusedChoiceId: string | undefined;
|
|
1464
|
+
while (true) {
|
|
1465
|
+
const choice = await showPiUsereqSettingsMenu(ctx, "Debug", buildDebugMenuChoices(config), {
|
|
1466
|
+
initialSelectedId: focusedChoiceId,
|
|
1467
|
+
});
|
|
1468
|
+
if (!choice) {
|
|
1469
|
+
return;
|
|
1470
|
+
}
|
|
1471
|
+
focusedChoiceId = choice;
|
|
1472
|
+
|
|
1473
|
+
if (choice === "debug-enabled") {
|
|
1474
|
+
config.DEBUG_ENABLED = config.DEBUG_ENABLED === "enable" ? "disable" : "enable";
|
|
1475
|
+
onConfigChange();
|
|
1476
|
+
ctx.ui.notify(`Debug ${config.DEBUG_ENABLED}`, "info");
|
|
1477
|
+
continue;
|
|
1478
|
+
}
|
|
1479
|
+
if (choice === "reset-defaults") {
|
|
1480
|
+
const resetPreview: ResetConfirmationChange[] = [
|
|
1481
|
+
{ label: "Debug", previousValue: config.DEBUG_ENABLED, nextValue: "disable" },
|
|
1482
|
+
{ label: "Log file", previousValue: config.DEBUG_LOG_FILE, nextValue: DEFAULT_DEBUG_LOG_FILE },
|
|
1483
|
+
{ label: "Status changes", previousValue: normalizeDebugStatusChanges(config.DEBUG_STATUS_CHANGES), nextValue: DEFAULT_DEBUG_STATUS_CHANGES },
|
|
1484
|
+
{ label: "Workflow events", previousValue: normalizeDebugWorkflowEvents(config.DEBUG_WORKFLOW_EVENTS), nextValue: DEFAULT_DEBUG_WORKFLOW_EVENTS },
|
|
1485
|
+
{ label: "Log on status", previousValue: config.DEBUG_LOG_ON_STATUS, nextValue: DEFAULT_DEBUG_LOG_ON_STATUS },
|
|
1486
|
+
{ label: "Enabled debug tools", previousValue: String(normalizeDebugEnabledTools(config.DEBUG_ENABLED_TOOLS).length), nextValue: "0" },
|
|
1487
|
+
{ label: "Enabled debug prompts", previousValue: String(normalizeDebugEnabledPrompts(config.DEBUG_ENABLED_PROMPTS).length), nextValue: "0" },
|
|
1488
|
+
].filter((change) => change.previousValue !== change.nextValue);
|
|
1489
|
+
const approved = await confirmResetChanges(
|
|
1490
|
+
ctx,
|
|
1491
|
+
"Confirm Debug reset",
|
|
1492
|
+
resetPreview,
|
|
1493
|
+
"Approve restoring the documented default Debug configuration.",
|
|
1494
|
+
"Abort the Debug reset and keep the current values.",
|
|
1495
|
+
);
|
|
1496
|
+
if (!approved) {
|
|
1497
|
+
ctx.ui.notify("Aborted Debug reset", "info");
|
|
1498
|
+
continue;
|
|
1499
|
+
}
|
|
1500
|
+
resetDebugConfigToDefaults(config);
|
|
1501
|
+
onConfigChange();
|
|
1502
|
+
ctx.ui.notify("Restored default Debug configuration", "info");
|
|
1503
|
+
continue;
|
|
1504
|
+
}
|
|
1505
|
+
if (config.DEBUG_ENABLED !== "enable") {
|
|
1506
|
+
ctx.ui.notify("Debug rows are locked while Debug is disabled", "info");
|
|
1507
|
+
continue;
|
|
1508
|
+
}
|
|
1509
|
+
if (choice === "debug-log-file") {
|
|
1510
|
+
const value = await ctx.ui.input("Log file", config.DEBUG_LOG_FILE);
|
|
1511
|
+
if (value !== undefined) {
|
|
1512
|
+
config.DEBUG_LOG_FILE = normalizeDebugLogFile(value);
|
|
1513
|
+
onConfigChange();
|
|
1514
|
+
ctx.ui.notify(`Debug log file set to ${config.DEBUG_LOG_FILE}`, "info");
|
|
1515
|
+
}
|
|
1516
|
+
continue;
|
|
1517
|
+
}
|
|
1518
|
+
if (choice === "debug-status-changes") {
|
|
1519
|
+
config.DEBUG_STATUS_CHANGES = normalizeDebugStatusChanges(
|
|
1520
|
+
config.DEBUG_STATUS_CHANGES === "enable" ? "disable" : "enable",
|
|
1521
|
+
);
|
|
1522
|
+
onConfigChange();
|
|
1523
|
+
ctx.ui.notify(`Debug status-change logging ${config.DEBUG_STATUS_CHANGES}`, "info");
|
|
1524
|
+
continue;
|
|
1525
|
+
}
|
|
1526
|
+
if (choice === "debug-workflow-events") {
|
|
1527
|
+
config.DEBUG_WORKFLOW_EVENTS = normalizeDebugWorkflowEvents(
|
|
1528
|
+
config.DEBUG_WORKFLOW_EVENTS === "enable" ? "disable" : "enable",
|
|
1529
|
+
);
|
|
1530
|
+
onConfigChange();
|
|
1531
|
+
ctx.ui.notify(`Debug workflow-event logging ${config.DEBUG_WORKFLOW_EVENTS}`, "info");
|
|
1532
|
+
continue;
|
|
1533
|
+
}
|
|
1534
|
+
if (choice === "debug-log-on-status") {
|
|
1535
|
+
const selectedStatus = await selectDebugLogOnStatus(ctx, config.DEBUG_LOG_ON_STATUS);
|
|
1536
|
+
if (selectedStatus !== undefined) {
|
|
1537
|
+
config.DEBUG_LOG_ON_STATUS = selectedStatus;
|
|
1538
|
+
onConfigChange();
|
|
1539
|
+
ctx.ui.notify(`Debug workflow filter set to ${config.DEBUG_LOG_ON_STATUS}`, "info");
|
|
1540
|
+
}
|
|
1541
|
+
continue;
|
|
1542
|
+
}
|
|
1543
|
+
if (choice.startsWith("debug-tool:")) {
|
|
1544
|
+
const toolName = choice.slice("debug-tool:".length);
|
|
1545
|
+
const enabledTools = new Set(normalizeDebugEnabledTools(config.DEBUG_ENABLED_TOOLS));
|
|
1546
|
+
if (enabledTools.has(toolName as PiUsereqStartupToolName)) {
|
|
1547
|
+
enabledTools.delete(toolName as PiUsereqStartupToolName);
|
|
1548
|
+
} else {
|
|
1549
|
+
enabledTools.add(toolName as PiUsereqStartupToolName);
|
|
1550
|
+
}
|
|
1551
|
+
config.DEBUG_ENABLED_TOOLS = getDebugToolToggleNames().filter((name) => enabledTools.has(name));
|
|
1552
|
+
onConfigChange();
|
|
1553
|
+
ctx.ui.notify(
|
|
1554
|
+
`${enabledTools.has(toolName as PiUsereqStartupToolName) ? "Enabled" : "Disabled"} debug logging for ${toolName}`,
|
|
1555
|
+
"info",
|
|
1556
|
+
);
|
|
1557
|
+
continue;
|
|
1558
|
+
}
|
|
1559
|
+
if (choice.startsWith("debug-prompt:")) {
|
|
1560
|
+
const promptName = choice.slice("debug-prompt:".length);
|
|
1561
|
+
const enabledPrompts = new Set(normalizeDebugEnabledPrompts(config.DEBUG_ENABLED_PROMPTS));
|
|
1562
|
+
if (enabledPrompts.has(promptName as (typeof DEBUG_PROMPT_NAMES)[number])) {
|
|
1563
|
+
enabledPrompts.delete(promptName as (typeof DEBUG_PROMPT_NAMES)[number]);
|
|
1564
|
+
} else {
|
|
1565
|
+
enabledPrompts.add(promptName as (typeof DEBUG_PROMPT_NAMES)[number]);
|
|
1566
|
+
}
|
|
1567
|
+
config.DEBUG_ENABLED_PROMPTS = DEBUG_PROMPT_NAMES.filter((name) => enabledPrompts.has(name));
|
|
1568
|
+
onConfigChange();
|
|
1569
|
+
ctx.ui.notify(
|
|
1570
|
+
`${enabledPrompts.has(promptName as (typeof DEBUG_PROMPT_NAMES)[number]) ? "Enabled" : "Disabled"} debug logging for ${promptName}`,
|
|
1571
|
+
"info",
|
|
1572
|
+
);
|
|
1573
|
+
}
|
|
1574
|
+
}
|
|
1575
|
+
}
|
|
1576
|
+
|
|
667
1577
|
/**
|
|
668
1578
|
* @brief Represents one persisted boolean notification-setting key.
|
|
669
1579
|
* @details Restricts menu toggles to the global enable flags and completed/interrupted/failed event toggles used by command-notify, sound, and Pushover configuration. Compile-time only and introduces no runtime cost.
|
|
@@ -881,7 +1791,7 @@ function buildPiNotifyEventLauncherChoice(
|
|
|
881
1791
|
|
|
882
1792
|
/**
|
|
883
1793
|
* @brief Builds the shared settings-menu choices for one notification event submenu.
|
|
884
|
-
* @details Serializes completed/interrupted/failed rows with right-aligned `on|off` values, then appends `Reset defaults`
|
|
1794
|
+
* @details Serializes completed/interrupted/failed rows with right-aligned `on|off` values, then appends a value-less `Reset defaults` row for submenu-scoped mutation control. Runtime is O(1). No external state is mutated.
|
|
885
1795
|
* @param[in] config {UseReqConfig} Effective project configuration.
|
|
886
1796
|
* @param[in] eventMenu {PiNotifyEventMenuDefinition} Notification-system event submenu contract.
|
|
887
1797
|
* @return {PiUsereqSettingsMenuChoice[]} Ordered event-submenu choice vector.
|
|
@@ -898,18 +1808,9 @@ function buildPiNotifyEventMenuChoices(
|
|
|
898
1808
|
value: config[eventMenu.keys[row.eventId]] ? "on" : "off",
|
|
899
1809
|
description: `${eventMenu.systemLabel}: ${row.description}`,
|
|
900
1810
|
})),
|
|
901
|
-
{
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
value: "",
|
|
905
|
-
description: `Restore the documented default ${eventMenu.systemLabel.toLowerCase()} event toggles.`,
|
|
906
|
-
},
|
|
907
|
-
{
|
|
908
|
-
id: "save-and-close",
|
|
909
|
-
label: "Save and close",
|
|
910
|
-
value: "",
|
|
911
|
-
description: "Return to the notifications menu.",
|
|
912
|
-
},
|
|
1811
|
+
...buildTerminalSettingsMenuChoices({
|
|
1812
|
+
resetDefaultsDescription: `Restore the documented default ${eventMenu.systemLabel.toLowerCase()} event toggles.`,
|
|
1813
|
+
}),
|
|
913
1814
|
];
|
|
914
1815
|
}
|
|
915
1816
|
|
|
@@ -961,6 +1862,7 @@ async function configurePiNotifyEventMenu(
|
|
|
961
1862
|
ctx: ExtensionCommandContext,
|
|
962
1863
|
config: UseReqConfig,
|
|
963
1864
|
eventMenu: PiNotifyEventMenuDefinition,
|
|
1865
|
+
onConfigChange: () => void,
|
|
964
1866
|
): Promise<void> {
|
|
965
1867
|
let focusedChoiceId: string | undefined;
|
|
966
1868
|
while (true) {
|
|
@@ -970,12 +1872,32 @@ async function configurePiNotifyEventMenu(
|
|
|
970
1872
|
buildPiNotifyEventMenuChoices(config, eventMenu),
|
|
971
1873
|
{ initialSelectedId: focusedChoiceId },
|
|
972
1874
|
);
|
|
973
|
-
if (!choice
|
|
1875
|
+
if (!choice) {
|
|
974
1876
|
return;
|
|
975
1877
|
}
|
|
976
1878
|
focusedChoiceId = choice;
|
|
977
1879
|
if (choice === "reset-defaults") {
|
|
1880
|
+
const defaults = getDefaultConfig("");
|
|
1881
|
+
const resetPreview = PI_NOTIFY_EVENT_ROW_DEFINITIONS
|
|
1882
|
+
.map((row) => ({
|
|
1883
|
+
label: row.label,
|
|
1884
|
+
previousValue: config[eventMenu.keys[row.eventId]] ? "on" : "off",
|
|
1885
|
+
nextValue: defaults[eventMenu.keys[row.eventId]] ? "on" : "off",
|
|
1886
|
+
}))
|
|
1887
|
+
.filter((change) => change.previousValue !== change.nextValue);
|
|
1888
|
+
const approved = await confirmResetChanges(
|
|
1889
|
+
ctx,
|
|
1890
|
+
`Confirm ${eventMenu.systemLabel} event reset`,
|
|
1891
|
+
resetPreview,
|
|
1892
|
+
`Approve restoring default ${eventMenu.systemLabel.toLowerCase()} event toggles.`,
|
|
1893
|
+
`Abort the ${eventMenu.systemLabel.toLowerCase()} event reset and keep the current values.`,
|
|
1894
|
+
);
|
|
1895
|
+
if (!approved) {
|
|
1896
|
+
ctx.ui.notify(`Aborted ${eventMenu.systemLabel.toLowerCase()} event reset`, "info");
|
|
1897
|
+
continue;
|
|
1898
|
+
}
|
|
978
1899
|
resetPiNotifyEventMenuToDefaults(config, eventMenu);
|
|
1900
|
+
onConfigChange();
|
|
979
1901
|
ctx.ui.notify(
|
|
980
1902
|
`Restored default ${eventMenu.systemLabel.toLowerCase()} events`,
|
|
981
1903
|
"info",
|
|
@@ -986,6 +1908,7 @@ async function configurePiNotifyEventMenu(
|
|
|
986
1908
|
config,
|
|
987
1909
|
choice as PiNotifyEventBooleanConfigKey,
|
|
988
1910
|
);
|
|
1911
|
+
onConfigChange();
|
|
989
1912
|
const eventLabel = resolvePiNotifyEventLabel(
|
|
990
1913
|
choice as PiNotifyEventBooleanConfigKey,
|
|
991
1914
|
eventMenu,
|
|
@@ -999,18 +1922,24 @@ async function configurePiNotifyEventMenu(
|
|
|
999
1922
|
|
|
1000
1923
|
/**
|
|
1001
1924
|
* @brief Builds the direct Pushover rows rendered inside `Notifications`.
|
|
1002
|
-
* @details Serializes the global enable flag, shared-event submenu launcher, priority, title, text, and credential rows into right-valued menu items appended after the sound-command rows. Runtime is O(
|
|
1925
|
+
* @details Serializes the global enable flag, shared-event submenu launcher, priority, title, text, and credential rows into right-valued menu items appended after the sound-command rows, dims and disables the enable row until both credentials are populated, and escapes control characters for the single-line `Pushover text` value. Runtime is O(n) in the rendered text-template length. No external state is mutated.
|
|
1003
1926
|
* @param[in] config {UseReqConfig} Effective project configuration.
|
|
1004
1927
|
* @return {PiUsereqSettingsMenuChoice[]} Ordered direct Pushover rows.
|
|
1005
|
-
* @satisfies REQ-163, REQ-165, REQ-172, REQ-184, REQ-185, REQ-198
|
|
1928
|
+
* @satisfies REQ-163, REQ-165, REQ-172, REQ-184, REQ-185, REQ-198, REQ-234, REQ-235
|
|
1006
1929
|
*/
|
|
1007
1930
|
function buildPiNotifyPushoverRows(config: UseReqConfig): PiUsereqSettingsMenuChoice[] {
|
|
1931
|
+
const pushoverCredentialsReady = hasPiNotifyPushoverCredentials(config);
|
|
1008
1932
|
return [
|
|
1009
1933
|
{
|
|
1010
1934
|
id: "notify-pushover-enabled",
|
|
1011
1935
|
label: "Enable pushover",
|
|
1012
|
-
|
|
1013
|
-
|
|
1936
|
+
labelTone: pushoverCredentialsReady ? undefined : "dim",
|
|
1937
|
+
value: pushoverCredentialsReady ? formatPiNotifyPushoverStatus(config) : "off",
|
|
1938
|
+
valueTone: pushoverCredentialsReady ? undefined : "dim",
|
|
1939
|
+
disabled: !pushoverCredentialsReady,
|
|
1940
|
+
description: pushoverCredentialsReady
|
|
1941
|
+
? "Enable or disable all Pushover delivery globally."
|
|
1942
|
+
: "Populate both Pushover credential fields to unlock global Pushover enablement.",
|
|
1014
1943
|
},
|
|
1015
1944
|
buildPiNotifyEventLauncherChoice(
|
|
1016
1945
|
config,
|
|
@@ -1031,7 +1960,7 @@ function buildPiNotifyPushoverRows(config: UseReqConfig): PiUsereqSettingsMenuCh
|
|
|
1031
1960
|
{
|
|
1032
1961
|
id: "notify-pushover-text",
|
|
1033
1962
|
label: "Pushover text",
|
|
1034
|
-
value: config["notify-pushover-text"],
|
|
1963
|
+
value: formatPiNotifyControlSequenceText(config["notify-pushover-text"]),
|
|
1035
1964
|
description: "Edit the text template used for outbound Pushover messages.",
|
|
1036
1965
|
},
|
|
1037
1966
|
{
|
|
@@ -1051,16 +1980,16 @@ function buildPiNotifyPushoverRows(config: UseReqConfig): PiUsereqSettingsMenuCh
|
|
|
1051
1980
|
|
|
1052
1981
|
/**
|
|
1053
1982
|
* @brief Opens the shared settings-menu selector for Pushover priority.
|
|
1054
|
-
* @details Reuses the pi-usereq settings-menu renderer so Pushover priority selection remains stylistically aligned with the notification menus and
|
|
1983
|
+
* @details Reuses the pi-usereq settings-menu renderer so Pushover priority selection remains stylistically aligned with the notification menus and appends a value-less subtree-local `Reset defaults` row. Runtime depends on user interaction count. Side effects are limited to transient custom-UI rendering.
|
|
1055
1984
|
* @param[in] ctx {ExtensionCommandContext} Active command context.
|
|
1056
1985
|
* @param[in] currentPriority {PiNotifyPushoverPriority} Persisted priority value.
|
|
1057
|
-
* @return {Promise<PiNotifyPushoverPriority | undefined>} Selected priority or `undefined` when cancelled.
|
|
1986
|
+
* @return {Promise<PiNotifyPushoverPriority | "reset-defaults" | undefined>} Selected priority, reset action, or `undefined` when cancelled.
|
|
1058
1987
|
* @satisfies REQ-172, REQ-192
|
|
1059
1988
|
*/
|
|
1060
1989
|
async function selectPiNotifyPushoverPriority(
|
|
1061
1990
|
ctx: ExtensionCommandContext,
|
|
1062
1991
|
currentPriority: PiNotifyPushoverPriority,
|
|
1063
|
-
): Promise<PiNotifyPushoverPriority | undefined> {
|
|
1992
|
+
): Promise<PiNotifyPushoverPriority | "reset-defaults" | undefined> {
|
|
1064
1993
|
const choice = await showPiUsereqSettingsMenu(ctx, "Pushover priority", [
|
|
1065
1994
|
{
|
|
1066
1995
|
id: "0",
|
|
@@ -1074,19 +2003,25 @@ async function selectPiNotifyPushoverPriority(
|
|
|
1074
2003
|
value: currentPriority === 1 ? "selected" : "",
|
|
1075
2004
|
description: "Send outbound Pushover messages with high priority `1`.",
|
|
1076
2005
|
},
|
|
2006
|
+
...buildTerminalSettingsMenuChoices({
|
|
2007
|
+
resetDefaultsDescription: "Restore the documented default Pushover priority.",
|
|
2008
|
+
}),
|
|
1077
2009
|
], { initialSelectedId: String(currentPriority) });
|
|
1078
2010
|
if (!choice) {
|
|
1079
2011
|
return undefined;
|
|
1080
2012
|
}
|
|
2013
|
+
if (choice === "reset-defaults") {
|
|
2014
|
+
return "reset-defaults";
|
|
2015
|
+
}
|
|
1081
2016
|
return normalizePiNotifyPushoverPriority(choice);
|
|
1082
2017
|
}
|
|
1083
2018
|
|
|
1084
2019
|
/**
|
|
1085
2020
|
* @brief Builds the shared settings-menu choices for notification configuration.
|
|
1086
|
-
* @details Serializes command-notify, sound, and Pushover blocks with dedicated shared-event submenu launchers so the settings-menu renderer can expose one unified but modular configuration surface
|
|
2021
|
+
* @details Serializes command-notify, sound, and Pushover blocks with dedicated shared-event submenu launchers so the settings-menu renderer can expose one unified but modular configuration surface, including locked Pushover enablement and escaped single-line rendering for `Pushover text`. Runtime is O(n) in the longest rendered command or text field. No external state is mutated.
|
|
1087
2022
|
* @param[in] config {UseReqConfig} Effective project configuration.
|
|
1088
2023
|
* @return {PiUsereqSettingsMenuChoice[]} Ordered notification-menu choice vector.
|
|
1089
|
-
* @satisfies REQ-137, REQ-149, REQ-150, REQ-151, REQ-152, REQ-163, REQ-164, REQ-165, REQ-172, REQ-179, REQ-181, REQ-183, REQ-188, REQ-193, REQ-198
|
|
2024
|
+
* @satisfies REQ-137, REQ-149, REQ-150, REQ-151, REQ-152, REQ-163, REQ-164, REQ-165, REQ-172, REQ-179, REQ-181, REQ-183, REQ-188, REQ-193, REQ-198, REQ-234, REQ-235
|
|
1090
2025
|
*/
|
|
1091
2026
|
function buildPiNotifyMenuChoices(config: UseReqConfig): PiUsereqSettingsMenuChoice[] {
|
|
1092
2027
|
return [
|
|
@@ -1141,33 +2076,24 @@ function buildPiNotifyMenuChoices(config: UseReqConfig): PiUsereqSettingsMenuCho
|
|
|
1141
2076
|
description: "Edit the shell command used when the selected sound command is `high`.",
|
|
1142
2077
|
},
|
|
1143
2078
|
...buildPiNotifyPushoverRows(config),
|
|
1144
|
-
{
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
value: "",
|
|
1148
|
-
description: "Restore the documented notification defaults for command-notify, sound, and Pushover settings.",
|
|
1149
|
-
},
|
|
1150
|
-
{
|
|
1151
|
-
id: "save-and-close",
|
|
1152
|
-
label: "Save and close",
|
|
1153
|
-
value: "",
|
|
1154
|
-
description: "Return to the parent configuration menu.",
|
|
1155
|
-
},
|
|
2079
|
+
...buildTerminalSettingsMenuChoices({
|
|
2080
|
+
resetDefaultsDescription: "Restore the documented notification defaults for command-notify, sound, and Pushover settings.",
|
|
2081
|
+
}),
|
|
1156
2082
|
];
|
|
1157
2083
|
}
|
|
1158
2084
|
|
|
1159
2085
|
/**
|
|
1160
2086
|
* @brief Opens the shared settings-menu selector for the active sound level.
|
|
1161
|
-
* @details Reuses the pi-usereq settings-menu renderer so sound-level selection remains stylistically aligned with the notification menu and
|
|
2087
|
+
* @details Reuses the pi-usereq settings-menu renderer so sound-level selection remains stylistically aligned with the notification menu and appends a value-less subtree-local `Reset defaults` row. Runtime depends on user interaction count. Side effects are limited to transient custom-UI rendering.
|
|
1162
2088
|
* @param[in] ctx {ExtensionCommandContext} Active command context.
|
|
1163
2089
|
* @param[in] currentLevel {PiNotifySoundLevel} Currently selected sound level.
|
|
1164
|
-
* @return {Promise<PiNotifySoundLevel | undefined>} Selected sound level or `undefined` when cancelled.
|
|
2090
|
+
* @return {Promise<PiNotifySoundLevel | "reset-defaults" | undefined>} Selected sound level, reset action, or `undefined` when cancelled.
|
|
1165
2091
|
* @satisfies REQ-131, REQ-179, REQ-192
|
|
1166
2092
|
*/
|
|
1167
2093
|
async function selectPiNotifySoundLevel(
|
|
1168
2094
|
ctx: ExtensionCommandContext,
|
|
1169
2095
|
currentLevel: PiNotifySoundLevel,
|
|
1170
|
-
): Promise<PiNotifySoundLevel | undefined> {
|
|
2096
|
+
): Promise<PiNotifySoundLevel | "reset-defaults" | undefined> {
|
|
1171
2097
|
const choice = await showPiUsereqSettingsMenu(ctx, "Enable sound", [
|
|
1172
2098
|
{
|
|
1173
2099
|
id: "none",
|
|
@@ -1193,21 +2119,31 @@ async function selectPiNotifySoundLevel(
|
|
|
1193
2119
|
value: currentLevel === "high" ? "selected" : "",
|
|
1194
2120
|
description: "Use the high-volume sound command when sound delivery is enabled for the current event.",
|
|
1195
2121
|
},
|
|
2122
|
+
...buildTerminalSettingsMenuChoices({
|
|
2123
|
+
resetDefaultsDescription: "Restore the documented default sound level.",
|
|
2124
|
+
}),
|
|
1196
2125
|
], { initialSelectedId: currentLevel });
|
|
1197
|
-
|
|
2126
|
+
if (!choice) {
|
|
2127
|
+
return undefined;
|
|
2128
|
+
}
|
|
2129
|
+
if (choice === "reset-defaults") {
|
|
2130
|
+
return "reset-defaults";
|
|
2131
|
+
}
|
|
2132
|
+
return choice as PiNotifySoundLevel;
|
|
1198
2133
|
}
|
|
1199
2134
|
|
|
1200
2135
|
/**
|
|
1201
2136
|
* @brief Runs the interactive notification-configuration menu.
|
|
1202
|
-
* @details Exposes command-notify, sound, and Pushover controls through the shared settings-menu renderer, delegates completed/interrupted/failed toggles to dedicated event submenus, and preserves row focus across menu re-renders. Runtime depends on user interaction count. Side effects include UI updates and config mutation.
|
|
2137
|
+
* @details Exposes command-notify, sound, and Pushover controls through the shared settings-menu renderer, delegates completed/interrupted/failed toggles to dedicated event submenus, keeps `Enable pushover` locked until both credentials are populated, decodes escaped control-sequence input for `Pushover text`, and preserves row focus across menu re-renders. Runtime depends on user interaction count. Side effects include UI updates and config mutation.
|
|
1203
2138
|
* @param[in] ctx {ExtensionCommandContext} Active command context.
|
|
1204
2139
|
* @param[in,out] config {UseReqConfig} Mutable configuration object.
|
|
1205
2140
|
* @return {Promise<boolean>} `true` when the sound-toggle shortcut changed.
|
|
1206
|
-
* @satisfies REQ-131, REQ-133, REQ-134, REQ-137, REQ-163, REQ-164, REQ-165, REQ-172, REQ-179, REQ-181, REQ-183, REQ-184, REQ-188, REQ-192, REQ-193, REQ-195, REQ-196, REQ-198
|
|
2141
|
+
* @satisfies REQ-131, REQ-133, REQ-134, REQ-137, REQ-163, REQ-164, REQ-165, REQ-172, REQ-179, REQ-181, REQ-183, REQ-184, REQ-188, REQ-192, REQ-193, REQ-195, REQ-196, REQ-198, REQ-234, REQ-235
|
|
1207
2142
|
*/
|
|
1208
2143
|
async function configurePiNotifyMenu(
|
|
1209
2144
|
ctx: ExtensionCommandContext,
|
|
1210
2145
|
config: UseReqConfig,
|
|
2146
|
+
onConfigChange: () => void,
|
|
1211
2147
|
): Promise<boolean> {
|
|
1212
2148
|
const originalShortcut = config["notify-sound-toggle-shortcut"];
|
|
1213
2149
|
let focusedChoiceId: string | undefined;
|
|
@@ -1218,12 +2154,18 @@ async function configurePiNotifyMenu(
|
|
|
1218
2154
|
buildPiNotifyMenuChoices(config),
|
|
1219
2155
|
{ initialSelectedId: focusedChoiceId },
|
|
1220
2156
|
);
|
|
1221
|
-
if (!choice
|
|
2157
|
+
if (!choice) {
|
|
1222
2158
|
return config["notify-sound-toggle-shortcut"] !== originalShortcut;
|
|
1223
2159
|
}
|
|
1224
2160
|
focusedChoiceId = choice;
|
|
1225
2161
|
if (choice === "notify-enabled" || choice === "notify-pushover-enabled") {
|
|
2162
|
+
if (choice === "notify-pushover-enabled" && !hasPiNotifyPushoverCredentials(config)) {
|
|
2163
|
+
config["notify-pushover-enabled"] = false;
|
|
2164
|
+
ctx.ui.notify("Populate both Pushover credential fields before enabling Pushover", "info");
|
|
2165
|
+
continue;
|
|
2166
|
+
}
|
|
1226
2167
|
const enabled = togglePiNotifyFlag(config, choice as PiNotifyBooleanConfigKey);
|
|
2168
|
+
onConfigChange();
|
|
1227
2169
|
const labelMap: Record<string, string> = {
|
|
1228
2170
|
"notify-enabled": "Notification",
|
|
1229
2171
|
"notify-pushover-enabled": "Pushover",
|
|
@@ -1236,6 +2178,7 @@ async function configurePiNotifyMenu(
|
|
|
1236
2178
|
ctx,
|
|
1237
2179
|
config,
|
|
1238
2180
|
PI_NOTIFY_EVENT_MENU_DEFINITIONS.notification,
|
|
2181
|
+
onConfigChange,
|
|
1239
2182
|
);
|
|
1240
2183
|
continue;
|
|
1241
2184
|
}
|
|
@@ -1244,6 +2187,7 @@ async function configurePiNotifyMenu(
|
|
|
1244
2187
|
ctx,
|
|
1245
2188
|
config,
|
|
1246
2189
|
PI_NOTIFY_EVENT_MENU_DEFINITIONS.sound,
|
|
2190
|
+
onConfigChange,
|
|
1247
2191
|
);
|
|
1248
2192
|
continue;
|
|
1249
2193
|
}
|
|
@@ -1252,6 +2196,7 @@ async function configurePiNotifyMenu(
|
|
|
1252
2196
|
ctx,
|
|
1253
2197
|
config,
|
|
1254
2198
|
PI_NOTIFY_EVENT_MENU_DEFINITIONS.pushover,
|
|
2199
|
+
onConfigChange,
|
|
1255
2200
|
);
|
|
1256
2201
|
continue;
|
|
1257
2202
|
}
|
|
@@ -1259,14 +2204,33 @@ async function configurePiNotifyMenu(
|
|
|
1259
2204
|
const value = await ctx.ui.input("Notify command", config.PI_NOTIFY_CMD);
|
|
1260
2205
|
if (value !== undefined) {
|
|
1261
2206
|
config.PI_NOTIFY_CMD = normalizePiNotifyCommand(value, DEFAULT_PI_NOTIFY_CMD);
|
|
2207
|
+
onConfigChange();
|
|
1262
2208
|
ctx.ui.notify("Updated notify command", "info");
|
|
1263
2209
|
}
|
|
1264
2210
|
continue;
|
|
1265
2211
|
}
|
|
1266
2212
|
if (choice === "selected-sound-command") {
|
|
1267
2213
|
const nextLevel = await selectPiNotifySoundLevel(ctx, config["notify-sound"]);
|
|
1268
|
-
if (nextLevel
|
|
2214
|
+
if (nextLevel === "reset-defaults") {
|
|
2215
|
+
const defaultSoundLevel = getDefaultConfig("")["notify-sound"];
|
|
2216
|
+
const approved = await confirmResetChanges(
|
|
2217
|
+
ctx,
|
|
2218
|
+
"Confirm sound reset",
|
|
2219
|
+
[{ label: "Enable sound", previousValue: config["notify-sound"], nextValue: defaultSoundLevel }]
|
|
2220
|
+
.filter((change) => change.previousValue !== change.nextValue),
|
|
2221
|
+
"Approve restoring the documented default sound level.",
|
|
2222
|
+
"Abort the sound reset and keep the current value.",
|
|
2223
|
+
);
|
|
2224
|
+
if (!approved) {
|
|
2225
|
+
ctx.ui.notify("Aborted sound reset", "info");
|
|
2226
|
+
} else {
|
|
2227
|
+
config["notify-sound"] = defaultSoundLevel;
|
|
2228
|
+
onConfigChange();
|
|
2229
|
+
ctx.ui.notify("Restored default sound level", "info");
|
|
2230
|
+
}
|
|
2231
|
+
} else if (nextLevel !== undefined) {
|
|
1269
2232
|
config["notify-sound"] = nextLevel;
|
|
2233
|
+
onConfigChange();
|
|
1270
2234
|
ctx.ui.notify(`Enable sound set to ${nextLevel}`, "info");
|
|
1271
2235
|
}
|
|
1272
2236
|
continue;
|
|
@@ -1278,6 +2242,7 @@ async function configurePiNotifyMenu(
|
|
|
1278
2242
|
);
|
|
1279
2243
|
if (value?.trim()) {
|
|
1280
2244
|
config["notify-sound-toggle-shortcut"] = value.trim();
|
|
2245
|
+
onConfigChange();
|
|
1281
2246
|
ctx.ui.notify(
|
|
1282
2247
|
`Sound toggle hotkey bind set to ${config["notify-sound-toggle-shortcut"]}`,
|
|
1283
2248
|
"info",
|
|
@@ -1292,6 +2257,7 @@ async function configurePiNotifyMenu(
|
|
|
1292
2257
|
value,
|
|
1293
2258
|
DEFAULT_PI_NOTIFY_SOUND_LOW_CMD,
|
|
1294
2259
|
);
|
|
2260
|
+
onConfigChange();
|
|
1295
2261
|
ctx.ui.notify("Updated sound command (low vol.)", "info");
|
|
1296
2262
|
}
|
|
1297
2263
|
continue;
|
|
@@ -1303,6 +2269,7 @@ async function configurePiNotifyMenu(
|
|
|
1303
2269
|
value,
|
|
1304
2270
|
DEFAULT_PI_NOTIFY_SOUND_MID_CMD,
|
|
1305
2271
|
);
|
|
2272
|
+
onConfigChange();
|
|
1306
2273
|
ctx.ui.notify("Updated sound command (mid vol.)", "info");
|
|
1307
2274
|
}
|
|
1308
2275
|
continue;
|
|
@@ -1314,14 +2281,36 @@ async function configurePiNotifyMenu(
|
|
|
1314
2281
|
value,
|
|
1315
2282
|
DEFAULT_PI_NOTIFY_SOUND_HIGH_CMD,
|
|
1316
2283
|
);
|
|
2284
|
+
onConfigChange();
|
|
1317
2285
|
ctx.ui.notify("Updated sound command (high vol.)", "info");
|
|
1318
2286
|
}
|
|
1319
2287
|
continue;
|
|
1320
2288
|
}
|
|
1321
2289
|
if (choice === "notify-pushover-priority") {
|
|
1322
2290
|
const nextPriority = await selectPiNotifyPushoverPriority(ctx, config["notify-pushover-priority"]);
|
|
1323
|
-
if (nextPriority
|
|
2291
|
+
if (nextPriority === "reset-defaults") {
|
|
2292
|
+
const defaultPriority = getDefaultConfig("")["notify-pushover-priority"];
|
|
2293
|
+
const approved = await confirmResetChanges(
|
|
2294
|
+
ctx,
|
|
2295
|
+
"Confirm Pushover priority reset",
|
|
2296
|
+
[{
|
|
2297
|
+
label: "Pushover priority",
|
|
2298
|
+
previousValue: formatPiNotifyPushoverPriority(config["notify-pushover-priority"]),
|
|
2299
|
+
nextValue: formatPiNotifyPushoverPriority(defaultPriority),
|
|
2300
|
+
}].filter((change) => change.previousValue !== change.nextValue),
|
|
2301
|
+
"Approve restoring the documented default Pushover priority.",
|
|
2302
|
+
"Abort the Pushover priority reset and keep the current value.",
|
|
2303
|
+
);
|
|
2304
|
+
if (!approved) {
|
|
2305
|
+
ctx.ui.notify("Aborted Pushover priority reset", "info");
|
|
2306
|
+
} else {
|
|
2307
|
+
config["notify-pushover-priority"] = defaultPriority;
|
|
2308
|
+
onConfigChange();
|
|
2309
|
+
ctx.ui.notify("Restored default Pushover priority", "info");
|
|
2310
|
+
}
|
|
2311
|
+
} else if (nextPriority !== undefined) {
|
|
1324
2312
|
config["notify-pushover-priority"] = nextPriority;
|
|
2313
|
+
onConfigChange();
|
|
1325
2314
|
ctx.ui.notify(`Pushover priority set to ${formatPiNotifyPushoverPriority(nextPriority)}`, "info");
|
|
1326
2315
|
}
|
|
1327
2316
|
continue;
|
|
@@ -1333,17 +2322,22 @@ async function configurePiNotifyMenu(
|
|
|
1333
2322
|
value,
|
|
1334
2323
|
DEFAULT_PI_NOTIFY_PUSHOVER_TITLE,
|
|
1335
2324
|
);
|
|
2325
|
+
onConfigChange();
|
|
1336
2326
|
ctx.ui.notify("Updated Pushover title", "info");
|
|
1337
2327
|
}
|
|
1338
2328
|
continue;
|
|
1339
2329
|
}
|
|
1340
2330
|
if (choice === "notify-pushover-text") {
|
|
1341
|
-
const value = await ctx.ui.input(
|
|
2331
|
+
const value = await ctx.ui.input(
|
|
2332
|
+
"Pushover text",
|
|
2333
|
+
formatPiNotifyControlSequenceText(config["notify-pushover-text"]),
|
|
2334
|
+
);
|
|
1342
2335
|
if (value !== undefined) {
|
|
1343
2336
|
config["notify-pushover-text"] = normalizePiNotifyTemplateValue(
|
|
1344
|
-
value,
|
|
2337
|
+
parsePiNotifyControlSequenceText(value),
|
|
1345
2338
|
DEFAULT_PI_NOTIFY_PUSHOVER_TEXT,
|
|
1346
2339
|
);
|
|
2340
|
+
onConfigChange();
|
|
1347
2341
|
ctx.ui.notify("Updated Pushover text", "info");
|
|
1348
2342
|
}
|
|
1349
2343
|
continue;
|
|
@@ -1355,6 +2349,10 @@ async function configurePiNotifyMenu(
|
|
|
1355
2349
|
);
|
|
1356
2350
|
if (value !== undefined) {
|
|
1357
2351
|
config["notify-pushover-user-key"] = normalizePiNotifyPushoverCredential(value);
|
|
2352
|
+
if (!hasPiNotifyPushoverCredentials(config)) {
|
|
2353
|
+
config["notify-pushover-enabled"] = false;
|
|
2354
|
+
}
|
|
2355
|
+
onConfigChange();
|
|
1358
2356
|
ctx.ui.notify("Updated Pushover user key", "info");
|
|
1359
2357
|
}
|
|
1360
2358
|
continue;
|
|
@@ -1366,12 +2364,44 @@ async function configurePiNotifyMenu(
|
|
|
1366
2364
|
);
|
|
1367
2365
|
if (value !== undefined) {
|
|
1368
2366
|
config["notify-pushover-api-token"] = normalizePiNotifyPushoverCredential(value);
|
|
2367
|
+
if (!hasPiNotifyPushoverCredentials(config)) {
|
|
2368
|
+
config["notify-pushover-enabled"] = false;
|
|
2369
|
+
}
|
|
2370
|
+
onConfigChange();
|
|
1369
2371
|
ctx.ui.notify("Updated Pushover API token", "info");
|
|
1370
2372
|
}
|
|
1371
2373
|
continue;
|
|
1372
2374
|
}
|
|
1373
2375
|
if (choice === "reset-defaults") {
|
|
2376
|
+
const defaults = getDefaultConfig("");
|
|
2377
|
+
const resetPreview: ResetConfirmationChange[] = [
|
|
2378
|
+
{ label: "Enable notification", previousValue: formatPiNotifyStatus(config), nextValue: formatPiNotifyStatus(defaults) },
|
|
2379
|
+
{ label: "Enable sound", previousValue: config["notify-sound"], nextValue: defaults["notify-sound"] },
|
|
2380
|
+
{ label: "Sound toggle hotkey bind", previousValue: config["notify-sound-toggle-shortcut"], nextValue: defaults["notify-sound-toggle-shortcut"] },
|
|
2381
|
+
{ label: "Notify command", previousValue: config.PI_NOTIFY_CMD, nextValue: defaults.PI_NOTIFY_CMD },
|
|
2382
|
+
{ label: "Sound command (low vol.)", previousValue: config.PI_NOTIFY_SOUND_LOW_CMD, nextValue: defaults.PI_NOTIFY_SOUND_LOW_CMD },
|
|
2383
|
+
{ label: "Sound command (mid vol.)", previousValue: config.PI_NOTIFY_SOUND_MID_CMD, nextValue: defaults.PI_NOTIFY_SOUND_MID_CMD },
|
|
2384
|
+
{ label: "Sound command (high vol.)", previousValue: config.PI_NOTIFY_SOUND_HIGH_CMD, nextValue: defaults.PI_NOTIFY_SOUND_HIGH_CMD },
|
|
2385
|
+
{ label: "Enable pushover", previousValue: formatPiNotifyPushoverStatus(config), nextValue: formatPiNotifyPushoverStatus(defaults) },
|
|
2386
|
+
{ label: "Pushover priority", previousValue: formatPiNotifyPushoverPriority(config["notify-pushover-priority"]), nextValue: formatPiNotifyPushoverPriority(defaults["notify-pushover-priority"]) },
|
|
2387
|
+
{ label: "Pushover title", previousValue: config["notify-pushover-title"], nextValue: defaults["notify-pushover-title"] },
|
|
2388
|
+
{ label: "Pushover text", previousValue: formatPiNotifyControlSequenceText(config["notify-pushover-text"]), nextValue: formatPiNotifyControlSequenceText(defaults["notify-pushover-text"]) },
|
|
2389
|
+
{ label: "Pushover User Key/Delivery Group Key", previousValue: config["notify-pushover-user-key"] || "(empty)", nextValue: defaults["notify-pushover-user-key"] || "(empty)" },
|
|
2390
|
+
{ label: "Pushover Token/API Token Key", previousValue: config["notify-pushover-api-token"] || "(empty)", nextValue: defaults["notify-pushover-api-token"] || "(empty)" },
|
|
2391
|
+
].filter((change) => change.previousValue !== change.nextValue);
|
|
2392
|
+
const approved = await confirmResetChanges(
|
|
2393
|
+
ctx,
|
|
2394
|
+
"Confirm Notifications reset",
|
|
2395
|
+
resetPreview,
|
|
2396
|
+
"Approve restoring the documented notification defaults.",
|
|
2397
|
+
"Abort the notification reset and keep the current values.",
|
|
2398
|
+
);
|
|
2399
|
+
if (!approved) {
|
|
2400
|
+
ctx.ui.notify("Aborted notification reset", "info");
|
|
2401
|
+
continue;
|
|
2402
|
+
}
|
|
1374
2403
|
resetPiNotifyConfigToDefaults(config);
|
|
2404
|
+
onConfigChange();
|
|
1375
2405
|
ctx.ui.notify("Restored notification defaults", "info");
|
|
1376
2406
|
continue;
|
|
1377
2407
|
}
|
|
@@ -1399,7 +2429,7 @@ function registerPiNotifyShortcut(
|
|
|
1399
2429
|
if (typeof shortcutRegistrar.registerShortcut !== "function") {
|
|
1400
2430
|
return;
|
|
1401
2431
|
}
|
|
1402
|
-
const config = loadProjectConfig(
|
|
2432
|
+
const config = loadProjectConfig(getProcessCwdSafe());
|
|
1403
2433
|
shortcutRegistrar.registerShortcut(config["notify-sound-toggle-shortcut"], {
|
|
1404
2434
|
description: "Cycle pi-usereq notification sound level",
|
|
1405
2435
|
handler: async (ctx) => {
|
|
@@ -1415,110 +2445,141 @@ function registerPiNotifyShortcut(
|
|
|
1415
2445
|
|
|
1416
2446
|
/**
|
|
1417
2447
|
* @brief Registers bundled prompt commands with the extension.
|
|
1418
|
-
* @details Creates one `req-<prompt>` command per bundled prompt name. Each handler
|
|
2448
|
+
* @details Creates one `req-<prompt>` command per bundled prompt name. Each handler rejects non-`idle` workflow state, transitions the shared workflow state through `checking`, `error`, and `running`, runs dedicated prompt-command git and required-doc preflight checks, optionally prepares a dedicated worktree execution plan using the active session directory, persists the prompt metadata needed for switch-triggered rebinding, switches the active session to the verified execution cwd before prompt handoff, logs dedicated workflow-activation diagnostics, renders the prompt, starts prompt delivery into the forked active session, records `running` immediately after delivery handoff begins, and then awaits the wrapped prompt-delivery promise whose stale post-restore rejections are suppressed. Runtime is O(p) for registration; handler cost depends on prompt preflight, worktree preparation, session switching, prompt rendering, prompt dispatch, and optional debug logging. Side effects include command registration, status-controller mutation, worktree creation, active-session replacement, optional worktree rollback, user-message delivery during execution, and optional debug-log writes.
|
|
1419
2449
|
* @param[in] pi {ExtensionAPI} Active extension API instance.
|
|
1420
2450
|
* @param[in,out] statusController {PiUsereqStatusController} Mutable status controller.
|
|
1421
2451
|
* @return {void} No return value.
|
|
1422
|
-
* @satisfies REQ-004, REQ-067, REQ-068, REQ-169
|
|
2452
|
+
* @satisfies REQ-004, REQ-067, REQ-068, REQ-169, REQ-200, REQ-201, REQ-202, REQ-203, REQ-206, REQ-207, REQ-219, REQ-220, REQ-221, REQ-224, REQ-225, REQ-226, REQ-227, REQ-245, REQ-246, REQ-247, REQ-277, REQ-281
|
|
1423
2453
|
*/
|
|
1424
2454
|
function registerPromptCommands(
|
|
1425
2455
|
pi: ExtensionAPI,
|
|
1426
2456
|
statusController: PiUsereqStatusController,
|
|
1427
2457
|
): void {
|
|
1428
|
-
|
|
2458
|
+
PROMPT_COMMAND_NAMES.forEach((promptName) => {
|
|
1429
2459
|
pi.registerCommand(`req-${promptName}`, {
|
|
1430
|
-
description:
|
|
2460
|
+
description: resolvePromptCommandDescription(promptName),
|
|
1431
2461
|
handler: async (args, ctx) => {
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
2462
|
+
if (statusController.state.workflowState !== "idle") {
|
|
2463
|
+
const message = `ERROR: Prompt workflow state is ${statusController.state.workflowState}, expected idle.`;
|
|
2464
|
+
ctx.ui.notify(message, "error");
|
|
2465
|
+
throw new ReqError(message, 1);
|
|
2466
|
+
}
|
|
2467
|
+
const commandCwd = resolveLiveBootstrapCwd(ctx.cwd);
|
|
2468
|
+
syncContextCwdMirror(ctx, commandCwd);
|
|
2469
|
+
bootstrapRuntimePathState(commandCwd, {
|
|
2470
|
+
gitPath: resolveRuntimeGitPath(commandCwd),
|
|
2471
|
+
});
|
|
2472
|
+
const projectBase = getProjectBase(commandCwd);
|
|
2473
|
+
const config = loadProjectConfig(commandCwd);
|
|
2474
|
+
transitionPromptWorkflowState(
|
|
2475
|
+
statusController,
|
|
2476
|
+
ctx,
|
|
2477
|
+
projectBase,
|
|
2478
|
+
config,
|
|
1436
2479
|
promptName,
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
2480
|
+
"checking",
|
|
2481
|
+
);
|
|
2482
|
+
ensureBundledResourcesAccessible();
|
|
2483
|
+
let executionPlan: PromptCommandExecutionPlan | undefined;
|
|
2484
|
+
let promptContext = ctx;
|
|
2485
|
+
try {
|
|
2486
|
+
executionPlan = preparePromptCommandExecution(
|
|
2487
|
+
promptName,
|
|
2488
|
+
args,
|
|
2489
|
+
projectBase,
|
|
2490
|
+
config,
|
|
2491
|
+
ctx.sessionManager.getSessionFile(),
|
|
2492
|
+
ctx.sessionManager.getSessionDir?.(),
|
|
2493
|
+
ctx.sessionManager.getBranch?.(),
|
|
2494
|
+
{
|
|
2495
|
+
config,
|
|
2496
|
+
workflowState: statusController.state.workflowState,
|
|
2497
|
+
},
|
|
2498
|
+
);
|
|
2499
|
+
const content = renderPrompt(
|
|
2500
|
+
promptName,
|
|
2501
|
+
args,
|
|
2502
|
+
projectBase,
|
|
2503
|
+
config,
|
|
2504
|
+
executionPlan,
|
|
2505
|
+
);
|
|
2506
|
+
statusController.state.pendingPromptRequest = executionPlan;
|
|
2507
|
+
writePersistedPromptCommandRuntimeState({
|
|
2508
|
+
workflowState: statusController.state.workflowState,
|
|
2509
|
+
pendingPromptRequest: statusController.state.pendingPromptRequest,
|
|
2510
|
+
activePromptRequest: statusController.state.activePromptRequest,
|
|
2511
|
+
});
|
|
2512
|
+
promptContext = (await activatePromptCommandExecution(executionPlan, ctx) ?? ctx) as typeof ctx;
|
|
2513
|
+
logPromptWorkflowEvent(
|
|
2514
|
+
projectBase,
|
|
2515
|
+
config,
|
|
2516
|
+
statusController.state.workflowState,
|
|
2517
|
+
promptName,
|
|
2518
|
+
"workflow_activation",
|
|
2519
|
+
{
|
|
2520
|
+
execution_session_file: executionPlan.executionSessionFile,
|
|
2521
|
+
context_path: executionPlan.contextPath,
|
|
2522
|
+
},
|
|
2523
|
+
{
|
|
2524
|
+
success: true,
|
|
2525
|
+
context_path: promptContext.cwd ?? executionPlan.contextPath,
|
|
2526
|
+
prompt_context_has_switch_session: typeof promptContext.switchSession === "function",
|
|
2527
|
+
},
|
|
2528
|
+
);
|
|
2529
|
+
renderPiUsereqStatus(statusController, promptContext);
|
|
2530
|
+
const promptDelivery = deliverPromptCommand(pi, content, promptContext);
|
|
2531
|
+
transitionPromptWorkflowState(
|
|
2532
|
+
statusController,
|
|
2533
|
+
promptContext,
|
|
2534
|
+
projectBase,
|
|
2535
|
+
config,
|
|
2536
|
+
promptName,
|
|
2537
|
+
"running",
|
|
2538
|
+
);
|
|
2539
|
+
await promptDelivery;
|
|
2540
|
+
} catch (error) {
|
|
2541
|
+
if (shouldIgnoreLatePromptDeliveryFailure(error, statusController.state.workflowState, executionPlan)) {
|
|
2542
|
+
return;
|
|
2543
|
+
}
|
|
2544
|
+
promptContext = (getPromptCommandErrorContext(error) ?? promptContext) as typeof ctx;
|
|
2545
|
+
statusController.state.pendingPromptRequest = undefined;
|
|
2546
|
+
statusController.state.activePromptRequest = undefined;
|
|
2547
|
+
transitionPromptWorkflowState(
|
|
2548
|
+
statusController,
|
|
2549
|
+
promptContext,
|
|
2550
|
+
projectBase,
|
|
2551
|
+
config,
|
|
2552
|
+
promptName,
|
|
2553
|
+
"error",
|
|
2554
|
+
);
|
|
2555
|
+
if (executionPlan !== undefined) {
|
|
2556
|
+
const abortResult = await abortPromptCommandExecution(executionPlan, promptContext, {
|
|
2557
|
+
config,
|
|
2558
|
+
workflowState: statusController.state.workflowState,
|
|
2559
|
+
});
|
|
2560
|
+
promptContext = (abortResult.activeContext ?? promptContext) as typeof ctx;
|
|
2561
|
+
if (!abortResult.cleanupSucceeded && abortResult.errorMessage) {
|
|
2562
|
+
notifyContextSafely(promptContext, abortResult.errorMessage, "error");
|
|
2563
|
+
}
|
|
2564
|
+
}
|
|
2565
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2566
|
+
notifyContextSafely(promptContext, message, "error");
|
|
2567
|
+
throw error;
|
|
2568
|
+
}
|
|
1441
2569
|
},
|
|
1442
2570
|
});
|
|
1443
2571
|
});
|
|
1444
2572
|
}
|
|
1445
2573
|
|
|
1446
2574
|
|
|
1447
|
-
/**
|
|
1448
|
-
* @brief Registers pi-usereq agent tools exposed to the model.
|
|
1449
|
-
* @details Defines the tool schemas, prompt metadata, and execution handlers that bridge extension tool calls into tool-runner operations without registering duplicate custom slash commands for the same capabilities. Runtime is O(t) for registration; execution cost depends on the selected tool. Side effects include tool registration.
|
|
1450
|
-
* @param[in] pi {ExtensionAPI} Active extension API instance.
|
|
1451
|
-
* @return {void} No return value.
|
|
1452
|
-
* @satisfies REQ-005, REQ-010, REQ-011, REQ-014, REQ-017, REQ-044, REQ-
|
|
1453
|
-
*/
|
|
1454
|
-
function registerAgentTools(pi: ExtensionAPI): void {
|
|
1455
|
-
const gitPathSchema = Type.Object(
|
|
1456
|
-
{},
|
|
1457
|
-
{
|
|
1458
|
-
description: "Input contract: no params. Output contract: JSON object with result and execution. Result exposes path_value and path_present for the cwd-derived runtime git root.",
|
|
1459
|
-
},
|
|
1460
|
-
);
|
|
1461
|
-
pi.registerTool({
|
|
1462
|
-
name: "git-path",
|
|
1463
|
-
label: "git-path",
|
|
1464
|
-
description: "Scope: current runtime path. Return a token-optimized JSON payload with result and execution sections. Result exposes the resolved `git-path` value through direct-access fields without request echoes.",
|
|
1465
|
-
promptSnippet: "Return the structured runtime git-root payload for the current project.",
|
|
1466
|
-
promptGuidelines: [
|
|
1467
|
-
"Input contract: no params. Scope is the cwd-derived runtime path context.",
|
|
1468
|
-
"Output contract: result + execution. Result exposes path_value and path_present.",
|
|
1469
|
-
"Behavior contract: git-path is derived at runtime from the current working directory and repository ancestry rules.",
|
|
1470
|
-
"Failure contract: configuration-loading failures surface through execution.code and execution.stderr_lines.",
|
|
1471
|
-
],
|
|
1472
|
-
parameters: gitPathSchema,
|
|
1473
|
-
async execute() {
|
|
1474
|
-
ensureBundledResourcesAccessible();
|
|
1475
|
-
const projectBase = getProjectBase(process.cwd());
|
|
1476
|
-
const config = loadProjectConfig(process.cwd());
|
|
1477
|
-
const result = runGitPath(projectBase, config);
|
|
1478
|
-
const payload = buildPathQueryToolPayload(
|
|
1479
|
-
"git-path",
|
|
1480
|
-
process.cwd(),
|
|
1481
|
-
projectBase,
|
|
1482
|
-
result.stdout.trimEnd(),
|
|
1483
|
-
buildToolExecutionSection(result),
|
|
1484
|
-
);
|
|
1485
|
-
return buildStructuredToolExecuteResult(payload);
|
|
1486
|
-
},
|
|
1487
|
-
});
|
|
1488
|
-
|
|
1489
|
-
const basePathSchema = Type.Object(
|
|
1490
|
-
{},
|
|
1491
|
-
{
|
|
1492
|
-
description: "Input contract: no params. Output contract: JSON object with result and execution. Result exposes path_value and path_present for the cwd-derived runtime base path.",
|
|
1493
|
-
},
|
|
1494
|
-
);
|
|
1495
|
-
pi.registerTool({
|
|
1496
|
-
name: "get-base-path",
|
|
1497
|
-
label: "get-base-path",
|
|
1498
|
-
description: "Scope: current runtime path. Return a token-optimized JSON payload with result and execution sections. Result exposes the resolved `base-path` value through direct-access fields without request echoes.",
|
|
1499
|
-
promptSnippet: "Return the structured runtime project-base payload.",
|
|
1500
|
-
promptGuidelines: [
|
|
1501
|
-
"Input contract: no params. Scope is the cwd-derived runtime path context.",
|
|
1502
|
-
"Output contract: result + execution. Result exposes path_value and path_present.",
|
|
1503
|
-
"Behavior contract: base-path equals the current working directory used by the extension command or tool.",
|
|
1504
|
-
"Failure contract: configuration-loading failures surface through execution.code and execution.stderr_lines.",
|
|
1505
|
-
],
|
|
1506
|
-
parameters: basePathSchema,
|
|
1507
|
-
async execute() {
|
|
1508
|
-
const projectBase = getProjectBase(process.cwd());
|
|
1509
|
-
const config = loadProjectConfig(process.cwd());
|
|
1510
|
-
const result = runGetBasePath(projectBase, config);
|
|
1511
|
-
const payload = buildPathQueryToolPayload(
|
|
1512
|
-
"get-base-path",
|
|
1513
|
-
process.cwd(),
|
|
1514
|
-
projectBase,
|
|
1515
|
-
result.stdout.trimEnd(),
|
|
1516
|
-
buildToolExecutionSection(result),
|
|
1517
|
-
);
|
|
1518
|
-
return buildStructuredToolExecuteResult(payload);
|
|
1519
|
-
},
|
|
1520
|
-
});
|
|
1521
|
-
|
|
2575
|
+
/**
|
|
2576
|
+
* @brief Registers pi-usereq agent tools exposed to the model.
|
|
2577
|
+
* @details Defines the tool schemas, prompt metadata, and execution handlers that bridge extension tool calls into tool-runner operations without registering duplicate custom slash commands for the same capabilities. Runtime is O(t) for registration; execution cost depends on the selected tool. Side effects include tool registration.
|
|
2578
|
+
* @param[in] pi {ExtensionAPI} Active extension API instance.
|
|
2579
|
+
* @return {void} No return value.
|
|
2580
|
+
* @satisfies REQ-005, REQ-010, REQ-011, REQ-014, REQ-017, REQ-044, REQ-069, REQ-070, REQ-071, REQ-072, REQ-073, REQ-074, REQ-075, REQ-076, REQ-077, REQ-078, REQ-079, REQ-080, REQ-089, REQ-090, REQ-091, REQ-092, REQ-093, REQ-094, REQ-095, REQ-096, REQ-097, REQ-098, REQ-099, REQ-100, REQ-101, REQ-102
|
|
2581
|
+
*/
|
|
2582
|
+
function registerAgentTools(pi: ExtensionAPI): void {
|
|
1522
2583
|
const filesReferencesSchema = Type.Object(
|
|
1523
2584
|
{
|
|
1524
2585
|
files: Type.Array(
|
|
@@ -1527,7 +2588,7 @@ function registerAgentTools(pi: ExtensionAPI): void {
|
|
|
1527
2588
|
),
|
|
1528
2589
|
},
|
|
1529
2590
|
{
|
|
1530
|
-
description: "Input contract: files[]. Output contract:
|
|
2591
|
+
description: "Input contract: files[]. Output contract: monolithic markdown in content[0].text plus details.execution diagnostics. Missing or unsupported inputs surface through execution diagnostics. The tool fails when no source file can be analyzed.",
|
|
1531
2592
|
},
|
|
1532
2593
|
);
|
|
1533
2594
|
const multiFileSchema = Type.Object(
|
|
@@ -1538,7 +2599,7 @@ function registerAgentTools(pi: ExtensionAPI): void {
|
|
|
1538
2599
|
),
|
|
1539
2600
|
},
|
|
1540
2601
|
{
|
|
1541
|
-
description: "Input contract: files[]. Output contract:
|
|
2602
|
+
description: "Input contract: files[]. Output contract: monolithic text in content[0].text plus details.execution diagnostics.",
|
|
1542
2603
|
},
|
|
1543
2604
|
);
|
|
1544
2605
|
const filesTokensSchema = Type.Object(
|
|
@@ -1549,58 +2610,43 @@ function registerAgentTools(pi: ExtensionAPI): void {
|
|
|
1549
2610
|
),
|
|
1550
2611
|
},
|
|
1551
2612
|
{
|
|
1552
|
-
description: "Input contract: files[]. Output contract:
|
|
2613
|
+
description: "Input contract: files[]. Output contract: monolithic pack-summary text in content[0].text plus details.execution diagnostics. The tool fails when no processable files remain.",
|
|
1553
2614
|
},
|
|
1554
2615
|
);
|
|
1555
2616
|
|
|
1556
2617
|
pi.registerTool({
|
|
1557
2618
|
name: "files-tokens",
|
|
1558
2619
|
label: "files-tokens",
|
|
1559
|
-
description: "Scope: explicit files. Return
|
|
1560
|
-
promptSnippet: "Return the
|
|
2620
|
+
description: "Scope: explicit files. Return the monolithic token pack summary in content[0].text and keep only execution metadata in details.execution.",
|
|
2621
|
+
promptSnippet: "Return the monolithic token summary for caller-selected files.",
|
|
1561
2622
|
promptGuidelines: [
|
|
1562
2623
|
"Scope: explicit files selected by files[]; caller order is preserved; each item may be project-relative or absolute.",
|
|
1563
|
-
"Output contract: summary
|
|
1564
|
-
"
|
|
1565
|
-
"Behavior contract: missing or non-file inputs become skipped entries; read failures become error entries; the tool fails only when no processable files remain.",
|
|
2624
|
+
"Output contract: monolithic pack-summary text in content[0].text; details.execution preserves only exit code and residual diagnostics.",
|
|
2625
|
+
"Behavior contract: missing or non-file inputs surface through details.execution diagnostics; the tool fails when no processable files remain.",
|
|
1566
2626
|
],
|
|
2627
|
+
renderResult: buildStructuredToolRenderResult("files-tokens"),
|
|
1567
2628
|
parameters: filesTokensSchema,
|
|
1568
2629
|
async execute(_toolCallId, params) {
|
|
1569
|
-
|
|
1570
|
-
const payload = buildTokenToolPayload({
|
|
1571
|
-
toolName: "files-tokens",
|
|
1572
|
-
scope: "explicit-files",
|
|
1573
|
-
baseDir: process.cwd(),
|
|
1574
|
-
requestedPaths: params.files,
|
|
1575
|
-
encodingName: TOKEN_COUNTER_ENCODING,
|
|
1576
|
-
});
|
|
1577
|
-
return buildTokenToolExecuteResult(payload);
|
|
1578
|
-
} catch (error) {
|
|
1579
|
-
return buildFailedTokenToolExecuteResult(error);
|
|
1580
|
-
}
|
|
2630
|
+
return executeMonolithicTool(() => runFilesTokens(params.files));
|
|
1581
2631
|
},
|
|
1582
2632
|
});
|
|
1583
2633
|
|
|
1584
2634
|
pi.registerTool({
|
|
1585
2635
|
name: "files-references",
|
|
1586
2636
|
label: "files-references",
|
|
1587
|
-
description: "Scope: explicit source files. Return
|
|
1588
|
-
promptSnippet: "Return the
|
|
2637
|
+
description: "Scope: explicit source files. Return the monolithic references markdown report in content[0].text and keep only execution metadata in details.execution.",
|
|
2638
|
+
promptSnippet: "Return the monolithic references markdown report for caller-selected source files.",
|
|
1589
2639
|
promptGuidelines: [
|
|
1590
2640
|
"Scope: explicit source files selected by files[]; caller order is preserved; each item may be project-relative or absolute.",
|
|
1591
|
-
"Output contract:
|
|
1592
|
-
"
|
|
1593
|
-
"Behavior contract: missing inputs, non-file inputs,
|
|
2641
|
+
"Output contract: monolithic markdown in content[0].text; details.execution preserves only exit code and residual diagnostics.",
|
|
2642
|
+
"Formatting contract: content matches the Python reference renderer used by `generate_markdown.py`.",
|
|
2643
|
+
"Behavior contract: missing inputs, non-file inputs, unsupported extensions, and analysis failures surface through details.execution diagnostics.",
|
|
1594
2644
|
],
|
|
2645
|
+
renderResult: buildStructuredToolRenderResult("files-references"),
|
|
1595
2646
|
parameters: filesReferencesSchema,
|
|
1596
2647
|
async execute(_toolCallId, params) {
|
|
1597
|
-
const
|
|
1598
|
-
|
|
1599
|
-
scope: "explicit-files",
|
|
1600
|
-
baseDir: process.cwd(),
|
|
1601
|
-
requestedPaths: params.files,
|
|
1602
|
-
});
|
|
1603
|
-
return buildReferenceToolExecuteResult(payload);
|
|
2648
|
+
const contextPath = getRuntimeContextPath(process.cwd());
|
|
2649
|
+
return executeMonolithicTool(() => runFilesReferences(params.files, contextPath));
|
|
1604
2650
|
},
|
|
1605
2651
|
});
|
|
1606
2652
|
|
|
@@ -1610,38 +2656,37 @@ function registerAgentTools(pi: ExtensionAPI): void {
|
|
|
1610
2656
|
Type.String({ description: "Project-relative or absolute source file path resolved from the current working directory when not already absolute" }),
|
|
1611
2657
|
{ description: "Explicit source-file list preserved in caller order" },
|
|
1612
2658
|
),
|
|
1613
|
-
enableLineNumbers: Type.Optional(Type.Boolean({ description: "When true,
|
|
2659
|
+
enableLineNumbers: Type.Optional(Type.Boolean({ description: "When true, fenced code block lines include original source line-number prefixes" })),
|
|
1614
2660
|
},
|
|
1615
2661
|
{
|
|
1616
|
-
description: "Input contract: files[] plus optional enableLineNumbers. Output contract:
|
|
2662
|
+
description: "Input contract: files[] plus optional enableLineNumbers. Output contract: monolithic markdown in content[0].text plus details.execution diagnostics. The tool fails when no file is compressed.",
|
|
1617
2663
|
},
|
|
1618
2664
|
);
|
|
1619
2665
|
|
|
1620
2666
|
pi.registerTool({
|
|
1621
2667
|
name: "files-compress",
|
|
1622
2668
|
label: "files-compress",
|
|
1623
|
-
description: "Scope: explicit files. Return
|
|
1624
|
-
promptSnippet: "Return the
|
|
2669
|
+
description: "Scope: explicit files. Return the monolithic compression markdown report in content[0].text and keep only execution metadata in details.execution.",
|
|
2670
|
+
promptSnippet: "Return the monolithic compression markdown report for caller-selected source files.",
|
|
1625
2671
|
promptGuidelines: [
|
|
1626
2672
|
"Scope: explicit source files selected by files[]; caller order is preserved; each item may be project-relative or absolute.",
|
|
1627
|
-
"Output contract:
|
|
1628
|
-
"
|
|
1629
|
-
"
|
|
2673
|
+
"Output contract: monolithic markdown in content[0].text; details.execution preserves only exit code and residual diagnostics.",
|
|
2674
|
+
"Formatting contract: output uses `@@@ <path> | <language>` headers, `> Lines:` metadata, and fenced code blocks matching `compress_files.py`.",
|
|
2675
|
+
"Line-number behavior: enableLineNumbers toggles original source line prefixes inside fenced code blocks.",
|
|
1630
2676
|
],
|
|
2677
|
+
renderResult: buildStructuredToolRenderResult("files-compress"),
|
|
1631
2678
|
parameters: filesCompressSchema,
|
|
1632
2679
|
async execute(_toolCallId, params) {
|
|
1633
|
-
const
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
});
|
|
1640
|
-
return buildCompressionToolExecuteResult(payload);
|
|
2680
|
+
const contextPath = getRuntimeContextPath(process.cwd());
|
|
2681
|
+
return executeMonolithicTool(() => runFilesCompress(
|
|
2682
|
+
params.files,
|
|
2683
|
+
contextPath,
|
|
2684
|
+
params.enableLineNumbers ?? false,
|
|
2685
|
+
));
|
|
1641
2686
|
},
|
|
1642
2687
|
});
|
|
1643
2688
|
|
|
1644
|
-
const
|
|
2689
|
+
const filesSearchSchema = Type.Object(
|
|
1645
2690
|
{
|
|
1646
2691
|
tag: Type.String({ description: "Pipe-separated construct-tag filter applied case-insensitively; unsupported tags are ignored" }),
|
|
1647
2692
|
pattern: Type.String({ description: "JavaScript RegExp applied to construct names only; use ^...$ for exact-name matching" }),
|
|
@@ -1649,434 +2694,198 @@ function registerAgentTools(pi: ExtensionAPI): void {
|
|
|
1649
2694
|
Type.String({ description: "Project-relative or absolute source file path resolved from the current working directory when not already absolute" }),
|
|
1650
2695
|
{ description: "Explicit source-file list preserved in caller order" },
|
|
1651
2696
|
),
|
|
1652
|
-
enableLineNumbers: Type.Optional(Type.Boolean({ description: "When true,
|
|
2697
|
+
enableLineNumbers: Type.Optional(Type.Boolean({ description: "When true, fenced code block lines include original source line-number prefixes" })),
|
|
1653
2698
|
},
|
|
1654
2699
|
{
|
|
1655
|
-
description:
|
|
2700
|
+
description: buildSearchToolSchemaDescription("explicit-files"),
|
|
1656
2701
|
},
|
|
1657
2702
|
);
|
|
1658
2703
|
|
|
1659
2704
|
pi.registerTool({
|
|
1660
|
-
name: "files-
|
|
1661
|
-
label: "files-
|
|
1662
|
-
description: "Scope: explicit source files. Return
|
|
1663
|
-
promptSnippet: "Return the
|
|
1664
|
-
promptGuidelines:
|
|
1665
|
-
|
|
2705
|
+
name: "files-search",
|
|
2706
|
+
label: "files-search",
|
|
2707
|
+
description: "Scope: explicit source files. Return the monolithic construct-search markdown report in content[0].text and keep only execution metadata in details.execution.",
|
|
2708
|
+
promptSnippet: "Return the monolithic construct-search markdown report for caller-selected source files.",
|
|
2709
|
+
promptGuidelines: buildSearchToolPromptGuidelines("explicit-files"),
|
|
2710
|
+
renderResult: buildStructuredToolRenderResult("files-search"),
|
|
2711
|
+
parameters: filesSearchSchema,
|
|
1666
2712
|
async execute(_toolCallId, params) {
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
tagFilter: params.tag,
|
|
1672
|
-
pattern: params.pattern,
|
|
1673
|
-
requestedPaths: params.files,
|
|
1674
|
-
includeLineNumbers: params.enableLineNumbers ?? false,
|
|
1675
|
-
});
|
|
1676
|
-
return buildFindToolExecuteResult(payload);
|
|
2713
|
+
return executeMonolithicTool(() => runFilesSearch(
|
|
2714
|
+
[params.tag, params.pattern, ...params.files],
|
|
2715
|
+
params.enableLineNumbers ?? false,
|
|
2716
|
+
));
|
|
1677
2717
|
},
|
|
1678
2718
|
});
|
|
1679
2719
|
|
|
1680
2720
|
const referencesSchema = Type.Object(
|
|
1681
2721
|
{},
|
|
1682
2722
|
{
|
|
1683
|
-
description: "Input contract: no params. Scope is the configured src-dir list resolved from the current project configuration. Output contract:
|
|
2723
|
+
description: "Input contract: no params. Scope is the configured src-dir list resolved from the current project configuration. Output contract: monolithic markdown in content[0].text plus details.execution diagnostics.",
|
|
1684
2724
|
},
|
|
1685
2725
|
);
|
|
1686
2726
|
const tokensSchema = Type.Object(
|
|
1687
2727
|
{},
|
|
1688
2728
|
{
|
|
1689
|
-
description: "Input contract: no params. Scope is the configured docs-dir plus canonical docs REQUIREMENTS.md, WORKFLOW.md, and REFERENCES.md. Output contract:
|
|
2729
|
+
description: "Input contract: no params. Scope is the configured docs-dir plus canonical docs REQUIREMENTS.md, WORKFLOW.md, and REFERENCES.md. Output contract: monolithic pack-summary text in content[0].text plus details.execution diagnostics.",
|
|
1690
2730
|
},
|
|
1691
2731
|
);
|
|
1692
2732
|
|
|
1693
2733
|
pi.registerTool({
|
|
1694
2734
|
name: "references",
|
|
1695
2735
|
label: "references",
|
|
1696
|
-
description: "Scope: configured project source directories. Return
|
|
1697
|
-
promptSnippet: "Return the
|
|
2736
|
+
description: "Scope: configured project source directories. Return the monolithic references markdown report in content[0].text and keep only execution metadata in details.execution.",
|
|
2737
|
+
promptSnippet: "Return the monolithic project references markdown report from the configured source directories.",
|
|
1698
2738
|
promptGuidelines: [
|
|
1699
2739
|
"Scope: no params; resolve src-dir from the current project configuration and scan the configured source surface from the current working directory.",
|
|
1700
|
-
"Output contract:
|
|
1701
|
-
"
|
|
1702
|
-
"
|
|
2740
|
+
"Output contract: monolithic markdown in content[0].text; details.execution preserves only exit code and residual diagnostics.",
|
|
2741
|
+
"Formatting contract: content prepends the file-structure markdown block before the per-file markdown produced by `generate_markdown.py`.",
|
|
2742
|
+
"Configuration contract: output changes with cwd-derived project config and src-dir values; the tool does not accept explicit file overrides.",
|
|
1703
2743
|
],
|
|
2744
|
+
renderResult: buildStructuredToolRenderResult("references"),
|
|
1704
2745
|
parameters: referencesSchema,
|
|
1705
2746
|
async execute() {
|
|
1706
|
-
const
|
|
1707
|
-
const
|
|
1708
|
-
const
|
|
1709
|
-
|
|
1710
|
-
scope: "configured-source-directories",
|
|
1711
|
-
baseDir: projectBase,
|
|
1712
|
-
requestedPaths: collectSourceFiles(config["src-dir"], projectBase),
|
|
1713
|
-
sourceDirectoryPaths: config["src-dir"],
|
|
1714
|
-
});
|
|
1715
|
-
return buildReferenceToolExecuteResult(payload);
|
|
2747
|
+
const contextPath = getRuntimeContextPath(process.cwd());
|
|
2748
|
+
const projectBase = getProjectBase(contextPath);
|
|
2749
|
+
const config = loadProjectConfig(projectBase);
|
|
2750
|
+
return executeMonolithicTool(() => runReferences(contextPath, config));
|
|
1716
2751
|
},
|
|
1717
2752
|
});
|
|
1718
2753
|
|
|
1719
2754
|
const compressSchema = Type.Object(
|
|
1720
2755
|
{
|
|
1721
|
-
enableLineNumbers: Type.Optional(Type.Boolean({ description: "When true,
|
|
2756
|
+
enableLineNumbers: Type.Optional(Type.Boolean({ description: "When true, fenced code block lines include original source line-number prefixes" })),
|
|
1722
2757
|
},
|
|
1723
2758
|
{
|
|
1724
|
-
description: "Input contract: optional enableLineNumbers boolean. Scope is the configured src-dir list resolved from the current project configuration. Output contract:
|
|
2759
|
+
description: "Input contract: optional enableLineNumbers boolean. Scope is the configured src-dir list resolved from the current project configuration. Output contract: monolithic markdown in content[0].text plus details.execution diagnostics.",
|
|
1725
2760
|
},
|
|
1726
2761
|
);
|
|
1727
2762
|
|
|
1728
2763
|
pi.registerTool({
|
|
1729
2764
|
name: "compress",
|
|
1730
2765
|
label: "compress",
|
|
1731
|
-
description: "Scope: configured project source directories. Return
|
|
1732
|
-
promptSnippet: "Return the
|
|
2766
|
+
description: "Scope: configured project source directories. Return the monolithic compression markdown report in content[0].text and keep only execution metadata in details.execution.",
|
|
2767
|
+
promptSnippet: "Return the monolithic project compression markdown report from the configured source directories.",
|
|
1733
2768
|
promptGuidelines: [
|
|
1734
2769
|
"Scope: resolve src-dir from the current project configuration and scan the configured source surface from the current working directory.",
|
|
1735
|
-
"Output contract:
|
|
1736
|
-
"
|
|
1737
|
-
"
|
|
2770
|
+
"Output contract: monolithic markdown in content[0].text; details.execution preserves only exit code and residual diagnostics.",
|
|
2771
|
+
"Formatting contract: output uses `@@@ <path> | <language>` headers, `> Lines:` metadata, and fenced code blocks matching `compress_files.py`.",
|
|
2772
|
+
"Line-number behavior: enableLineNumbers toggles original source line prefixes inside fenced code blocks.",
|
|
1738
2773
|
],
|
|
2774
|
+
renderResult: buildStructuredToolRenderResult("compress"),
|
|
1739
2775
|
parameters: compressSchema,
|
|
1740
2776
|
async execute(_toolCallId, params) {
|
|
1741
|
-
const
|
|
1742
|
-
const
|
|
1743
|
-
const
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
includeLineNumbers: params.enableLineNumbers ?? false,
|
|
1750
|
-
sourceDirectoryPaths: config["src-dir"],
|
|
1751
|
-
});
|
|
1752
|
-
return buildCompressionToolExecuteResult(payload);
|
|
2777
|
+
const contextPath = getRuntimeContextPath(process.cwd());
|
|
2778
|
+
const projectBase = getProjectBase(contextPath);
|
|
2779
|
+
const config = loadProjectConfig(projectBase);
|
|
2780
|
+
return executeMonolithicTool(() => runCompress(
|
|
2781
|
+
contextPath,
|
|
2782
|
+
config,
|
|
2783
|
+
params.enableLineNumbers ?? false,
|
|
2784
|
+
));
|
|
1753
2785
|
},
|
|
1754
2786
|
});
|
|
1755
2787
|
|
|
1756
|
-
const
|
|
2788
|
+
const searchSchema = Type.Object(
|
|
1757
2789
|
{
|
|
1758
2790
|
tag: Type.String({ description: "Pipe-separated construct-tag filter applied case-insensitively; unsupported tags are ignored" }),
|
|
1759
2791
|
pattern: Type.String({ description: "JavaScript RegExp applied to construct names only; use ^...$ for exact-name matching" }),
|
|
1760
|
-
enableLineNumbers: Type.Optional(Type.Boolean({ description: "When true,
|
|
2792
|
+
enableLineNumbers: Type.Optional(Type.Boolean({ description: "When true, fenced code block lines include original source line-number prefixes" })),
|
|
1761
2793
|
},
|
|
1762
2794
|
{
|
|
1763
|
-
description:
|
|
2795
|
+
description: buildSearchToolSchemaDescription("configured-source-directories"),
|
|
1764
2796
|
},
|
|
1765
2797
|
);
|
|
1766
2798
|
|
|
1767
2799
|
pi.registerTool({
|
|
1768
|
-
name: "
|
|
1769
|
-
label: "
|
|
1770
|
-
description: "Scope: configured project source directories. Return
|
|
1771
|
-
promptSnippet: "Return the
|
|
1772
|
-
promptGuidelines:
|
|
1773
|
-
|
|
2800
|
+
name: "search",
|
|
2801
|
+
label: "search",
|
|
2802
|
+
description: "Scope: configured project source directories. Return the monolithic construct-search markdown report in content[0].text and keep only execution metadata in details.execution.",
|
|
2803
|
+
promptSnippet: "Return the monolithic construct-search markdown report from the configured source directories.",
|
|
2804
|
+
promptGuidelines: buildSearchToolPromptGuidelines("configured-source-directories"),
|
|
2805
|
+
renderResult: buildStructuredToolRenderResult("search"),
|
|
2806
|
+
parameters: searchSchema,
|
|
1774
2807
|
async execute(_toolCallId, params) {
|
|
1775
|
-
const
|
|
1776
|
-
const
|
|
1777
|
-
const
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
includeLineNumbers: params.enableLineNumbers ?? false,
|
|
1786
|
-
sourceDirectoryPaths: config["src-dir"],
|
|
1787
|
-
});
|
|
1788
|
-
return buildFindToolExecuteResult(payload);
|
|
2808
|
+
const contextPath = getRuntimeContextPath(process.cwd());
|
|
2809
|
+
const projectBase = getProjectBase(contextPath);
|
|
2810
|
+
const config = loadProjectConfig(projectBase);
|
|
2811
|
+
return executeMonolithicTool(() => runSearch(
|
|
2812
|
+
contextPath,
|
|
2813
|
+
params.tag,
|
|
2814
|
+
params.pattern,
|
|
2815
|
+
config,
|
|
2816
|
+
params.enableLineNumbers ?? false,
|
|
2817
|
+
));
|
|
1789
2818
|
},
|
|
1790
2819
|
});
|
|
1791
2820
|
|
|
1792
2821
|
pi.registerTool({
|
|
1793
2822
|
name: "tokens",
|
|
1794
2823
|
label: "tokens",
|
|
1795
|
-
description: "Scope: canonical docs from the configured docs-dir. Return the
|
|
1796
|
-
promptSnippet: "Return the
|
|
2824
|
+
description: "Scope: canonical docs from the configured docs-dir. Return the monolithic token pack summary in content[0].text and keep only execution metadata in details.execution.",
|
|
2825
|
+
promptSnippet: "Return the monolithic token summary for canonical documentation files.",
|
|
1797
2826
|
promptGuidelines: [
|
|
1798
2827
|
"Scope: no params; resolve docs-dir from project config; target canonical docs REQUIREMENTS.md, WORKFLOW.md, and REFERENCES.md.",
|
|
1799
|
-
"Output contract:
|
|
1800
|
-
"
|
|
1801
|
-
"Behavior contract: missing canonical docs become skipped entries; read failures become error entries; the tool fails only when no processable canonical docs remain.",
|
|
2828
|
+
"Output contract: monolithic pack-summary text in content[0].text; details.execution preserves only exit code and residual diagnostics.",
|
|
2829
|
+
"Behavior contract: missing canonical docs surface through details.execution diagnostics; the tool fails when no processable canonical docs remain.",
|
|
1802
2830
|
],
|
|
2831
|
+
renderResult: buildStructuredToolRenderResult("tokens"),
|
|
1803
2832
|
parameters: tokensSchema,
|
|
1804
2833
|
async execute() {
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
const canonicalDocNames = ["REQUIREMENTS.md", "WORKFLOW.md", "REFERENCES.md"];
|
|
1810
|
-
const payload = buildTokenToolPayload({
|
|
1811
|
-
toolName: "tokens",
|
|
1812
|
-
scope: "canonical-docs",
|
|
1813
|
-
baseDir: projectBase,
|
|
1814
|
-
requestedPaths: canonicalDocNames.map((name) => path.join(docsDir, name)),
|
|
1815
|
-
docsDir,
|
|
1816
|
-
canonicalDocNames,
|
|
1817
|
-
encodingName: TOKEN_COUNTER_ENCODING,
|
|
1818
|
-
});
|
|
1819
|
-
return buildTokenToolExecuteResult(payload);
|
|
1820
|
-
} catch (error) {
|
|
1821
|
-
return buildFailedTokenToolExecuteResult(error);
|
|
1822
|
-
}
|
|
2834
|
+
const contextPath = getRuntimeContextPath(process.cwd());
|
|
2835
|
+
const projectBase = getProjectBase(contextPath);
|
|
2836
|
+
const config = loadProjectConfig(projectBase);
|
|
2837
|
+
return executeMonolithicTool(() => runTokens(contextPath, config));
|
|
1823
2838
|
},
|
|
1824
2839
|
});
|
|
1825
2840
|
|
|
1826
2841
|
pi.registerTool({
|
|
1827
2842
|
name: "files-static-check",
|
|
1828
2843
|
label: "files-static-check",
|
|
1829
|
-
description: "Scope: explicit files. Return
|
|
1830
|
-
promptSnippet: "Return the
|
|
2844
|
+
description: "Scope: explicit files. Return the monolithic static-check report in content[0].text and keep only execution metadata in details.execution.",
|
|
2845
|
+
promptSnippet: "Return the monolithic explicit-file static-check report for the current project configuration.",
|
|
1831
2846
|
promptGuidelines: [
|
|
1832
2847
|
"Input contract: files[]. Scope is explicit caller-selected files resolved from the current working directory.",
|
|
1833
|
-
"Output contract:
|
|
2848
|
+
"Output contract: monolithic text in content[0].text; details.execution preserves only exit code and residual diagnostics.",
|
|
1834
2849
|
"Configuration contract: checker selection is derived from the cwd-resolved static-check configuration and file extensions only.",
|
|
1835
|
-
"Failure contract: execution
|
|
2850
|
+
"Failure contract: execution diagnostics preserve failing checker output and skipped-input warnings.",
|
|
1836
2851
|
],
|
|
2852
|
+
renderResult: buildStructuredToolRenderResult("files-static-check"),
|
|
1837
2853
|
parameters: multiFileSchema,
|
|
1838
2854
|
async execute(_toolCallId, params) {
|
|
1839
|
-
const
|
|
1840
|
-
const
|
|
1841
|
-
const
|
|
1842
|
-
|
|
1843
|
-
const payload = buildStaticCheckToolPayload(
|
|
1844
|
-
"files-static-check",
|
|
1845
|
-
"explicit-files",
|
|
1846
|
-
projectBase,
|
|
1847
|
-
params.files,
|
|
1848
|
-
[],
|
|
1849
|
-
[],
|
|
1850
|
-
staticCheckConfig,
|
|
1851
|
-
buildToolExecutionSection(result),
|
|
1852
|
-
);
|
|
1853
|
-
return buildStructuredToolExecuteResult(payload);
|
|
2855
|
+
const contextPath = getRuntimeContextPath(process.cwd());
|
|
2856
|
+
const projectBase = getProjectBase(contextPath);
|
|
2857
|
+
const config = loadProjectConfig(projectBase);
|
|
2858
|
+
return executeMonolithicTool(() => runFilesStaticCheck(params.files, projectBase, config));
|
|
1854
2859
|
},
|
|
1855
2860
|
});
|
|
1856
2861
|
|
|
1857
2862
|
const staticCheckSchema = Type.Object(
|
|
1858
2863
|
{},
|
|
1859
2864
|
{
|
|
1860
|
-
description: "Input contract: no params. Scope is the configured src-dir plus tests-dir selection after fixture exclusion. Output contract:
|
|
2865
|
+
description: "Input contract: no params. Scope is the configured src-dir plus tests-dir selection after fixture exclusion. Output contract: monolithic text in content[0].text plus details.execution diagnostics.",
|
|
1861
2866
|
},
|
|
1862
2867
|
);
|
|
1863
2868
|
pi.registerTool({
|
|
1864
2869
|
name: "static-check",
|
|
1865
2870
|
label: "static-check",
|
|
1866
|
-
description: "Scope: configured source and test directories. Return
|
|
1867
|
-
promptSnippet: "Return the
|
|
2871
|
+
description: "Scope: configured source and test directories. Return the monolithic static-check report in content[0].text and keep only execution metadata in details.execution.",
|
|
2872
|
+
promptSnippet: "Return the monolithic project static-check report for the current configuration.",
|
|
1868
2873
|
promptGuidelines: [
|
|
1869
2874
|
"Input contract: no params. Scope is src-dir plus tests-dir from the cwd-derived project configuration.",
|
|
1870
|
-
"Output contract:
|
|
2875
|
+
"Output contract: monolithic text in content[0].text; details.execution preserves only exit code and residual diagnostics.",
|
|
1871
2876
|
"Selection contract: tests/fixtures and <tests-dir>/fixtures are excluded before checker dispatch.",
|
|
1872
|
-
"Failure contract: execution
|
|
2877
|
+
"Failure contract: execution diagnostics preserve failing checker output and skipped-selection warnings.",
|
|
1873
2878
|
],
|
|
2879
|
+
renderResult: buildStructuredToolRenderResult("static-check"),
|
|
1874
2880
|
parameters: staticCheckSchema,
|
|
1875
2881
|
async execute() {
|
|
1876
|
-
const
|
|
1877
|
-
const
|
|
1878
|
-
const
|
|
1879
|
-
|
|
1880
|
-
const testsDirRel = makeRelativeIfContainsProject(config["tests-dir"], projectBase)
|
|
1881
|
-
.split(path.sep)
|
|
1882
|
-
.join("/")
|
|
1883
|
-
.replace(/^\.?\/?/, "")
|
|
1884
|
-
.replace(/\/+$/, "");
|
|
1885
|
-
const excludedDirectoryPaths = [...new Set([
|
|
1886
|
-
"tests/fixtures",
|
|
1887
|
-
testsDirRel ? `${testsDirRel}/fixtures` : "fixtures",
|
|
1888
|
-
])];
|
|
1889
|
-
let selectedPaths: string[] = [];
|
|
1890
|
-
let execution;
|
|
1891
|
-
try {
|
|
1892
|
-
selectedPaths = collectProjectStaticCheckSelection(projectBase, config).selectedPaths;
|
|
1893
|
-
execution = buildToolExecutionSection(runProjectStaticCheck(projectBase, config));
|
|
1894
|
-
} catch (error) {
|
|
1895
|
-
execution = buildToolExecutionSection(normalizeToolFailure(error));
|
|
1896
|
-
}
|
|
1897
|
-
const payload = buildStaticCheckToolPayload(
|
|
1898
|
-
"static-check",
|
|
1899
|
-
"configured-source-and-test-directories",
|
|
1900
|
-
projectBase,
|
|
1901
|
-
selectedPaths,
|
|
1902
|
-
selectionDirectoryPaths,
|
|
1903
|
-
excludedDirectoryPaths,
|
|
1904
|
-
staticCheckConfig,
|
|
1905
|
-
execution,
|
|
1906
|
-
);
|
|
1907
|
-
return buildStructuredToolExecuteResult(payload);
|
|
1908
|
-
},
|
|
1909
|
-
});
|
|
1910
|
-
|
|
1911
|
-
const gitCheckSchema = Type.Object(
|
|
1912
|
-
{},
|
|
1913
|
-
{
|
|
1914
|
-
description: "Input contract: no params. Output contract: JSON object with result and execution. Result exposes git-path presence plus aggregate repository status fields.",
|
|
1915
|
-
},
|
|
1916
|
-
);
|
|
1917
|
-
pi.registerTool({
|
|
1918
|
-
name: "git-check",
|
|
1919
|
-
label: "git-check",
|
|
1920
|
-
description: "Scope: current runtime path. Return a token-optimized JSON payload with result and execution sections. Result exposes repository validation status through direct fields without request echoes.",
|
|
1921
|
-
promptSnippet: "Return the structured git-validation payload for the runtime repository.",
|
|
1922
|
-
promptGuidelines: [
|
|
1923
|
-
"Input contract: no params. Scope is the cwd-derived runtime path context.",
|
|
1924
|
-
"Output contract: result + execution. Result exposes git_path_present and aggregate status.",
|
|
1925
|
-
"Behavior contract: the tool checks work-tree membership, porcelain cleanliness, and symbolic-or-detached HEAD validity.",
|
|
1926
|
-
"Failure contract: execution.code and execution.stderr_lines surface git-path or repository-state errors.",
|
|
1927
|
-
],
|
|
1928
|
-
parameters: gitCheckSchema,
|
|
1929
|
-
async execute() {
|
|
1930
|
-
const projectBase = getProjectBase(process.cwd());
|
|
1931
|
-
const config = loadProjectConfig(process.cwd());
|
|
1932
|
-
let execution;
|
|
1933
|
-
try {
|
|
1934
|
-
execution = buildToolExecutionSection(runGitCheck(projectBase, config));
|
|
1935
|
-
} catch (error) {
|
|
1936
|
-
execution = buildToolExecutionSection(normalizeToolFailure(error));
|
|
1937
|
-
}
|
|
1938
|
-
const payload = buildGitCheckToolPayload(projectBase, resolveRuntimeGitPath(projectBase), execution);
|
|
1939
|
-
return buildStructuredToolExecuteResult(payload);
|
|
1940
|
-
},
|
|
1941
|
-
});
|
|
1942
|
-
|
|
1943
|
-
const docsCheckSchema = Type.Object(
|
|
1944
|
-
{},
|
|
1945
|
-
{
|
|
1946
|
-
description: "Input contract: no params. Output contract: JSON object with summary, files, and execution. File entries expose canonical paths, prompt_command remediation, and presence status for canonical docs.",
|
|
1947
|
-
},
|
|
1948
|
-
);
|
|
1949
|
-
pi.registerTool({
|
|
1950
|
-
name: "docs-check",
|
|
1951
|
-
label: "docs-check",
|
|
1952
|
-
description: "Scope: canonical docs. Return a token-optimized JSON payload with summary, files, and execution sections. File entries expose remediation prompt commands and direct presence facts for REQUIREMENTS.md, WORKFLOW.md, and REFERENCES.md.",
|
|
1953
|
-
promptSnippet: "Return the structured canonical-document validation payload.",
|
|
1954
|
-
promptGuidelines: [
|
|
1955
|
-
"Input contract: no params. Scope is docs-dir from the cwd-derived project configuration.",
|
|
1956
|
-
"Output contract: summary + files + execution. File entries expose file_name, canonical_path, prompt_command, and status.",
|
|
1957
|
-
"Specialization trigger: remediation differs per missing canonical file through prompt_command.",
|
|
1958
|
-
"Failure contract: execution.code is non-zero when any canonical document is missing; execution.stderr_lines enumerate missing files.",
|
|
1959
|
-
],
|
|
1960
|
-
parameters: docsCheckSchema,
|
|
1961
|
-
async execute() {
|
|
1962
|
-
const projectBase = getProjectBase(process.cwd());
|
|
1963
|
-
const config = loadProjectConfig(process.cwd());
|
|
1964
|
-
const payload = buildDocsCheckToolPayload(projectBase, config["docs-dir"]);
|
|
1965
|
-
return buildStructuredToolExecuteResult(payload);
|
|
1966
|
-
},
|
|
1967
|
-
});
|
|
1968
|
-
|
|
1969
|
-
const gitWtNameSchema = Type.Object(
|
|
1970
|
-
{},
|
|
1971
|
-
{
|
|
1972
|
-
description: "Input contract: no params. Output contract: JSON object with result and execution. Result exposes worktree_name when generation succeeds.",
|
|
1973
|
-
},
|
|
1974
|
-
);
|
|
1975
|
-
pi.registerTool({
|
|
1976
|
-
name: "git-wt-name",
|
|
1977
|
-
label: "git-wt-name",
|
|
1978
|
-
description: "Scope: current runtime path. Return a token-optimized JSON payload with result and execution sections. Result exposes the generated worktree name while static naming rules remain in registration metadata.",
|
|
1979
|
-
promptSnippet: "Return the structured worktree-name generation payload.",
|
|
1980
|
-
promptGuidelines: [
|
|
1981
|
-
"Input contract: no params. Scope is the cwd-derived runtime path context.",
|
|
1982
|
-
"Output contract: result + execution. Result exposes worktree_name.",
|
|
1983
|
-
"Behavior contract: generation follows useReq-<project>-<sanitized-branch>-<YYYYMMDDHHMMSS>.",
|
|
1984
|
-
"Failure contract: execution.code and execution.stderr_lines surface git-path or branch-resolution errors.",
|
|
1985
|
-
],
|
|
1986
|
-
parameters: gitWtNameSchema,
|
|
1987
|
-
async execute() {
|
|
1988
|
-
const projectBase = getProjectBase(process.cwd());
|
|
1989
|
-
const config = loadProjectConfig(process.cwd());
|
|
1990
|
-
let execution;
|
|
1991
|
-
try {
|
|
1992
|
-
execution = buildToolExecutionSection(runGitWtName(projectBase, config));
|
|
1993
|
-
} catch (error) {
|
|
1994
|
-
execution = buildToolExecutionSection(normalizeToolFailure(error));
|
|
1995
|
-
}
|
|
1996
|
-
const payload = buildWorktreeNameToolPayload(projectBase, resolveRuntimeGitPath(projectBase), execution);
|
|
1997
|
-
return buildStructuredToolExecuteResult(payload);
|
|
1998
|
-
},
|
|
1999
|
-
});
|
|
2000
|
-
|
|
2001
|
-
const gitWtCreateSchema = Type.Object(
|
|
2002
|
-
{
|
|
2003
|
-
wtName: Type.String({ description: "Exact target worktree name and branch name" }),
|
|
2004
|
-
},
|
|
2005
|
-
{
|
|
2006
|
-
description: "Input contract: wtName. Output contract: JSON object with result and execution. Result exposes worktree_name and derived worktree_path.",
|
|
2007
|
-
},
|
|
2008
|
-
);
|
|
2009
|
-
pi.registerTool({
|
|
2010
|
-
name: "git-wt-create",
|
|
2011
|
-
label: "git-wt-create",
|
|
2012
|
-
description: "Scope: current runtime path. Return a token-optimized JSON payload with result and execution sections. Result exposes the exact worktree name and derived path without static operation echoes.",
|
|
2013
|
-
promptSnippet: "Return the structured worktree-creation payload for the requested name.",
|
|
2014
|
-
promptGuidelines: [
|
|
2015
|
-
"Input contract: wtName is required and must match the exact worktree/branch name to create.",
|
|
2016
|
-
"Output contract: result + execution. Result exposes worktree_name and worktree_path.",
|
|
2017
|
-
"Specialization trigger: worktree_path depends on the runtime git root parent directory.",
|
|
2018
|
-
"Failure contract: execution.code and execution.stderr_lines surface invalid-name, git, or finalization errors.",
|
|
2019
|
-
],
|
|
2020
|
-
parameters: gitWtCreateSchema,
|
|
2021
|
-
async execute(_toolCallId, params) {
|
|
2022
|
-
const projectBase = getProjectBase(process.cwd());
|
|
2023
|
-
const config = loadProjectConfig(process.cwd());
|
|
2024
|
-
let execution;
|
|
2025
|
-
try {
|
|
2026
|
-
execution = buildToolExecutionSection(runGitWtCreate(projectBase, params.wtName, config));
|
|
2027
|
-
} catch (error) {
|
|
2028
|
-
execution = buildToolExecutionSection(normalizeToolFailure(error));
|
|
2029
|
-
}
|
|
2030
|
-
const payload = buildWorktreeMutationToolPayload(
|
|
2031
|
-
"git-wt-create",
|
|
2032
|
-
projectBase,
|
|
2033
|
-
resolveRuntimeGitPath(projectBase),
|
|
2034
|
-
params.wtName,
|
|
2035
|
-
execution,
|
|
2036
|
-
);
|
|
2037
|
-
return buildStructuredToolExecuteResult(payload);
|
|
2882
|
+
const contextPath = getRuntimeContextPath(process.cwd());
|
|
2883
|
+
const projectBase = getProjectBase(contextPath);
|
|
2884
|
+
const config = loadProjectConfig(projectBase);
|
|
2885
|
+
return executeMonolithicTool(() => runProjectStaticCheck(contextPath, config));
|
|
2038
2886
|
},
|
|
2039
2887
|
});
|
|
2040
2888
|
|
|
2041
|
-
const gitWtDeleteSchema = Type.Object(
|
|
2042
|
-
{
|
|
2043
|
-
wtName: Type.String({ description: "Exact target worktree name and branch name" }),
|
|
2044
|
-
},
|
|
2045
|
-
{
|
|
2046
|
-
description: "Input contract: wtName. Output contract: JSON object with result and execution. Result exposes worktree_name and derived worktree_path.",
|
|
2047
|
-
},
|
|
2048
|
-
);
|
|
2049
|
-
pi.registerTool({
|
|
2050
|
-
name: "git-wt-delete",
|
|
2051
|
-
label: "git-wt-delete",
|
|
2052
|
-
description: "Scope: current runtime path. Return a token-optimized JSON payload with result and execution sections. Result exposes the exact worktree name and derived path without static operation echoes.",
|
|
2053
|
-
promptSnippet: "Return the structured worktree-deletion payload for the requested name.",
|
|
2054
|
-
promptGuidelines: [
|
|
2055
|
-
"Input contract: wtName is required and must match the exact worktree/branch name to delete.",
|
|
2056
|
-
"Output contract: result + execution. Result exposes worktree_name and worktree_path.",
|
|
2057
|
-
"Specialization trigger: worktree_path depends on the runtime git root parent directory.",
|
|
2058
|
-
"Failure contract: execution.code and execution.stderr_lines surface missing-target or deletion errors.",
|
|
2059
|
-
],
|
|
2060
|
-
parameters: gitWtDeleteSchema,
|
|
2061
|
-
async execute(_toolCallId, params) {
|
|
2062
|
-
const projectBase = getProjectBase(process.cwd());
|
|
2063
|
-
const config = loadProjectConfig(process.cwd());
|
|
2064
|
-
let execution;
|
|
2065
|
-
try {
|
|
2066
|
-
execution = buildToolExecutionSection(runGitWtDelete(projectBase, params.wtName, config));
|
|
2067
|
-
} catch (error) {
|
|
2068
|
-
execution = buildToolExecutionSection(normalizeToolFailure(error));
|
|
2069
|
-
}
|
|
2070
|
-
const payload = buildWorktreeMutationToolPayload(
|
|
2071
|
-
"git-wt-delete",
|
|
2072
|
-
projectBase,
|
|
2073
|
-
resolveRuntimeGitPath(projectBase),
|
|
2074
|
-
params.wtName,
|
|
2075
|
-
execution,
|
|
2076
|
-
);
|
|
2077
|
-
return buildStructuredToolExecuteResult(payload);
|
|
2078
|
-
},
|
|
2079
|
-
});
|
|
2080
2889
|
}
|
|
2081
2890
|
|
|
2082
2891
|
/**
|
|
@@ -2108,28 +2917,19 @@ function buildPiUsereqToolsMenuChoices(pi: ExtensionAPI, config: UseReqConfig):
|
|
|
2108
2917
|
value: `${tools.length} targets`,
|
|
2109
2918
|
description: "Disable every configurable startup tool for future session starts.",
|
|
2110
2919
|
},
|
|
2111
|
-
{
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
value: `${normalizeEnabledPiUsereqTools(undefined).length} defaults`,
|
|
2115
|
-
description: "Restore the documented default startup-tool selection.",
|
|
2116
|
-
},
|
|
2117
|
-
{
|
|
2118
|
-
id: "save-and-close",
|
|
2119
|
-
label: "Save and close",
|
|
2120
|
-
value: "",
|
|
2121
|
-
description: "Return to the parent configuration menu.",
|
|
2122
|
-
},
|
|
2920
|
+
...buildTerminalSettingsMenuChoices({
|
|
2921
|
+
resetDefaultsDescription: "Restore the documented default startup-tool selection.",
|
|
2922
|
+
}),
|
|
2123
2923
|
];
|
|
2124
2924
|
}
|
|
2125
2925
|
|
|
2126
2926
|
/**
|
|
2127
2927
|
* @brief Builds the shared settings-menu choices for per-tool startup toggles.
|
|
2128
|
-
* @details Exposes every configurable startup tool as one row whose right-side value reports the current enabled state. Runtime is O(t) in configurable-tool count. No external state is mutated.
|
|
2928
|
+
* @details Exposes every configurable startup tool as one row whose right-side value reports the current enabled state, preserves the documented custom/files/embedded/default-disabled ordering, and appends a value-less subtree-local `Reset defaults` row. Runtime is O(t) in configurable-tool count. No external state is mutated.
|
|
2129
2929
|
* @param[in] pi {ExtensionAPI} Active extension API instance.
|
|
2130
2930
|
* @param[in] config {UseReqConfig} Effective project configuration.
|
|
2131
2931
|
* @return {PiUsereqSettingsMenuChoice[]} Ordered per-tool toggle choices.
|
|
2132
|
-
* @satisfies REQ-007, REQ-151, REQ-152, REQ-153, REQ-154
|
|
2932
|
+
* @satisfies REQ-007, REQ-151, REQ-152, REQ-153, REQ-154, REQ-231, REQ-232
|
|
2133
2933
|
*/
|
|
2134
2934
|
function buildPiUsereqToolToggleChoices(pi: ExtensionAPI, config: UseReqConfig): PiUsereqSettingsMenuChoice[] {
|
|
2135
2935
|
const enabledTools = new Set(getConfiguredEnabledPiUsereqTools(config));
|
|
@@ -2140,25 +2940,27 @@ function buildPiUsereqToolToggleChoices(pi: ExtensionAPI, config: UseReqConfig):
|
|
|
2140
2940
|
value: enabledTools.has(tool.name) ? "on" : "off",
|
|
2141
2941
|
description: tool.description ?? `Toggle startup activation for ${tool.name}.`,
|
|
2142
2942
|
})),
|
|
2143
|
-
{
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
value: "",
|
|
2147
|
-
description: "Return to the Enable tools menu.",
|
|
2148
|
-
},
|
|
2943
|
+
...buildTerminalSettingsMenuChoices({
|
|
2944
|
+
resetDefaultsDescription: "Restore the documented default startup-tool selection.",
|
|
2945
|
+
}),
|
|
2149
2946
|
];
|
|
2150
2947
|
}
|
|
2151
2948
|
|
|
2152
2949
|
/**
|
|
2153
2950
|
* @brief Runs the interactive active-tool configuration menu.
|
|
2154
|
-
* @details Synchronizes runtime active tools with persisted config, renders startup-tool actions through the shared settings-menu UI, and updates configuration state in response to selections until the user exits. Runtime depends on user interaction count. Side effects include UI updates, active-tool changes, and config mutation.
|
|
2951
|
+
* @details Synchronizes runtime active tools with persisted config, renders startup-tool actions through the shared settings-menu UI, preserves the documented per-tool ordering, and updates configuration state in response to selections until the user exits. Runtime depends on user interaction count. Side effects include UI updates, active-tool changes, and config mutation.
|
|
2155
2952
|
* @param[in] pi {ExtensionAPI} Active extension API instance.
|
|
2156
2953
|
* @param[in] ctx {ExtensionCommandContext} Active command context.
|
|
2157
2954
|
* @param[in,out] config {UseReqConfig} Mutable configuration object.
|
|
2158
2955
|
* @return {Promise<void>} Promise resolved when the menu closes.
|
|
2159
|
-
* @satisfies REQ-007, REQ-063, REQ-064, REQ-150, REQ-151, REQ-152, REQ-153, REQ-154, REQ-193
|
|
2956
|
+
* @satisfies REQ-007, REQ-063, REQ-064, REQ-150, REQ-151, REQ-152, REQ-153, REQ-154, REQ-193, REQ-231, REQ-232
|
|
2160
2957
|
*/
|
|
2161
|
-
async function configurePiUsereqToolsMenu(
|
|
2958
|
+
async function configurePiUsereqToolsMenu(
|
|
2959
|
+
pi: ExtensionAPI,
|
|
2960
|
+
ctx: ExtensionCommandContext,
|
|
2961
|
+
config: UseReqConfig,
|
|
2962
|
+
onConfigChange: () => void,
|
|
2963
|
+
): Promise<void> {
|
|
2162
2964
|
applyConfiguredPiUsereqTools(pi, config);
|
|
2163
2965
|
let focusedChoiceId: string | undefined;
|
|
2164
2966
|
while (true) {
|
|
@@ -2168,32 +2970,71 @@ async function configurePiUsereqToolsMenu(pi: ExtensionAPI, ctx: ExtensionComman
|
|
|
2168
2970
|
initialSelectedId: focusedChoiceId,
|
|
2169
2971
|
});
|
|
2170
2972
|
|
|
2171
|
-
if (!choice
|
|
2973
|
+
if (!choice) {
|
|
2172
2974
|
return;
|
|
2173
2975
|
}
|
|
2174
2976
|
focusedChoiceId = choice;
|
|
2175
2977
|
|
|
2176
2978
|
if (choice === "enable-all-tools") {
|
|
2177
2979
|
setConfiguredPiUsereqTools(pi, config, tools.map((tool) => tool.name));
|
|
2980
|
+
onConfigChange();
|
|
2178
2981
|
ctx.ui.notify("Enabled all configurable active tools", "info");
|
|
2179
2982
|
continue;
|
|
2180
2983
|
}
|
|
2181
2984
|
|
|
2182
2985
|
if (choice === "disable-all-tools") {
|
|
2183
2986
|
setConfiguredPiUsereqTools(pi, config, []);
|
|
2987
|
+
onConfigChange();
|
|
2184
2988
|
ctx.ui.notify("Disabled all configurable active tools", "info");
|
|
2185
2989
|
continue;
|
|
2186
2990
|
}
|
|
2187
2991
|
|
|
2188
2992
|
if (choice === "reset-defaults") {
|
|
2993
|
+
const approved = await confirmResetChanges(
|
|
2994
|
+
ctx,
|
|
2995
|
+
"Confirm Enable tools reset",
|
|
2996
|
+
[{
|
|
2997
|
+
label: "Enable tools",
|
|
2998
|
+
previousValue: String(getConfiguredEnabledPiUsereqTools(config).length),
|
|
2999
|
+
nextValue: String(normalizeEnabledPiUsereqTools(undefined).length),
|
|
3000
|
+
}].filter((change) => change.previousValue !== change.nextValue),
|
|
3001
|
+
"Approve restoring the documented default startup-tool selection.",
|
|
3002
|
+
"Abort the startup-tool reset and keep the current values.",
|
|
3003
|
+
);
|
|
3004
|
+
if (!approved) {
|
|
3005
|
+
ctx.ui.notify("Aborted startup-tool reset", "info");
|
|
3006
|
+
continue;
|
|
3007
|
+
}
|
|
2189
3008
|
setConfiguredPiUsereqTools(pi, config, normalizeEnabledPiUsereqTools(undefined));
|
|
3009
|
+
onConfigChange();
|
|
2190
3010
|
ctx.ui.notify("Restored default configurable active tools", "info");
|
|
2191
3011
|
continue;
|
|
2192
3012
|
}
|
|
2193
3013
|
|
|
2194
3014
|
if (choice === "enable-tools") {
|
|
2195
3015
|
const selectedToolName = await showPiUsereqSettingsMenu(ctx, "Enable tools", buildPiUsereqToolToggleChoices(pi, config));
|
|
2196
|
-
if (!selectedToolName
|
|
3016
|
+
if (!selectedToolName) {
|
|
3017
|
+
continue;
|
|
3018
|
+
}
|
|
3019
|
+
if (selectedToolName === "reset-defaults") {
|
|
3020
|
+
const approved = await confirmResetChanges(
|
|
3021
|
+
ctx,
|
|
3022
|
+
"Confirm Enable tools reset",
|
|
3023
|
+
[{
|
|
3024
|
+
label: "Enable tools",
|
|
3025
|
+
previousValue: String(getConfiguredEnabledPiUsereqTools(config).length),
|
|
3026
|
+
nextValue: String(normalizeEnabledPiUsereqTools(undefined).length),
|
|
3027
|
+
}].filter((change) => change.previousValue !== change.nextValue),
|
|
3028
|
+
"Approve restoring the documented default startup-tool selection.",
|
|
3029
|
+
"Abort the startup-tool reset and keep the current values.",
|
|
3030
|
+
);
|
|
3031
|
+
if (!approved) {
|
|
3032
|
+
ctx.ui.notify("Aborted startup-tool reset", "info");
|
|
3033
|
+
continue;
|
|
3034
|
+
}
|
|
3035
|
+
setConfiguredPiUsereqTools(pi, config, normalizeEnabledPiUsereqTools(undefined));
|
|
3036
|
+
onConfigChange();
|
|
3037
|
+
ctx.ui.notify("Restored default configurable active tools", "info");
|
|
2197
3038
|
continue;
|
|
2198
3039
|
}
|
|
2199
3040
|
if (enabledTools.has(selectedToolName)) {
|
|
@@ -2202,6 +3043,7 @@ async function configurePiUsereqToolsMenu(pi: ExtensionAPI, ctx: ExtensionComman
|
|
|
2202
3043
|
enabledTools.add(selectedToolName);
|
|
2203
3044
|
}
|
|
2204
3045
|
setConfiguredPiUsereqTools(pi, config, tools.map((tool) => tool.name).filter((toolName) => enabledTools.has(toolName)));
|
|
3046
|
+
onConfigChange();
|
|
2205
3047
|
ctx.ui.notify(
|
|
2206
3048
|
`${enabledTools.has(selectedToolName) ? "Enabled" : "Disabled"} ${selectedToolName}`,
|
|
2207
3049
|
"info",
|
|
@@ -2211,34 +3053,75 @@ async function configurePiUsereqToolsMenu(pi: ExtensionAPI, ctx: ExtensionComman
|
|
|
2211
3053
|
}
|
|
2212
3054
|
|
|
2213
3055
|
/**
|
|
2214
|
-
* @brief
|
|
2215
|
-
* @details
|
|
3056
|
+
* @brief Resolves one static-check language config for menu rendering.
|
|
3057
|
+
* @details Returns the configured per-language static-check object when present and otherwise synthesizes a disabled empty-language object so menu code can render all supported languages deterministically. Runtime is O(1). No external state is mutated.
|
|
3058
|
+
* @param[in] config {UseReqConfig} Effective project configuration.
|
|
3059
|
+
* @param[in] language {string} Canonical language name.
|
|
3060
|
+
* @return {StaticCheckLanguageConfig} Resolved per-language config object.
|
|
3061
|
+
*/
|
|
3062
|
+
function getStaticCheckLanguageConfigForMenu(
|
|
3063
|
+
config: UseReqConfig,
|
|
3064
|
+
language: string,
|
|
3065
|
+
): StaticCheckLanguageConfig {
|
|
3066
|
+
return config["static-check"][language] ?? createStaticCheckLanguageConfig([]);
|
|
3067
|
+
}
|
|
3068
|
+
|
|
3069
|
+
/**
|
|
3070
|
+
* @brief Counts languages that currently expose at least one configured checker.
|
|
3071
|
+
* @details Treats configured-but-disabled languages as configured when their checker list is non-empty so removal actions remain deterministic. Runtime is O(l). No external state is mutated.
|
|
3072
|
+
* @param[in] config {UseReqConfig} Effective project configuration.
|
|
3073
|
+
* @return {number} Number of languages with at least one configured checker.
|
|
3074
|
+
*/
|
|
3075
|
+
function countConfiguredStaticCheckLanguages(config: UseReqConfig): number {
|
|
3076
|
+
return Object.values(config["static-check"]).filter((languageConfig) => languageConfig.checkers.length > 0).length;
|
|
3077
|
+
}
|
|
3078
|
+
|
|
3079
|
+
/**
|
|
3080
|
+
* @brief Counts languages whose static-check enable flag is on.
|
|
3081
|
+
* @details Counts only languages whose persisted per-language config explicitly sets `enabled=enable`, regardless of checker count. Runtime is O(l). No external state is mutated.
|
|
3082
|
+
* @param[in] config {UseReqConfig} Effective project configuration.
|
|
3083
|
+
* @return {number} Number of enabled languages.
|
|
3084
|
+
*/
|
|
3085
|
+
function countEnabledStaticCheckLanguages(config: UseReqConfig): number {
|
|
3086
|
+
return Object.values(config["static-check"]).filter((languageConfig) => languageConfig.enabled === "enable").length;
|
|
3087
|
+
}
|
|
3088
|
+
|
|
3089
|
+
/**
|
|
3090
|
+
* @brief Restores the documented static-check default configuration.
|
|
3091
|
+
* @details Replaces the mutable config subtree with a fresh clone of the documented per-language defaults so menu reset actions restore both enable flags and checker lists in one step. Runtime is O(l + c). Side effect: mutates `config`.
|
|
3092
|
+
* @param[in,out] config {UseReqConfig} Mutable configuration object.
|
|
3093
|
+
* @return {void} No return value.
|
|
3094
|
+
* @satisfies REQ-250, REQ-251, REQ-252
|
|
3095
|
+
*/
|
|
3096
|
+
function resetStaticCheckConfig(config: UseReqConfig): void {
|
|
3097
|
+
config["static-check"] = getDefaultStaticCheckConfig();
|
|
3098
|
+
}
|
|
3099
|
+
|
|
3100
|
+
/**
|
|
3101
|
+
* @brief Summarizes enabled and configured static-check languages.
|
|
3102
|
+
* @details Counts enabled languages and languages with at least one checker, then emits one compact summary string suitable for the top-level configuration menu. Runtime is O(l). No external state is mutated.
|
|
2216
3103
|
* @param[in] config {UseReqConfig} Effective project configuration.
|
|
2217
|
-
* @return {string} Compact summary string
|
|
3104
|
+
* @return {string} Compact summary string.
|
|
2218
3105
|
*/
|
|
2219
3106
|
function formatStaticCheckLanguagesSummary(config: UseReqConfig): string {
|
|
2220
|
-
|
|
2221
|
-
.filter(([, entries]) => Array.isArray(entries) && entries.length > 0)
|
|
2222
|
-
.sort(([left], [right]) => left.localeCompare(right))
|
|
2223
|
-
.map(([language, entries]) => `${language} (${entries.length})`);
|
|
2224
|
-
return languages.join(", ") || "(none)";
|
|
3107
|
+
return `${countEnabledStaticCheckLanguages(config)} enabled • ${countConfiguredStaticCheckLanguages(config)} configured`;
|
|
2225
3108
|
}
|
|
2226
3109
|
|
|
2227
3110
|
/**
|
|
2228
3111
|
* @brief Builds the shared settings-menu choices for static-check management.
|
|
2229
|
-
* @details Serializes guided Command-oriented
|
|
3112
|
+
* @details Serializes guided Command-oriented add and remove actions, renders one direct on/off toggle row for every supported language, and appends canonical terminal rows while omitting raw-spec and reference-only actions. Runtime is O(l). No external state is mutated.
|
|
2230
3113
|
* @param[in] config {UseReqConfig} Effective project configuration.
|
|
2231
3114
|
* @return {PiUsereqSettingsMenuChoice[]} Ordered static-check menu choices.
|
|
2232
|
-
* @satisfies REQ-008, REQ-150, REQ-
|
|
3115
|
+
* @satisfies REQ-008, REQ-150, REQ-151, REQ-152, REQ-153, REQ-154, REQ-160, REQ-161, REQ-193, REQ-248
|
|
2233
3116
|
*/
|
|
2234
3117
|
function buildStaticCheckMenuChoices(config: UseReqConfig): PiUsereqSettingsMenuChoice[] {
|
|
2235
|
-
const
|
|
2236
|
-
const configuredLanguageCount =
|
|
3118
|
+
const supportedLanguages = getSupportedStaticCheckLanguageSupport();
|
|
3119
|
+
const configuredLanguageCount = countConfiguredStaticCheckLanguages(config);
|
|
2237
3120
|
return [
|
|
2238
3121
|
{
|
|
2239
3122
|
id: "add-static-check-entry",
|
|
2240
3123
|
label: "Add static code checker",
|
|
2241
|
-
value: `${
|
|
3124
|
+
value: `${supportedLanguages.length} languages`,
|
|
2242
3125
|
description: "Select a supported language, then configure one Command static-check executable.",
|
|
2243
3126
|
},
|
|
2244
3127
|
{
|
|
@@ -2247,96 +3130,134 @@ function buildStaticCheckMenuChoices(config: UseReqConfig): PiUsereqSettingsMenu
|
|
|
2247
3130
|
value: configuredLanguageCount > 0 ? `${configuredLanguageCount} configured` : "(none)",
|
|
2248
3131
|
description: "Remove every configured static-check entry for one language.",
|
|
2249
3132
|
},
|
|
2250
|
-
{
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
3133
|
+
...supportedLanguages.map(({ language, extensions }) => {
|
|
3134
|
+
const languageConfig = getStaticCheckLanguageConfigForMenu(config, language);
|
|
3135
|
+
const configuredCount = languageConfig.checkers.length;
|
|
3136
|
+
const suffix = configuredCount === 1 ? "checker" : "checkers";
|
|
3137
|
+
return {
|
|
3138
|
+
id: `toggle-static-check-language:${language}`,
|
|
3139
|
+
label: language,
|
|
3140
|
+
value: languageConfig.enabled === "enable" ? "on" : "off",
|
|
3141
|
+
description: `Toggle static-check execution for ${language}. Configured ${configuredCount} ${suffix}. Supported extensions: ${extensions.join(", ")}.`,
|
|
3142
|
+
};
|
|
3143
|
+
}),
|
|
3144
|
+
...buildTerminalSettingsMenuChoices({
|
|
3145
|
+
resetDefaultsDescription: "Restore the documented per-language static-check defaults.",
|
|
3146
|
+
}),
|
|
2262
3147
|
];
|
|
2263
3148
|
}
|
|
2264
3149
|
|
|
2265
3150
|
/**
|
|
2266
3151
|
* @brief Builds the shared settings-menu choices for supported static-check languages.
|
|
2267
|
-
* @details Exposes every supported language as one row whose right-side value reports extensions
|
|
3152
|
+
* @details Exposes every supported language as one row whose right-side value reports extensions, enablement, and configured checker count for guided Command configuration flows, then appends subtree-local terminal rows. Runtime is O(l). No external state is mutated.
|
|
2268
3153
|
* @param[in] config {UseReqConfig} Effective project configuration.
|
|
2269
3154
|
* @return {PiUsereqSettingsMenuChoice[]} Ordered language-choice vector.
|
|
2270
3155
|
*/
|
|
2271
3156
|
function buildSupportedStaticCheckLanguageChoices(config: UseReqConfig): PiUsereqSettingsMenuChoice[] {
|
|
2272
3157
|
return [
|
|
2273
3158
|
...getSupportedStaticCheckLanguageSupport().map(({ language, extensions }) => {
|
|
2274
|
-
const
|
|
3159
|
+
const languageConfig = getStaticCheckLanguageConfigForMenu(config, language);
|
|
3160
|
+
const configuredCount = languageConfig.checkers.length;
|
|
2275
3161
|
const suffix = configuredCount === 1 ? "checker" : "checkers";
|
|
2276
3162
|
return {
|
|
2277
3163
|
id: language,
|
|
2278
3164
|
label: language,
|
|
2279
|
-
value: `${extensions.join(", ")} • ${configuredCount} ${suffix}`,
|
|
2280
|
-
description: `Configure the Command static-check
|
|
3165
|
+
value: `${extensions.join(", ")} • ${languageConfig.enabled === "enable" ? "on" : "off"} • ${configuredCount} ${suffix}`,
|
|
3166
|
+
description: `Configure the Command static-check entries for ${language}. Supported extensions: ${extensions.join(", ")}.`,
|
|
2281
3167
|
};
|
|
2282
3168
|
}),
|
|
2283
|
-
{
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
value: "",
|
|
2287
|
-
description: "Return to the Language static code checkers menu.",
|
|
2288
|
-
},
|
|
3169
|
+
...buildTerminalSettingsMenuChoices({
|
|
3170
|
+
resetDefaultsDescription: "Restore the documented per-language static-check defaults.",
|
|
3171
|
+
}),
|
|
2289
3172
|
];
|
|
2290
3173
|
}
|
|
2291
3174
|
|
|
2292
3175
|
/**
|
|
2293
3176
|
* @brief Builds the shared settings-menu choices for configured static-check languages.
|
|
2294
|
-
* @details Exposes only languages
|
|
3177
|
+
* @details Exposes only languages whose checker lists are non-empty so removal remains deterministic, then appends subtree-local terminal rows. Runtime is O(l). No external state is mutated.
|
|
2295
3178
|
* @param[in] config {UseReqConfig} Effective project configuration.
|
|
2296
3179
|
* @return {PiUsereqSettingsMenuChoice[]} Ordered configured-language vector.
|
|
2297
3180
|
*/
|
|
2298
3181
|
function buildConfiguredStaticCheckLanguageChoices(config: UseReqConfig): PiUsereqSettingsMenuChoice[] {
|
|
2299
3182
|
return [
|
|
2300
3183
|
...getSupportedStaticCheckLanguageSupport()
|
|
2301
|
-
.filter(({ language }) => (config
|
|
2302
|
-
.map(({ language, extensions }) =>
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
},
|
|
3184
|
+
.filter(({ language }) => getStaticCheckLanguageConfigForMenu(config, language).checkers.length > 0)
|
|
3185
|
+
.map(({ language, extensions }) => {
|
|
3186
|
+
const languageConfig = getStaticCheckLanguageConfigForMenu(config, language);
|
|
3187
|
+
return {
|
|
3188
|
+
id: language,
|
|
3189
|
+
label: language,
|
|
3190
|
+
value: `${extensions.join(", ")} • ${languageConfig.checkers.length} configured`,
|
|
3191
|
+
description: `Remove every configured static-check entry for ${language}.`,
|
|
3192
|
+
};
|
|
3193
|
+
}),
|
|
3194
|
+
...buildTerminalSettingsMenuChoices({
|
|
3195
|
+
resetDefaultsDescription: "Restore the documented per-language static-check defaults.",
|
|
3196
|
+
}),
|
|
2314
3197
|
];
|
|
2315
3198
|
}
|
|
2316
3199
|
|
|
2317
3200
|
/**
|
|
2318
3201
|
* @brief Runs the interactive static-check configuration menu.
|
|
2319
|
-
* @details Lets the user add Command entries by guided prompts, remove configured language entries, and reset the
|
|
3202
|
+
* @details Lets the user add Command entries by guided prompts, remove configured language entries, toggle direct per-language enable flags, and reset the subtree to documented defaults through the shared settings-menu renderer until the user exits. Runtime depends on user interaction count. Side effects include UI updates and config mutation.
|
|
2320
3203
|
* @param[in] ctx {ExtensionCommandContext} Active command context.
|
|
2321
3204
|
* @param[in,out] config {UseReqConfig} Mutable configuration object.
|
|
2322
3205
|
* @return {Promise<void>} Promise resolved when the menu closes.
|
|
2323
|
-
* @satisfies REQ-008, REQ-
|
|
3206
|
+
* @satisfies REQ-008, REQ-151, REQ-152, REQ-153, REQ-154, REQ-160, REQ-161, REQ-193, REQ-195, REQ-248, REQ-253
|
|
2324
3207
|
*/
|
|
2325
|
-
async function configureStaticCheckMenu(
|
|
3208
|
+
async function configureStaticCheckMenu(
|
|
3209
|
+
ctx: ExtensionCommandContext,
|
|
3210
|
+
config: UseReqConfig,
|
|
3211
|
+
onConfigChange: () => void,
|
|
3212
|
+
): Promise<void> {
|
|
2326
3213
|
let focusedChoiceId: string | undefined;
|
|
2327
3214
|
while (true) {
|
|
2328
3215
|
const staticChoice = await showPiUsereqSettingsMenu(ctx, "Language static code checkers", buildStaticCheckMenuChoices(config), {
|
|
2329
3216
|
initialSelectedId: focusedChoiceId,
|
|
2330
3217
|
});
|
|
2331
3218
|
|
|
2332
|
-
if (!staticChoice
|
|
3219
|
+
if (!staticChoice) {
|
|
2333
3220
|
return;
|
|
2334
3221
|
}
|
|
2335
3222
|
focusedChoiceId = staticChoice;
|
|
2336
3223
|
|
|
3224
|
+
if (staticChoice.startsWith("toggle-static-check-language:")) {
|
|
3225
|
+
const language = staticChoice.slice("toggle-static-check-language:".length);
|
|
3226
|
+
config["static-check"][language] ??= createStaticCheckLanguageConfig([]);
|
|
3227
|
+
const languageConfig = config["static-check"][language]!;
|
|
3228
|
+
languageConfig.enabled = languageConfig.enabled === "enable" ? "disable" : "enable";
|
|
3229
|
+
onConfigChange();
|
|
3230
|
+
ctx.ui.notify(
|
|
3231
|
+
`${languageConfig.enabled === "enable" ? "Enabled" : "Disabled"} static-check for ${language}`,
|
|
3232
|
+
"info",
|
|
3233
|
+
);
|
|
3234
|
+
continue;
|
|
3235
|
+
}
|
|
3236
|
+
|
|
2337
3237
|
if (staticChoice === "add-static-check-entry") {
|
|
2338
3238
|
const selectedLanguage = await showPiUsereqSettingsMenu(ctx, "static-check language", buildSupportedStaticCheckLanguageChoices(config));
|
|
2339
|
-
if (!selectedLanguage
|
|
3239
|
+
if (!selectedLanguage) {
|
|
3240
|
+
continue;
|
|
3241
|
+
}
|
|
3242
|
+
if (selectedLanguage === "reset-defaults") {
|
|
3243
|
+
const approved = await confirmResetChanges(
|
|
3244
|
+
ctx,
|
|
3245
|
+
"Confirm static-check reset",
|
|
3246
|
+
[{
|
|
3247
|
+
label: "Language static code checkers",
|
|
3248
|
+
previousValue: formatStaticCheckLanguagesSummary(config),
|
|
3249
|
+
nextValue: formatStaticCheckLanguagesSummary({ ...config, "static-check": getDefaultStaticCheckConfig() }),
|
|
3250
|
+
}].filter((change) => change.previousValue !== change.nextValue),
|
|
3251
|
+
"Approve restoring the documented per-language static-check defaults.",
|
|
3252
|
+
"Abort the static-check reset and keep the current values.",
|
|
3253
|
+
);
|
|
3254
|
+
if (!approved) {
|
|
3255
|
+
ctx.ui.notify("Aborted static-check reset", "info");
|
|
3256
|
+
continue;
|
|
3257
|
+
}
|
|
3258
|
+
resetStaticCheckConfig(config);
|
|
3259
|
+
onConfigChange();
|
|
3260
|
+
ctx.ui.notify("Restored default static code checker configuration", "info");
|
|
2340
3261
|
continue;
|
|
2341
3262
|
}
|
|
2342
3263
|
|
|
@@ -2356,23 +3277,64 @@ async function configureStaticCheckMenu(ctx: ExtensionCommandContext, config: Us
|
|
|
2356
3277
|
entry.params = params;
|
|
2357
3278
|
}
|
|
2358
3279
|
|
|
2359
|
-
config["static-check"][selectedLanguage] ??= [];
|
|
2360
|
-
config["static-check"][selectedLanguage]!.
|
|
3280
|
+
config["static-check"][selectedLanguage] ??= createStaticCheckLanguageConfig([]);
|
|
3281
|
+
config["static-check"][selectedLanguage]!.enabled = "enable";
|
|
3282
|
+
config["static-check"][selectedLanguage]!.checkers.push(entry);
|
|
3283
|
+
onConfigChange();
|
|
2361
3284
|
ctx.ui.notify(`Added ${entry.module} checker for ${selectedLanguage}`, "info");
|
|
2362
3285
|
continue;
|
|
2363
3286
|
}
|
|
2364
3287
|
|
|
2365
3288
|
if (staticChoice === "remove-static-check-entry") {
|
|
2366
3289
|
const configuredLanguage = await showPiUsereqSettingsMenu(ctx, "Remove static code checker", buildConfiguredStaticCheckLanguageChoices(config));
|
|
2367
|
-
if (!configuredLanguage
|
|
3290
|
+
if (!configuredLanguage) {
|
|
3291
|
+
continue;
|
|
3292
|
+
}
|
|
3293
|
+
if (configuredLanguage === "reset-defaults") {
|
|
3294
|
+
const approved = await confirmResetChanges(
|
|
3295
|
+
ctx,
|
|
3296
|
+
"Confirm static-check reset",
|
|
3297
|
+
[{
|
|
3298
|
+
label: "Language static code checkers",
|
|
3299
|
+
previousValue: formatStaticCheckLanguagesSummary(config),
|
|
3300
|
+
nextValue: formatStaticCheckLanguagesSummary({ ...config, "static-check": getDefaultStaticCheckConfig() }),
|
|
3301
|
+
}].filter((change) => change.previousValue !== change.nextValue),
|
|
3302
|
+
"Approve restoring the documented per-language static-check defaults.",
|
|
3303
|
+
"Abort the static-check reset and keep the current values.",
|
|
3304
|
+
);
|
|
3305
|
+
if (!approved) {
|
|
3306
|
+
ctx.ui.notify("Aborted static-check reset", "info");
|
|
3307
|
+
continue;
|
|
3308
|
+
}
|
|
3309
|
+
resetStaticCheckConfig(config);
|
|
3310
|
+
onConfigChange();
|
|
3311
|
+
ctx.ui.notify("Restored default static code checker configuration", "info");
|
|
2368
3312
|
continue;
|
|
2369
3313
|
}
|
|
2370
|
-
|
|
3314
|
+
config["static-check"][configuredLanguage] = createStaticCheckLanguageConfig([]);
|
|
3315
|
+
onConfigChange();
|
|
2371
3316
|
ctx.ui.notify(`Removed static-check entries for ${configuredLanguage}`, "info");
|
|
2372
3317
|
continue;
|
|
2373
3318
|
}
|
|
3319
|
+
|
|
2374
3320
|
if (staticChoice === "reset-defaults") {
|
|
2375
|
-
|
|
3321
|
+
const approved = await confirmResetChanges(
|
|
3322
|
+
ctx,
|
|
3323
|
+
"Confirm static-check reset",
|
|
3324
|
+
[{
|
|
3325
|
+
label: "Language static code checkers",
|
|
3326
|
+
previousValue: formatStaticCheckLanguagesSummary(config),
|
|
3327
|
+
nextValue: formatStaticCheckLanguagesSummary({ ...config, "static-check": getDefaultStaticCheckConfig() }),
|
|
3328
|
+
}].filter((change) => change.previousValue !== change.nextValue),
|
|
3329
|
+
"Approve restoring the documented per-language static-check defaults.",
|
|
3330
|
+
"Abort the static-check reset and keep the current values.",
|
|
3331
|
+
);
|
|
3332
|
+
if (!approved) {
|
|
3333
|
+
ctx.ui.notify("Aborted static-check reset", "info");
|
|
3334
|
+
continue;
|
|
3335
|
+
}
|
|
3336
|
+
resetStaticCheckConfig(config);
|
|
3337
|
+
onConfigChange();
|
|
2376
3338
|
ctx.ui.notify("Restored default static code checker configuration", "info");
|
|
2377
3339
|
}
|
|
2378
3340
|
}
|
|
@@ -2380,16 +3342,21 @@ async function configureStaticCheckMenu(ctx: ExtensionCommandContext, config: Us
|
|
|
2380
3342
|
|
|
2381
3343
|
/**
|
|
2382
3344
|
* @brief Builds the shared settings-menu choices for the top-level pi-usereq configuration UI.
|
|
2383
|
-
* @details Serializes primary configuration actions into right-valued menu rows consumed by the shared settings-menu renderer, including the display-only config path beside `show-config`. Runtime is O(s) in source-directory count. No external state is mutated.
|
|
3345
|
+
* @details Serializes primary configuration actions into right-valued menu rows consumed by the shared settings-menu renderer, including automatic git-commit mode, effective prompt-command worktree state, notification summary, debug summary, locked worktree rows when automatic git commit is disabled, and the display-only config path beside `show-config`. Runtime is O(s) in source-directory count. No external state is mutated.
|
|
2384
3346
|
* @param[in] cwd {string} Current working directory.
|
|
2385
3347
|
* @param[in] config {UseReqConfig} Effective project configuration.
|
|
2386
3348
|
* @return {PiUsereqSettingsMenuChoice[]} Ordered top-level menu choices.
|
|
2387
|
-
* @satisfies REQ-006, REQ-031, REQ-137, REQ-150, REQ-151, REQ-152, REQ-162, REQ-190, REQ-191, REQ-197
|
|
3349
|
+
* @satisfies REQ-006, REQ-031, REQ-137, REQ-150, REQ-151, REQ-152, REQ-162, REQ-190, REQ-191, REQ-197, REQ-204, REQ-205, REQ-212, REQ-215, REQ-216, REQ-236, REQ-237, REQ-238, REQ-239, REQ-240
|
|
2388
3350
|
*/
|
|
2389
3351
|
function buildPiUsereqMenuChoices(
|
|
2390
3352
|
cwd: string,
|
|
2391
3353
|
config: UseReqConfig,
|
|
2392
3354
|
): PiUsereqSettingsMenuChoice[] {
|
|
3355
|
+
const autoGitCommitDisabled = config.AUTO_GIT_COMMIT === "disable";
|
|
3356
|
+
const effectiveGitWorktreeEnabled = resolveEffectiveGitWorktreeEnabled(
|
|
3357
|
+
config.AUTO_GIT_COMMIT,
|
|
3358
|
+
config.GIT_WORKTREE_ENABLED,
|
|
3359
|
+
);
|
|
2393
3360
|
return [
|
|
2394
3361
|
{
|
|
2395
3362
|
id: "docs-dir",
|
|
@@ -2409,11 +3376,37 @@ function buildPiUsereqMenuChoices(
|
|
|
2409
3376
|
value: config["tests-dir"],
|
|
2410
3377
|
description: "Edit the repository-relative directory used for project test assets and static-check selection.",
|
|
2411
3378
|
},
|
|
3379
|
+
{
|
|
3380
|
+
id: "auto-git-commit",
|
|
3381
|
+
label: "Auto git commit",
|
|
3382
|
+
value: config.AUTO_GIT_COMMIT,
|
|
3383
|
+
description: "Select bundled `git_commit.md` or `git_read-only.md` for `%%COMMIT%%`; disabling also forces prompt-command worktrees off.",
|
|
3384
|
+
},
|
|
3385
|
+
{
|
|
3386
|
+
id: "git-worktree-enabled",
|
|
3387
|
+
label: "Git worktree",
|
|
3388
|
+
labelTone: autoGitCommitDisabled ? "dim" : undefined,
|
|
3389
|
+
value: effectiveGitWorktreeEnabled,
|
|
3390
|
+
valueTone: autoGitCommitDisabled ? "dim" : undefined,
|
|
3391
|
+
description: autoGitCommitDisabled
|
|
3392
|
+
? "Forced to `disable` while `Auto git commit` is disabled."
|
|
3393
|
+
: "Enable or disable prompt-command worktree orchestration.",
|
|
3394
|
+
},
|
|
3395
|
+
{
|
|
3396
|
+
id: "git-worktree-prefix",
|
|
3397
|
+
label: "Worktree prefix",
|
|
3398
|
+
labelTone: autoGitCommitDisabled ? "dim" : undefined,
|
|
3399
|
+
value: config.GIT_WORKTREE_PREFIX,
|
|
3400
|
+
valueTone: autoGitCommitDisabled ? "dim" : undefined,
|
|
3401
|
+
description: autoGitCommitDisabled
|
|
3402
|
+
? "Stored prefix is locked while `Auto git commit` is disabled."
|
|
3403
|
+
: "Edit the static prefix used by generated worktree names.",
|
|
3404
|
+
},
|
|
2412
3405
|
{
|
|
2413
3406
|
id: "static-check",
|
|
2414
3407
|
label: "Language static code checkers",
|
|
2415
3408
|
value: formatStaticCheckLanguagesSummary(config),
|
|
2416
|
-
description: "Manage guided Command static-check entries
|
|
3409
|
+
description: "Manage guided Command static-check entries and per-language enable flags.",
|
|
2417
3410
|
},
|
|
2418
3411
|
{
|
|
2419
3412
|
id: "startup-tools",
|
|
@@ -2427,25 +3420,22 @@ function buildPiUsereqMenuChoices(
|
|
|
2427
3420
|
value: `notification:${formatPiNotifyStatus(config)} • sound:${config["notify-sound"]} • pushover:${formatPiNotifyPushoverStatus(config)}`,
|
|
2428
3421
|
description: "Manage command-notify, sound, and Pushover settings with dedicated event submenus.",
|
|
2429
3422
|
},
|
|
3423
|
+
{
|
|
3424
|
+
id: "debug",
|
|
3425
|
+
label: "Debug",
|
|
3426
|
+
value: formatDebugMenuSummary(config),
|
|
3427
|
+
description: "Manage debug logging for tools and `req-*` prompt orchestration.",
|
|
3428
|
+
},
|
|
2430
3429
|
{
|
|
2431
3430
|
id: "show-config",
|
|
2432
3431
|
label: "Show configuration",
|
|
2433
3432
|
value: formatProjectConfigPathForMenu(cwd),
|
|
2434
3433
|
valueTone: "dim",
|
|
2435
|
-
description: "
|
|
2436
|
-
},
|
|
2437
|
-
{
|
|
2438
|
-
id: "reset-defaults",
|
|
2439
|
-
label: "Reset defaults",
|
|
2440
|
-
value: "",
|
|
2441
|
-
description: "Restore the default pi-usereq configuration for the current project base.",
|
|
2442
|
-
},
|
|
2443
|
-
{
|
|
2444
|
-
id: "save-and-close",
|
|
2445
|
-
label: "Save and close",
|
|
2446
|
-
value: "",
|
|
2447
|
-
description: "Persist the current configuration and return to the normal pi session UI.",
|
|
3434
|
+
description: "Persist the current project configuration file and write its exact text into the editor.",
|
|
2448
3435
|
},
|
|
3436
|
+
...buildTerminalSettingsMenuChoices({
|
|
3437
|
+
resetDefaultsDescription: "Restore the default pi-usereq configuration for the current project base.",
|
|
3438
|
+
}),
|
|
2449
3439
|
];
|
|
2450
3440
|
}
|
|
2451
3441
|
|
|
@@ -2470,24 +3460,15 @@ function buildSrcDirMenuChoices(config: UseReqConfig): PiUsereqSettingsMenuChoic
|
|
|
2470
3460
|
value: config["src-dir"].join(", "),
|
|
2471
3461
|
description: "Select one configured source directory to remove from the current configuration.",
|
|
2472
3462
|
},
|
|
2473
|
-
{
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
value: `${DEFAULT_SRC_DIRS.join(", ")}`,
|
|
2477
|
-
description: "Restore the documented default source-directory configuration.",
|
|
2478
|
-
},
|
|
2479
|
-
{
|
|
2480
|
-
id: "save-and-close",
|
|
2481
|
-
label: "Save and close",
|
|
2482
|
-
value: "",
|
|
2483
|
-
description: "Return to the parent configuration menu.",
|
|
2484
|
-
},
|
|
3463
|
+
...buildTerminalSettingsMenuChoices({
|
|
3464
|
+
resetDefaultsDescription: "Restore the documented default source-directory configuration.",
|
|
3465
|
+
}),
|
|
2485
3466
|
];
|
|
2486
3467
|
}
|
|
2487
3468
|
|
|
2488
3469
|
/**
|
|
2489
3470
|
* @brief Builds the shared settings-menu choices for removing one source-directory entry.
|
|
2490
|
-
* @details Exposes every configured `src-dir` entry as one removable row and appends a `
|
|
3471
|
+
* @details Exposes every configured `src-dir` entry as one removable row and appends a value-less subtree-local `Reset defaults` row. Runtime is O(s) in source-directory count. No external state is mutated.
|
|
2491
3472
|
* @param[in] config {UseReqConfig} Effective project configuration.
|
|
2492
3473
|
* @return {PiUsereqSettingsMenuChoice[]} Ordered removable source-directory choices.
|
|
2493
3474
|
* @satisfies REQ-006, REQ-151, REQ-152, REQ-153, REQ-154
|
|
@@ -2500,34 +3481,35 @@ function buildSrcDirRemovalChoices(config: UseReqConfig): PiUsereqSettingsMenuCh
|
|
|
2500
3481
|
value: "remove",
|
|
2501
3482
|
description: `Remove the source-directory entry ${entry} from the current configuration.`,
|
|
2502
3483
|
})),
|
|
2503
|
-
{
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
value: "",
|
|
2507
|
-
description: "Return to the source-directory menu.",
|
|
2508
|
-
},
|
|
3484
|
+
...buildTerminalSettingsMenuChoices({
|
|
3485
|
+
resetDefaultsDescription: "Restore the documented default source-directory configuration.",
|
|
3486
|
+
}),
|
|
2509
3487
|
];
|
|
2510
3488
|
}
|
|
2511
3489
|
|
|
2512
3490
|
/**
|
|
2513
3491
|
* @brief Runs the top-level pi-usereq configuration menu.
|
|
2514
|
-
* @details Loads project config, exposes docs/test/source/static-check/startup-tool/notification actions through the shared settings-menu renderer, persists changes on exit, and refreshes the single-line status bar. Runtime depends on user interaction count. Side effects include UI updates, config writes, active-tool changes, and editor text updates.
|
|
3492
|
+
* @details Loads project config, exposes docs/test/source/automatic-commit/worktree/static-check/startup-tool/notification/debug actions through the shared settings-menu renderer, forces worktree disablement when automatic git commit is disabled, prevents locked row edits, persists changes on exit, closes immediately after `Show configuration`, and refreshes the single-line status bar. Runtime depends on user interaction count. Side effects include UI updates, config writes, active-tool changes, and editor text updates.
|
|
2515
3493
|
* @param[in] pi {ExtensionAPI} Active extension API instance.
|
|
2516
3494
|
* @param[in] ctx {ExtensionCommandContext} Active command context.
|
|
2517
3495
|
* @param[in,out] statusController {PiUsereqStatusController} Mutable status controller.
|
|
2518
3496
|
* @return {Promise<void>} Promise resolved when configuration is saved and the menu closes.
|
|
2519
|
-
* @satisfies REQ-006, REQ-031, REQ-137, REQ-150, REQ-151, REQ-152, REQ-153, REQ-154, REQ-162, REQ-190, REQ-191, REQ-192, REQ-194, REQ-195
|
|
3497
|
+
* @satisfies REQ-006, REQ-031, REQ-137, REQ-150, REQ-151, REQ-152, REQ-153, REQ-154, REQ-162, REQ-190, REQ-191, REQ-192, REQ-194, REQ-195, REQ-204, REQ-205, REQ-212, REQ-215, REQ-216, REQ-236, REQ-237, REQ-238, REQ-239, REQ-240, REQ-241, REQ-242, REQ-243
|
|
2520
3498
|
*/
|
|
2521
3499
|
async function configurePiUsereq(
|
|
2522
3500
|
pi: ExtensionAPI,
|
|
2523
3501
|
ctx: ExtensionCommandContext,
|
|
2524
3502
|
statusController: PiUsereqStatusController,
|
|
2525
3503
|
): Promise<void> {
|
|
3504
|
+
bootstrapRuntimePathState(ctx.cwd, {
|
|
3505
|
+
gitPath: resolveRuntimeGitPath(ctx.cwd),
|
|
3506
|
+
});
|
|
2526
3507
|
let config = loadProjectConfig(ctx.cwd);
|
|
2527
3508
|
const projectBase = getProjectBase(ctx.cwd);
|
|
2528
3509
|
const initialShortcut = config["notify-sound-toggle-shortcut"];
|
|
2529
|
-
const
|
|
2530
|
-
|
|
3510
|
+
const persistConfigChange = () => {
|
|
3511
|
+
Object.assign(config, normalizeConfigPaths(projectBase, config));
|
|
3512
|
+
saveProjectConfig(ctx.cwd, config);
|
|
2531
3513
|
setPiUsereqStatusConfig(statusController, config);
|
|
2532
3514
|
renderPiUsereqStatus(statusController, ctx);
|
|
2533
3515
|
};
|
|
@@ -2540,9 +3522,7 @@ async function configurePiUsereq(
|
|
|
2540
3522
|
buildPiUsereqMenuChoices(ctx.cwd, config),
|
|
2541
3523
|
{ initialSelectedId: focusedChoiceId },
|
|
2542
3524
|
);
|
|
2543
|
-
if (!choice
|
|
2544
|
-
ensureSaved();
|
|
2545
|
-
refreshStatus();
|
|
3525
|
+
if (!choice) {
|
|
2546
3526
|
if (config["notify-sound-toggle-shortcut"] !== initialShortcut) {
|
|
2547
3527
|
ctx.ui.notify("Sound toggle hotkey bind updated; run /reload to apply the new binding", "info");
|
|
2548
3528
|
}
|
|
@@ -2551,12 +3531,61 @@ async function configurePiUsereq(
|
|
|
2551
3531
|
focusedChoiceId = choice;
|
|
2552
3532
|
if (choice === "docs-dir") {
|
|
2553
3533
|
const value = await ctx.ui.input("Document directory", config["docs-dir"]);
|
|
2554
|
-
if (value?.trim())
|
|
3534
|
+
if (value?.trim()) {
|
|
3535
|
+
config["docs-dir"] = value.trim();
|
|
3536
|
+
persistConfigChange();
|
|
3537
|
+
}
|
|
2555
3538
|
continue;
|
|
2556
3539
|
}
|
|
2557
3540
|
if (choice === "tests-dir") {
|
|
2558
3541
|
const value = await ctx.ui.input("Unit tests directory", config["tests-dir"]);
|
|
2559
|
-
if (value?.trim())
|
|
3542
|
+
if (value?.trim()) {
|
|
3543
|
+
config["tests-dir"] = value.trim();
|
|
3544
|
+
persistConfigChange();
|
|
3545
|
+
}
|
|
3546
|
+
continue;
|
|
3547
|
+
}
|
|
3548
|
+
if (choice === "auto-git-commit") {
|
|
3549
|
+
const nextAutoGitCommit = config.AUTO_GIT_COMMIT === "enable"
|
|
3550
|
+
? "disable"
|
|
3551
|
+
: "enable";
|
|
3552
|
+
config.AUTO_GIT_COMMIT = nextAutoGitCommit;
|
|
3553
|
+
if (nextAutoGitCommit === "disable") {
|
|
3554
|
+
config.GIT_WORKTREE_ENABLED = "disable";
|
|
3555
|
+
persistConfigChange();
|
|
3556
|
+
ctx.ui.notify("Auto git commit disabled; Git worktree forced off", "info");
|
|
3557
|
+
} else {
|
|
3558
|
+
persistConfigChange();
|
|
3559
|
+
ctx.ui.notify("Auto git commit enabled", "info");
|
|
3560
|
+
}
|
|
3561
|
+
continue;
|
|
3562
|
+
}
|
|
3563
|
+
if (choice === "git-worktree-enabled") {
|
|
3564
|
+
if (config.AUTO_GIT_COMMIT === "disable") {
|
|
3565
|
+
ctx.ui.notify("Git worktree is locked while Auto git commit is disabled", "info");
|
|
3566
|
+
continue;
|
|
3567
|
+
}
|
|
3568
|
+
config.GIT_WORKTREE_ENABLED = config.GIT_WORKTREE_ENABLED === "enable"
|
|
3569
|
+
? "disable"
|
|
3570
|
+
: "enable";
|
|
3571
|
+
persistConfigChange();
|
|
3572
|
+
ctx.ui.notify(
|
|
3573
|
+
`Git worktree ${resolveEffectiveGitWorktreeEnabled(config.AUTO_GIT_COMMIT, config.GIT_WORKTREE_ENABLED) === "enable" ? "enabled" : "disabled"}`,
|
|
3574
|
+
"info",
|
|
3575
|
+
);
|
|
3576
|
+
continue;
|
|
3577
|
+
}
|
|
3578
|
+
if (choice === "git-worktree-prefix") {
|
|
3579
|
+
if (config.AUTO_GIT_COMMIT === "disable") {
|
|
3580
|
+
ctx.ui.notify("Worktree prefix is locked while Auto git commit is disabled", "info");
|
|
3581
|
+
continue;
|
|
3582
|
+
}
|
|
3583
|
+
const value = await ctx.ui.input("Worktree prefix", config.GIT_WORKTREE_PREFIX);
|
|
3584
|
+
if (value !== undefined) {
|
|
3585
|
+
config.GIT_WORKTREE_PREFIX = value.trim() || DEFAULT_GIT_WORKTREE_PREFIX;
|
|
3586
|
+
persistConfigChange();
|
|
3587
|
+
ctx.ui.notify(`Worktree prefix set to ${config.GIT_WORKTREE_PREFIX}`, "info");
|
|
3588
|
+
}
|
|
2560
3589
|
continue;
|
|
2561
3590
|
}
|
|
2562
3591
|
if (choice === "src-dir") {
|
|
@@ -2565,7 +3594,7 @@ async function configurePiUsereq(
|
|
|
2565
3594
|
const srcAction = await showPiUsereqSettingsMenu(ctx, "Source-code directories", buildSrcDirMenuChoices(config), {
|
|
2566
3595
|
initialSelectedId: srcFocusedChoiceId,
|
|
2567
3596
|
});
|
|
2568
|
-
if (!srcAction
|
|
3597
|
+
if (!srcAction) {
|
|
2569
3598
|
break;
|
|
2570
3599
|
}
|
|
2571
3600
|
srcFocusedChoiceId = srcAction;
|
|
@@ -2573,47 +3602,118 @@ async function configurePiUsereq(
|
|
|
2573
3602
|
const value = await ctx.ui.input("New source-code directory", "src");
|
|
2574
3603
|
if (value?.trim()) {
|
|
2575
3604
|
config["src-dir"] = [...config["src-dir"], value.trim()];
|
|
3605
|
+
persistConfigChange();
|
|
2576
3606
|
}
|
|
2577
3607
|
continue;
|
|
2578
3608
|
}
|
|
2579
3609
|
if (srcAction === "remove-src-dir-entry") {
|
|
2580
3610
|
const toRemove = await showPiUsereqSettingsMenu(ctx, "Remove source-code directory", buildSrcDirRemovalChoices(config));
|
|
2581
|
-
if (toRemove
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
3611
|
+
if (!toRemove) {
|
|
3612
|
+
continue;
|
|
3613
|
+
}
|
|
3614
|
+
if (toRemove === "reset-defaults") {
|
|
3615
|
+
const approved = await confirmResetChanges(
|
|
3616
|
+
ctx,
|
|
3617
|
+
"Confirm source-directory reset",
|
|
3618
|
+
[{
|
|
3619
|
+
label: "Source-code directories",
|
|
3620
|
+
previousValue: config["src-dir"].join(", "),
|
|
3621
|
+
nextValue: DEFAULT_SRC_DIRS.join(", "),
|
|
3622
|
+
}].filter((change) => change.previousValue !== change.nextValue),
|
|
3623
|
+
"Approve restoring the documented default source-directory configuration.",
|
|
3624
|
+
"Abort the source-directory reset and keep the current values.",
|
|
3625
|
+
);
|
|
3626
|
+
if (!approved) {
|
|
3627
|
+
ctx.ui.notify("Aborted source-directory reset", "info");
|
|
3628
|
+
continue;
|
|
2585
3629
|
}
|
|
3630
|
+
config["src-dir"] = [...DEFAULT_SRC_DIRS];
|
|
3631
|
+
persistConfigChange();
|
|
3632
|
+
ctx.ui.notify("Restored default source-code directories", "info");
|
|
3633
|
+
continue;
|
|
3634
|
+
}
|
|
3635
|
+
config["src-dir"] = config["src-dir"].filter((entry) => entry !== toRemove);
|
|
3636
|
+
if (config["src-dir"].length === 0) {
|
|
3637
|
+
config["src-dir"] = ["src"];
|
|
2586
3638
|
}
|
|
3639
|
+
persistConfigChange();
|
|
2587
3640
|
continue;
|
|
2588
3641
|
}
|
|
2589
3642
|
if (srcAction === "reset-defaults") {
|
|
3643
|
+
const approved = await confirmResetChanges(
|
|
3644
|
+
ctx,
|
|
3645
|
+
"Confirm source-directory reset",
|
|
3646
|
+
[{
|
|
3647
|
+
label: "Source-code directories",
|
|
3648
|
+
previousValue: config["src-dir"].join(", "),
|
|
3649
|
+
nextValue: DEFAULT_SRC_DIRS.join(", "),
|
|
3650
|
+
}].filter((change) => change.previousValue !== change.nextValue),
|
|
3651
|
+
"Approve restoring the documented default source-directory configuration.",
|
|
3652
|
+
"Abort the source-directory reset and keep the current values.",
|
|
3653
|
+
);
|
|
3654
|
+
if (!approved) {
|
|
3655
|
+
ctx.ui.notify("Aborted source-directory reset", "info");
|
|
3656
|
+
continue;
|
|
3657
|
+
}
|
|
2590
3658
|
config["src-dir"] = [...DEFAULT_SRC_DIRS];
|
|
3659
|
+
persistConfigChange();
|
|
2591
3660
|
ctx.ui.notify("Restored default source-code directories", "info");
|
|
2592
3661
|
}
|
|
2593
3662
|
}
|
|
2594
3663
|
continue;
|
|
2595
3664
|
}
|
|
2596
3665
|
if (choice === "static-check") {
|
|
2597
|
-
await configureStaticCheckMenu(ctx, config);
|
|
3666
|
+
await configureStaticCheckMenu(ctx, config, persistConfigChange);
|
|
2598
3667
|
continue;
|
|
2599
3668
|
}
|
|
2600
3669
|
if (choice === "startup-tools") {
|
|
2601
|
-
await configurePiUsereqToolsMenu(pi, ctx, config);
|
|
3670
|
+
await configurePiUsereqToolsMenu(pi, ctx, config, persistConfigChange);
|
|
2602
3671
|
continue;
|
|
2603
3672
|
}
|
|
2604
3673
|
if (choice === "notifications") {
|
|
2605
|
-
await configurePiNotifyMenu(ctx, config);
|
|
3674
|
+
await configurePiNotifyMenu(ctx, config, persistConfigChange);
|
|
3675
|
+
continue;
|
|
3676
|
+
}
|
|
3677
|
+
if (choice === "debug") {
|
|
3678
|
+
await configureDebugMenu(ctx, config, persistConfigChange);
|
|
2606
3679
|
continue;
|
|
2607
3680
|
}
|
|
2608
3681
|
if (choice === "reset-defaults") {
|
|
2609
|
-
|
|
3682
|
+
const defaultConfig = getDefaultConfig(projectBase);
|
|
3683
|
+
const approved = await confirmResetChanges(
|
|
3684
|
+
ctx,
|
|
3685
|
+
"Confirm pi-usereq reset",
|
|
3686
|
+
[
|
|
3687
|
+
{ label: "Document directory", previousValue: config["docs-dir"], nextValue: defaultConfig["docs-dir"] },
|
|
3688
|
+
{ label: "Source-code directories", previousValue: config["src-dir"].join(", "), nextValue: defaultConfig["src-dir"].join(", ") },
|
|
3689
|
+
{ label: "Unit tests directory", previousValue: config["tests-dir"], nextValue: defaultConfig["tests-dir"] },
|
|
3690
|
+
{ label: "Auto git commit", previousValue: config.AUTO_GIT_COMMIT, nextValue: defaultConfig.AUTO_GIT_COMMIT },
|
|
3691
|
+
{ label: "Git worktree", previousValue: config.GIT_WORKTREE_ENABLED, nextValue: defaultConfig.GIT_WORKTREE_ENABLED },
|
|
3692
|
+
{ label: "Worktree prefix", previousValue: config.GIT_WORKTREE_PREFIX, nextValue: defaultConfig.GIT_WORKTREE_PREFIX },
|
|
3693
|
+
{ label: "Enable tools", previousValue: String(getConfiguredEnabledPiUsereqTools(config).length), nextValue: String(getConfiguredEnabledPiUsereqTools(defaultConfig).length) },
|
|
3694
|
+
{ label: "Notifications", previousValue: buildPiNotifyMenuChoices(config).length.toString(), nextValue: buildPiNotifyMenuChoices(defaultConfig).length.toString() },
|
|
3695
|
+
{ label: "Debug", previousValue: formatDebugMenuSummary(config), nextValue: formatDebugMenuSummary(defaultConfig) },
|
|
3696
|
+
].filter((change) => change.previousValue !== change.nextValue),
|
|
3697
|
+
"Approve restoring the default pi-usereq configuration.",
|
|
3698
|
+
"Abort the pi-usereq reset and keep the current values.",
|
|
3699
|
+
);
|
|
3700
|
+
if (!approved) {
|
|
3701
|
+
ctx.ui.notify("Aborted pi-usereq reset", "info");
|
|
3702
|
+
continue;
|
|
3703
|
+
}
|
|
3704
|
+
config = defaultConfig;
|
|
2610
3705
|
applyConfiguredPiUsereqTools(pi, config);
|
|
3706
|
+
persistConfigChange();
|
|
2611
3707
|
ctx.ui.notify("Restored all default configuration values", "info");
|
|
2612
3708
|
continue;
|
|
2613
3709
|
}
|
|
2614
3710
|
if (choice === "show-config") {
|
|
2615
|
-
|
|
2616
|
-
|
|
3711
|
+
persistConfigChange();
|
|
3712
|
+
if (config["notify-sound-toggle-shortcut"] !== initialShortcut) {
|
|
3713
|
+
ctx.ui.notify("Sound toggle hotkey bind updated; run /reload to apply the new binding", "info");
|
|
3714
|
+
}
|
|
3715
|
+
writePersistedProjectConfigToEditor(ctx, ctx.cwd, config);
|
|
3716
|
+
return;
|
|
2617
3717
|
}
|
|
2618
3718
|
}
|
|
2619
3719
|
}
|
|
@@ -2645,14 +3745,14 @@ function registerConfigCommands(
|
|
|
2645
3745
|
* notification-sound shortcut when the runtime supports shortcuts, and
|
|
2646
3746
|
* installs shared wrappers for all supported pi lifecycle hooks so status
|
|
2647
3747
|
* telemetry, context usage, prompt timing, cumulative runtime, prompt-specific
|
|
2648
|
-
* Pushover metadata,
|
|
2649
|
-
* events. Runtime is O(h) in hook
|
|
3748
|
+
* Pushover metadata, tool-result debug logging, and prompt-orchestration debug
|
|
3749
|
+
* effects remain synchronized with runtime events. Runtime is O(h) in hook
|
|
2650
3750
|
* count during registration. Side effects include filesystem reads,
|
|
2651
|
-
* command/tool/shortcut registration, UI updates, active-tool changes,
|
|
2652
|
-
* timer scheduling.
|
|
3751
|
+
* command/tool/shortcut registration, UI updates, active-tool changes,
|
|
3752
|
+
* optional debug-log writes, and timer scheduling.
|
|
2653
3753
|
* @param[in] pi {ExtensionAPI} Active extension API instance.
|
|
2654
3754
|
* @return {void} No return value.
|
|
2655
|
-
* @satisfies DES-002, REQ-004, REQ-005, REQ-009, REQ-044, REQ-
|
|
3755
|
+
* @satisfies DES-002, REQ-004, REQ-005, REQ-009, REQ-044, REQ-067, REQ-068, REQ-109, REQ-111, REQ-112, REQ-113, REQ-114, REQ-115, REQ-116, REQ-117, REQ-118, REQ-119, REQ-120, REQ-121, REQ-122, REQ-123, REQ-124, REQ-125, REQ-126, REQ-127, REQ-128, REQ-131, REQ-132, REQ-133, REQ-134, REQ-137, REQ-159, REQ-163, REQ-164, REQ-165, REQ-166, REQ-167, REQ-168, REQ-169, REQ-172, REQ-174, REQ-179, REQ-180, REQ-184, REQ-188, REQ-190, REQ-191, REQ-192, REQ-193, REQ-194, REQ-195, REQ-196, REQ-197, REQ-236, REQ-237, REQ-238, REQ-239, REQ-240, REQ-241, REQ-242, REQ-243, REQ-244, REQ-245, REQ-246, REQ-247
|
|
2656
3756
|
*/
|
|
2657
3757
|
export default function piUsereqExtension(pi: ExtensionAPI): void {
|
|
2658
3758
|
const statusController = createPiUsereqStatusController();
|