pi-usereq 0.4.0 → 0.6.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.
Files changed (65) hide show
  1. package/.gitignore +3 -0
  2. package/CHANGELOG.md +41 -0
  3. package/README.md +1 -1
  4. package/package.json +1 -1
  5. package/{req → pi-usereq}/docs/REFERENCES.md +499 -491
  6. package/{req → pi-usereq}/docs/REQUIREMENTS.md +84 -62
  7. package/{req → pi-usereq}/docs/WORKFLOW.md +87 -102
  8. package/src/core/agent-tool-json.ts +42 -193
  9. package/src/core/compress-payload.ts +7 -15
  10. package/src/core/config.ts +39 -12
  11. package/src/core/extension-status.ts +94 -58
  12. package/src/core/find-payload.ts +21 -44
  13. package/src/core/pi-notify.ts +198 -11
  14. package/src/core/reference-payload.ts +6 -14
  15. package/src/core/runtime-project-paths.ts +3 -32
  16. package/src/core/settings-menu.ts +9 -4
  17. package/src/core/static-check.ts +35 -171
  18. package/src/core/token-counter.ts +3 -121
  19. package/src/index.ts +339 -180
  20. package/tests/attended-results-scenarios.ts +3 -13
  21. package/tests/cli-command-option-parity.test.ts +21 -12
  22. package/tests/debug-extension-harness.test.ts +20 -22
  23. package/tests/extension-registration.test.ts +394 -157
  24. package/tests/oracle-project.test.ts +8 -3
  25. package/tests/oracle-standalone.test.ts +7 -13
  26. package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_c.c.json +0 -5
  27. package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_cpp.cpp.json +0 -5
  28. package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_csharp.cs.json +0 -5
  29. package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_elixir.ex.json +0 -5
  30. package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_go.go.json +0 -5
  31. package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_haskell.hs.json +0 -5
  32. package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_java.java.json +0 -5
  33. package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_javascript.js.json +0 -5
  34. package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_kotlin.kt.json +0 -5
  35. package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_lua.lua.json +0 -5
  36. package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_perl.pl.json +0 -5
  37. package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_php.php.json +0 -5
  38. package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_python.py.json +0 -5
  39. package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_ruby.rb.json +0 -5
  40. package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_rust.rs.json +0 -5
  41. package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_scala.scala.json +0 -5
  42. package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_shell.sh.json +0 -5
  43. package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_swift.swift.json +0 -5
  44. package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_typescript.ts.json +0 -5
  45. package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_zig.zig.json +0 -5
  46. package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_c.c.json +0 -5
  47. package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_cpp.cpp.json +0 -5
  48. package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_csharp.cs.json +0 -5
  49. package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_elixir.ex.json +0 -5
  50. package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_go.go.json +0 -5
  51. package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_haskell.hs.json +0 -5
  52. package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_java.java.json +0 -5
  53. package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_javascript.js.json +0 -5
  54. package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_kotlin.kt.json +0 -5
  55. package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_lua.lua.json +0 -5
  56. package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_perl.pl.json +0 -5
  57. package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_php.php.json +0 -5
  58. package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_python.py.json +0 -5
  59. package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_ruby.rb.json +0 -5
  60. package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_rust.rs.json +0 -5
  61. package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_scala.scala.json +0 -5
  62. package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_shell.sh.json +0 -5
  63. package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_swift.swift.json +0 -5
  64. package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_typescript.ts.json +0 -5
  65. package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_zig.zig.json +0 -5
package/src/index.ts CHANGED
@@ -8,7 +8,7 @@
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.4.0"
11
+ export const VERSION = "0.6.0"
12
12
 
13
13
  import path from "node:path";
14
14
  import type {
@@ -53,6 +53,7 @@ import {
53
53
  } from "./core/find-payload.js";
54
54
  import {
55
55
  getDefaultConfig,
56
+ getProjectConfigPath,
56
57
  loadConfig,
57
58
  normalizeConfigPaths,
58
59
  saveConfig,
@@ -62,10 +63,15 @@ import {
62
63
  import {
63
64
  cyclePiNotifySoundLevel,
64
65
  formatPiNotifyBeepStatus,
66
+ formatPiNotifyPushoverStatus,
67
+ normalizePiNotifyPushoverCredential,
68
+ normalizePiNotifyPushoverPriority,
65
69
  runPiNotifyEffects,
70
+ type PiNotifyPushoverPriority,
71
+ type PiNotifyPushoverRequest,
66
72
  type PiNotifySoundLevel,
67
73
  } from "./core/pi-notify.js";
68
- import { buildRuntimePathContext, buildRuntimePathFacts } from "./core/path-context.js";
74
+ import { formatRuntimePathForDisplay } from "./core/path-context.js";
69
75
  import { resolveRuntimeGitPath } from "./core/runtime-project-paths.js";
70
76
  import { showPiUsereqSettingsMenu, type PiUsereqSettingsMenuChoice } from "./core/settings-menu.js";
71
77
  import {
@@ -154,20 +160,6 @@ function getProjectBase(cwd: string): string {
154
160
  return path.resolve(cwd);
155
161
  }
156
162
 
157
- /**
158
- * @brief Builds the shared runtime path facts for the current command or tool context.
159
- * @details Derives installation, execution, base, config, resource, docs, test, source, and optional git paths from the cwd-derived project configuration plus runtime-only repository probing, then converts them into prompt/tool-facing strings. Runtime is O(s + p) where s is configured source-directory count and p is aggregate path length. Side effects are limited to git subprocess execution.
160
- * @param[in] cwd {string} Current working directory.
161
- * @param[in] config {UseReqConfig} Effective project configuration.
162
- * @return {import("./core/path-context.js").RuntimePathFacts} Shared runtime path facts.
163
- * @satisfies REQ-145, REQ-146
164
- */
165
- function buildSharedRuntimePathFacts(cwd: string, config: UseReqConfig): import("./core/path-context.js").RuntimePathFacts {
166
- const projectBase = getProjectBase(cwd);
167
- const gitPath = resolveRuntimeGitPath(projectBase);
168
- return buildRuntimePathFacts(buildRuntimePathContext(projectBase, config, { gitPath }));
169
- }
170
-
171
163
  /**
172
164
  * @brief Loads project configuration for the extension runtime.
173
165
  * @details Resolves the project base, loads persisted config, and normalizes configured directory paths without reading or persisting runtime-derived `base-path` or `git-path` metadata. Runtime is dominated by config I/O. Side effects are limited to filesystem reads.
@@ -193,6 +185,19 @@ function saveProjectConfig(cwd: string, config: UseReqConfig): void {
193
185
  saveConfig(projectBase, normalizeConfigPaths(projectBase, config));
194
186
  }
195
187
 
188
+ /**
189
+ * @brief Formats the current project config path for top-level menu display.
190
+ * @details Resolves `<base-path>/.pi-usereq/config.json` from the cwd-derived
191
+ * project base and formats it relative to the user home when possible. Runtime
192
+ * is O(p) in path length. No external state is mutated.
193
+ * @param[in] cwd {string} Current working directory.
194
+ * @return {string} User-home-relative or absolute config path display value.
195
+ * @satisfies REQ-162
196
+ */
197
+ function formatProjectConfigPathForMenu(cwd: string): string {
198
+ return formatRuntimePathForDisplay(getProjectConfigPath(getProjectBase(cwd)));
199
+ }
200
+
196
201
  /**
197
202
  * @brief Collects the project-scoped static-check selection used by the agent tool.
198
203
  * @details Resolves configured source plus test directories, reuses the same fixture-root exclusions as `runProjectStaticCheck`, and returns canonical relative file paths for structured payload emission. Runtime is O(F) plus project file-discovery cost. Side effects are limited to filesystem reads and git subprocesses delegated through `collectSourceFiles`.
@@ -240,10 +245,12 @@ function collectProjectStaticCheckSelection(
240
245
  * @return {string} Newline-delimited execution diagnostics.
241
246
  */
242
247
  function buildTokenToolExecutionStderr(payload: TokenToolPayload): string {
243
- const skippedLines = payload.guidance.source_observations.skipped_inputs
244
- .map((entry) => `skipped: ${entry.canonical_path}: ${entry.reason}`);
245
- const errorLines = payload.guidance.source_observations.error_inputs
246
- .map((entry) => `error: ${entry.canonical_path}: ${entry.reason}`);
248
+ const skippedLines = payload.files
249
+ .filter((entry) => entry.status === "skipped" && entry.error_message)
250
+ .map((entry) => `skipped: ${entry.canonical_path}: ${entry.error_message!}`);
251
+ const errorLines = payload.files
252
+ .filter((entry) => entry.status === "error" && entry.error_message)
253
+ .map((entry) => `error: ${entry.canonical_path}: ${entry.error_message!}`);
247
254
  return [...skippedLines, ...errorLines].join("\n");
248
255
  }
249
256
 
@@ -261,10 +268,8 @@ function buildTokenToolExecuteResult(
261
268
  details: TokenToolPayload & { execution: { code: number; stderr: string } };
262
269
  } {
263
270
  const details = {
264
- request: payload.request,
265
271
  summary: payload.summary,
266
272
  files: payload.files,
267
- guidance: payload.guidance,
268
273
  execution: {
269
274
  code: payload.summary.counted_file_count > 0 ? 0 : 1,
270
275
  stderr: buildTokenToolExecutionStderr(payload),
@@ -287,7 +292,6 @@ function buildReferenceToolExecuteResult(
287
292
  details: ReferenceToolPayload & { execution: { code: number; stderr: string } };
288
293
  } {
289
294
  const details = {
290
- request: payload.request,
291
295
  summary: payload.summary,
292
296
  repository: payload.repository,
293
297
  files: payload.files,
@@ -313,7 +317,6 @@ function buildCompressionToolExecuteResult(
313
317
  details: CompressToolPayload & { execution: { code: number; stderr: string } };
314
318
  } {
315
319
  const details = {
316
- request: payload.request,
317
320
  summary: payload.summary,
318
321
  repository: payload.repository,
319
322
  files: payload.files,
@@ -400,7 +403,7 @@ function buildFindToolSchemaDescription(scope: FindToolScope): string {
400
403
  const inputContract = scope === "explicit-files"
401
404
  ? "Input contract: tag + pattern + files[] + optional enableLineNumbers."
402
405
  : "Input contract: tag + pattern + optional enableLineNumbers. Scope is the configured src-dir list resolved from the current project configuration.";
403
- return `${inputContract} Output contract: JSON object with request, summary, repository, files, and execution. Repository exposes file_canonical_paths and supported_tags_by_language. File entries expose path facts, supported_tags, structured statuses, file_doxygen, and match records with typed line ranges, stripped code lines, and structured Doxygen fields. Regex matches construct names only.`;
406
+ return `${inputContract} Output contract: JSON object with summary, repository, files, and execution. Static supported-tag matrices are documented in tool registration metadata instead of runtime responses. File entries expose structured statuses, file_doxygen, and match records with typed line ranges, stripped code lines, and structured Doxygen fields. Regex matches construct names only.`;
404
407
  }
405
408
 
406
409
  /**
@@ -414,13 +417,13 @@ function buildFindToolPromptGuidelines(scope: FindToolScope): string[] {
414
417
  ? "Scope: explicit source files selected by files[]; caller order is preserved; each item may be project-relative or absolute."
415
418
  : "Scope: resolve src-dir from the current project configuration and scan the configured source surface from the current working directory.";
416
419
  const outputLine = scope === "explicit-files"
417
- ? "Output contract: request + summary + repository + files + execution. Repository exposes requested file scope and supported_tags_by_language; file entries expose status, supported_tags, line ranges, file_doxygen, and matches; match entries expose symbol_kind, signature_text, line ranges, code_lines, stripped_source_text, and structured Doxygen fields."
418
- : "Output contract: request + summary + repository + files + execution. Repository exposes source_directory_paths, file_canonical_paths, and supported_tags_by_language; file entries expose status, supported_tags, line ranges, file_doxygen, and matches; match entries expose symbol_kind, signature_text, line ranges, code_lines, stripped_source_text, and structured Doxygen fields.";
420
+ ? "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."
421
+ : "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.";
419
422
  return [
420
423
  scopeLine,
421
424
  outputLine,
422
425
  "Regex rule: pattern is applied to construct names only with JavaScript RegExp search semantics; it never matches construct bodies; use ^...$ for exact-name matching.",
423
- "Tag rule: tag is pipe-separated and case-insensitive; unsupported tags are ignored; if no valid tag remains, request.tag_filter_status becomes invalid.",
426
+ "Tag rule: tag is pipe-separated and case-insensitive; unsupported tags are ignored; if no valid tag remains, the response search_status becomes invalid_tag_filter.",
424
427
  "Line-number behavior: enableLineNumbers changes only display_text and stripped_source_text rendering; numeric source_line_number and line_range facts remain dedicated fields.",
425
428
  "Failure contract: invalid tag filters, invalid regex patterns, unsupported extensions, unsupported tag-language combinations, no-match files, and analysis failures are surfaced as structured statuses plus optional execution.stderr diagnostics.",
426
429
  ...buildFindToolSupportedTagGuidelines(),
@@ -442,7 +445,6 @@ function buildFindToolExecuteResult(
442
445
  } {
443
446
  const stderr = buildFindToolExecutionStderr(payload);
444
447
  const details = {
445
- request: payload.request,
446
448
  summary: payload.summary,
447
449
  repository: payload.repository,
448
450
  files: payload.files,
@@ -537,18 +539,20 @@ function applyConfiguredPiUsereqTools(pi: ExtensionAPI, config: UseReqConfig): v
537
539
  * @details Applies session-start-specific resource validation, project-config
538
540
  * refresh, and startup-tool enablement before forwarding the originating hook
539
541
  * name and payload into the shared `updateExtensionStatus(...)` pipeline.
540
- * On `agent_end`, also dispatches configured pi-notify beep and sound effects.
541
- * Runtime is dominated by configuration loading during `session_start`; all
542
- * other hooks are O(1). Side effects include resource checks, active-tool
543
- * mutation, status updates, live-ticker disposal on shutdown, stdout writes,
544
- * and optional child-process spawning.
542
+ * On `agent_end`, also dispatches configured pi-notify beep, sound, and
543
+ * prompt-specific Pushover effects when the current run originates from a
544
+ * bundled prompt command. Runtime is dominated by configuration loading during
545
+ * `session_start`; all other hooks are O(1). Side effects include resource
546
+ * checks, active-tool mutation, status updates, live-ticker disposal on
547
+ * shutdown, stdout writes, optional child-process spawning, and outbound
548
+ * HTTPS requests.
545
549
  * @param[in] pi {ExtensionAPI} Active extension API instance.
546
550
  * @param[in,out] statusController {PiUsereqStatusController} Mutable status controller.
547
551
  * @param[in] hookName {PiUsereqStatusHookName} Intercepted hook name.
548
552
  * @param[in] event {unknown} Hook payload forwarded by pi.
549
553
  * @param[in] ctx {ExtensionContext} Active extension context.
550
554
  * @return {Promise<void>} Promise resolved when hook processing completes.
551
- * @satisfies REQ-117, REQ-118, REQ-119, REQ-129, REQ-130, REQ-131, REQ-132, REQ-133
555
+ * @satisfies REQ-117, REQ-118, REQ-119, REQ-129, REQ-130, REQ-131, REQ-132, REQ-133, REQ-166, REQ-167, REQ-168, REQ-169, REQ-172
552
556
  */
553
557
  async function handleExtensionStatusEvent(
554
558
  pi: ExtensionAPI,
@@ -564,8 +568,24 @@ async function handleExtensionStatusEvent(
564
568
  setPiUsereqStatusConfig(statusController, config);
565
569
  }
566
570
  updateExtensionStatus(statusController, hookName, event, ctx);
567
- if (hookName === "agent_end" && statusController.config) {
568
- runPiNotifyEffects(statusController.config, event as { messages: AgentEndEvent["messages"] });
571
+ if (hookName === "agent_end") {
572
+ if (statusController.config) {
573
+ const pushoverRequest: PiNotifyPushoverRequest | undefined = statusController.state.activePromptRequest
574
+ && statusController.state.lastRunDurationMs !== undefined
575
+ ? {
576
+ promptName: statusController.state.activePromptRequest.promptName,
577
+ promptArgs: statusController.state.activePromptRequest.promptArgs,
578
+ basePath: path.resolve(ctx.cwd),
579
+ completionTimeMs: statusController.state.lastRunDurationMs,
580
+ }
581
+ : undefined;
582
+ runPiNotifyEffects(
583
+ statusController.config,
584
+ event as { messages: AgentEndEvent["messages"] },
585
+ pushoverRequest,
586
+ );
587
+ }
588
+ statusController.state.activePromptRequest = undefined;
569
589
  }
570
590
  if (hookName === "session_shutdown") {
571
591
  disposePiUsereqStatusController(statusController);
@@ -671,12 +691,162 @@ function togglePiNotifyBeepFlag(config: UseReqConfig, key: PiNotifyBeepConfigKey
671
691
  return config[key];
672
692
  }
673
693
 
694
+ /**
695
+ * @brief Formats one persisted Pushover priority for menu display.
696
+ * @details Maps the canonical `0|1` priority domain to deterministic menu text reused by the Pushover configuration UI. Runtime is O(1). No external state is mutated.
697
+ * @param[in] priority {PiNotifyPushoverPriority} Persisted Pushover priority.
698
+ * @return {string} Menu-display label.
699
+ * @satisfies REQ-165
700
+ */
701
+ function formatPiNotifyPushoverPriority(priority: PiNotifyPushoverPriority): string {
702
+ return priority === 1 ? "1=High Priority" : "0=Normal";
703
+ }
704
+
705
+ /**
706
+ * @brief Builds the shared settings-menu choices for Pushover configuration.
707
+ * @details Serializes the Pushover global-disable flag, successful-completion enable flag, credential strings, and priority value into right-valued menu rows consumed by the shared settings-menu renderer. Runtime is O(1). No external state is mutated.
708
+ * @param[in] config {UseReqConfig} Effective project configuration.
709
+ * @return {PiUsereqSettingsMenuChoice[]} Ordered Pushover-menu choice vector.
710
+ * @satisfies REQ-163, REQ-165, REQ-166, REQ-172
711
+ */
712
+ function buildPiNotifyPushoverMenuChoices(config: UseReqConfig): PiUsereqSettingsMenuChoice[] {
713
+ return [
714
+ {
715
+ id: "pushover-global-disable",
716
+ label: "Global disable",
717
+ value: config["notify-pushover-global-disable"] ? "on" : "off",
718
+ description: "Suppress all Pushover delivery without changing the successful-prompt enable flag.",
719
+ },
720
+ {
721
+ id: "pushover-on-success",
722
+ label: "Notify on success",
723
+ value: formatPiNotifyPushoverStatus(config),
724
+ description: "Toggle Pushover delivery after successful prompt completion only.",
725
+ },
726
+ {
727
+ id: "pushover-user-key",
728
+ label: "User Key/Delivery Group Key",
729
+ value: config["notify-pushover-user-key"] || "(empty)",
730
+ description: "Edit the Pushover user key or delivery group key sent with successful prompt notifications.",
731
+ },
732
+ {
733
+ id: "pushover-api-token",
734
+ label: "Token/API Token Key",
735
+ value: config["notify-pushover-api-token"] || "(empty)",
736
+ description: "Edit the Pushover application token used for successful prompt notifications.",
737
+ },
738
+ {
739
+ id: "pushover-priority",
740
+ label: "Priority",
741
+ value: formatPiNotifyPushoverPriority(config["notify-pushover-priority"]),
742
+ description: "Select whether successful prompt notifications use normal or high Pushover priority.",
743
+ },
744
+ {
745
+ id: "back",
746
+ label: "Back",
747
+ value: "",
748
+ description: "Return to the notifications menu.",
749
+ },
750
+ ];
751
+ }
752
+
753
+ /**
754
+ * @brief Opens the shared settings-menu selector for Pushover priority.
755
+ * @details Reuses the pi-usereq settings-menu renderer so Pushover priority selection remains stylistically aligned with the existing notification menus and returns the chosen priority or `undefined` on cancel. Runtime depends on user interaction count. Side effects are limited to transient custom-UI rendering.
756
+ * @param[in] ctx {ExtensionCommandContext} Active command context.
757
+ * @param[in] currentPriority {PiNotifyPushoverPriority} Persisted priority value.
758
+ * @return {Promise<PiNotifyPushoverPriority | undefined>} Selected priority or `undefined` when cancelled.
759
+ * @satisfies REQ-165
760
+ */
761
+ async function selectPiNotifyPushoverPriority(
762
+ ctx: ExtensionCommandContext,
763
+ currentPriority: PiNotifyPushoverPriority,
764
+ ): Promise<PiNotifyPushoverPriority | undefined> {
765
+ const choice = await showPiUsereqSettingsMenu(ctx, "Pushover priority", [
766
+ {
767
+ id: "0",
768
+ label: "0=Normal",
769
+ value: currentPriority === 0 ? "selected" : "",
770
+ description: "Send successful prompt notifications with normal Pushover priority.",
771
+ },
772
+ {
773
+ id: "1",
774
+ label: "1=High Priority",
775
+ value: currentPriority === 1 ? "selected" : "",
776
+ description: "Send successful prompt notifications with high Pushover priority.",
777
+ },
778
+ ]);
779
+ if (!choice) {
780
+ return undefined;
781
+ }
782
+ return normalizePiNotifyPushoverPriority(choice);
783
+ }
784
+
785
+ /**
786
+ * @brief Runs the interactive Pushover-configuration menu.
787
+ * @details Exposes the Pushover global-disable flag, successful-completion enable flag, user key, API token, and priority selector through the shared settings-menu renderer. Runtime depends on user interaction count. Side effects include UI updates and config mutation.
788
+ * @param[in] ctx {ExtensionCommandContext} Active command context.
789
+ * @param[in,out] config {UseReqConfig} Mutable configuration object.
790
+ * @return {Promise<void>} Promise resolved when the menu closes.
791
+ * @satisfies REQ-163, REQ-165, REQ-166, REQ-172
792
+ */
793
+ async function configurePiNotifyPushoverMenu(
794
+ ctx: ExtensionCommandContext,
795
+ config: UseReqConfig,
796
+ ): Promise<void> {
797
+ while (true) {
798
+ const choice = await showPiUsereqSettingsMenu(ctx, "Pushover notifications", buildPiNotifyPushoverMenuChoices(config));
799
+ if (!choice || choice === "back") {
800
+ return;
801
+ }
802
+ if (choice === "pushover-global-disable") {
803
+ config["notify-pushover-global-disable"] = !config["notify-pushover-global-disable"];
804
+ ctx.ui.notify(`Pushover global disable ${config["notify-pushover-global-disable"] ? "enabled" : "disabled"}`, "info");
805
+ continue;
806
+ }
807
+ if (choice === "pushover-on-success") {
808
+ config["notify-pushover-on-success"] = !config["notify-pushover-on-success"];
809
+ ctx.ui.notify(`Pushover on success ${config["notify-pushover-on-success"] ? "enabled" : "disabled"}`, "info");
810
+ continue;
811
+ }
812
+ if (choice === "pushover-user-key") {
813
+ const value = await ctx.ui.input(
814
+ "User Key/Delivery Group Key",
815
+ config["notify-pushover-user-key"] || "gzfjjvp1xxmhibqwzh9m7i1zwvf83j",
816
+ );
817
+ if (value !== undefined) {
818
+ config["notify-pushover-user-key"] = normalizePiNotifyPushoverCredential(value);
819
+ ctx.ui.notify("Updated Pushover user key", "info");
820
+ }
821
+ continue;
822
+ }
823
+ if (choice === "pushover-api-token") {
824
+ const value = await ctx.ui.input(
825
+ "Token/API Token Key",
826
+ config["notify-pushover-api-token"] || "ah6bf5u2sj63mcvou6qamiabeoubbe",
827
+ );
828
+ if (value !== undefined) {
829
+ config["notify-pushover-api-token"] = normalizePiNotifyPushoverCredential(value);
830
+ ctx.ui.notify("Updated Pushover API token", "info");
831
+ }
832
+ continue;
833
+ }
834
+ if (choice === "pushover-priority") {
835
+ const nextPriority = await selectPiNotifyPushoverPriority(ctx, config["notify-pushover-priority"]);
836
+ if (nextPriority !== undefined) {
837
+ config["notify-pushover-priority"] = nextPriority;
838
+ ctx.ui.notify(`Pushover priority set to ${formatPiNotifyPushoverPriority(nextPriority)}`, "info");
839
+ }
840
+ }
841
+ }
842
+ }
843
+
674
844
  /**
675
845
  * @brief Builds the shared settings-menu choices for notification configuration.
676
- * @details Serializes the current beep flags, selected notify command, hotkey bind, and per-level notify commands into right-valued menu rows consumed by the shared settings-menu renderer. Runtime is O(1) plus command-length formatting. No external state is mutated.
846
+ * @details Serializes the current beep flags, selected notify command, hotkey bind, per-level notify commands, and the Pushover submenu entry into right-valued menu rows consumed by the shared settings-menu renderer. Runtime is O(1) plus command-length formatting. No external state is mutated.
677
847
  * @param[in] config {UseReqConfig} Effective project configuration.
678
848
  * @return {PiUsereqSettingsMenuChoice[]} Ordered notification-menu choice vector.
679
- * @satisfies REQ-137, REQ-149, REQ-150, REQ-151, REQ-152
849
+ * @satisfies REQ-137, REQ-149, REQ-150, REQ-151, REQ-152, REQ-163, REQ-164, REQ-165, REQ-166, REQ-172
680
850
  */
681
851
  function buildPiNotifyMenuChoices(config: UseReqConfig): PiUsereqSettingsMenuChoice[] {
682
852
  return [
@@ -728,6 +898,12 @@ function buildPiNotifyMenuChoices(config: UseReqConfig): PiUsereqSettingsMenuCho
728
898
  value: config.PI_NOTIFY_SOUND_HIGH_CMD,
729
899
  description: "Edit the shell command used when the selected notify command is `high`.",
730
900
  },
901
+ {
902
+ id: "pushover-notifications",
903
+ label: "Pushover notifications",
904
+ value: formatPiNotifyPushoverStatus(config),
905
+ description: "Open the Pushover submenu for successful-completion delivery, credentials, priority, and global disable.",
906
+ },
731
907
  {
732
908
  id: "back",
733
909
  label: "Back",
@@ -780,11 +956,11 @@ async function selectPiNotifySoundLevel(
780
956
 
781
957
  /**
782
958
  * @brief Runs the interactive notification-configuration menu.
783
- * @details Exposes prompt-end beep toggles, selected notify-command selection, hotkey-bind editing, and per-level notify-command editors through the shared settings-menu renderer. Runtime depends on user interaction count. Side effects include UI updates and config mutation.
959
+ * @details Exposes prompt-end beep toggles, selected notify-command selection, hotkey-bind editing, per-level notify-command editors, and the nested Pushover submenu through the shared settings-menu renderer. Runtime depends on user interaction count. Side effects include UI updates and config mutation.
784
960
  * @param[in] ctx {ExtensionCommandContext} Active command context.
785
961
  * @param[in,out] config {UseReqConfig} Mutable configuration object.
786
962
  * @return {Promise<boolean>} `true` when the sound-toggle shortcut changed.
787
- * @satisfies REQ-129, REQ-131, REQ-133, REQ-134, REQ-137, REQ-149, REQ-150, REQ-151, REQ-152, REQ-153, REQ-154
963
+ * @satisfies REQ-129, REQ-131, REQ-133, REQ-134, REQ-137, REQ-149, REQ-150, REQ-151, REQ-152, REQ-153, REQ-154, REQ-163, REQ-164, REQ-165, REQ-166, REQ-172
788
964
  */
789
965
  async function configurePiNotifyMenu(
790
966
  ctx: ExtensionCommandContext,
@@ -849,6 +1025,10 @@ async function configurePiNotifyMenu(
849
1025
  config.PI_NOTIFY_SOUND_HIGH_CMD = value.trim();
850
1026
  ctx.ui.notify("Updated notify command (high vol.)", "info");
851
1027
  }
1028
+ continue;
1029
+ }
1030
+ if (choice === "pushover-notifications") {
1031
+ await configurePiNotifyPushoverMenu(ctx, config);
852
1032
  }
853
1033
  }
854
1034
  }
@@ -890,12 +1070,16 @@ function registerPiNotifyShortcut(
890
1070
 
891
1071
  /**
892
1072
  * @brief Registers bundled prompt commands with the extension.
893
- * @details Creates one `req-<prompt>` command per bundled prompt name. Each handler ensures resources exist, renders the prompt, and sends it into the current active session. Runtime is O(p) for registration; handler cost depends on prompt rendering plus prompt dispatch. Side effects include command registration and user-message delivery during execution.
1073
+ * @details Creates one `req-<prompt>` command per bundled prompt name. Each handler ensures resources exist, records the prompt metadata needed for successful completion notifications, renders the prompt, and sends it into the current active session. Runtime is O(p) for registration; handler cost depends on prompt rendering plus prompt dispatch. Side effects include command registration, status-controller mutation, and user-message delivery during execution.
894
1074
  * @param[in] pi {ExtensionAPI} Active extension API instance.
1075
+ * @param[in,out] statusController {PiUsereqStatusController} Mutable status controller.
895
1076
  * @return {void} No return value.
896
- * @satisfies REQ-004, REQ-067, REQ-068
1077
+ * @satisfies REQ-004, REQ-067, REQ-068, REQ-169
897
1078
  */
898
- function registerPromptCommands(pi: ExtensionAPI): void {
1079
+ function registerPromptCommands(
1080
+ pi: ExtensionAPI,
1081
+ statusController: PiUsereqStatusController,
1082
+ ): void {
899
1083
  PROMPT_NAMES.forEach((promptName) => {
900
1084
  pi.registerCommand(`req-${promptName}`, {
901
1085
  description: `Run pi-usereq prompt ${promptName}`,
@@ -903,6 +1087,10 @@ function registerPromptCommands(pi: ExtensionAPI): void {
903
1087
  ensureBundledResourcesAccessible();
904
1088
  const projectBase = getProjectBase(ctx.cwd);
905
1089
  const config = loadProjectConfig(ctx.cwd);
1090
+ statusController.state.pendingPromptRequest = {
1091
+ promptName,
1092
+ promptArgs: args,
1093
+ };
906
1094
  const content = renderPrompt(promptName, args, projectBase, config);
907
1095
  await deliverPromptCommand(pi, content);
908
1096
  },
@@ -922,17 +1110,17 @@ function registerAgentTools(pi: ExtensionAPI): void {
922
1110
  const gitPathSchema = Type.Object(
923
1111
  {},
924
1112
  {
925
- description: "Input contract: no params. Output contract: JSON object with request, result, and execution. Result exposes path_key, path_value, and path_present for the cwd-derived runtime git root.",
1113
+ 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.",
926
1114
  },
927
1115
  );
928
1116
  pi.registerTool({
929
1117
  name: "git-path",
930
1118
  label: "git-path",
931
- description: "Scope: current runtime path. Return a JSON-first payload with request, result, and execution sections. Result exposes the resolved `git-path` value through direct-access fields instead of text-only output.",
1119
+ 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.",
932
1120
  promptSnippet: "Return the structured runtime git-root payload for the current project.",
933
1121
  promptGuidelines: [
934
1122
  "Input contract: no params. Scope is the cwd-derived runtime path context.",
935
- "Output contract: request + result + execution. Result exposes path_key, path_value, and path_present.",
1123
+ "Output contract: result + execution. Result exposes path_value and path_present.",
936
1124
  "Behavior contract: git-path is derived at runtime from the current working directory and repository ancestry rules.",
937
1125
  "Failure contract: configuration-loading failures surface through execution.code and execution.stderr_lines.",
938
1126
  ],
@@ -941,14 +1129,12 @@ function registerAgentTools(pi: ExtensionAPI): void {
941
1129
  ensureBundledResourcesAccessible();
942
1130
  const projectBase = getProjectBase(process.cwd());
943
1131
  const config = loadProjectConfig(process.cwd());
944
- const runtimePaths = buildSharedRuntimePathFacts(process.cwd(), config);
945
1132
  const result = runGitPath(projectBase, config);
946
1133
  const payload = buildPathQueryToolPayload(
947
1134
  "git-path",
948
1135
  process.cwd(),
949
1136
  projectBase,
950
1137
  result.stdout.trimEnd(),
951
- runtimePaths,
952
1138
  buildToolExecutionSection(result),
953
1139
  );
954
1140
  return buildStructuredToolExecuteResult(payload);
@@ -958,17 +1144,17 @@ function registerAgentTools(pi: ExtensionAPI): void {
958
1144
  const basePathSchema = Type.Object(
959
1145
  {},
960
1146
  {
961
- description: "Input contract: no params. Output contract: JSON object with request, result, and execution. Result exposes path_key, path_value, and path_present for the cwd-derived runtime base path.",
1147
+ 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.",
962
1148
  },
963
1149
  );
964
1150
  pi.registerTool({
965
1151
  name: "get-base-path",
966
1152
  label: "get-base-path",
967
- description: "Scope: current runtime path. Return a JSON-first payload with request, result, and execution sections. Result exposes the resolved `base-path` value through direct-access fields instead of text-only output.",
1153
+ 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.",
968
1154
  promptSnippet: "Return the structured runtime project-base payload.",
969
1155
  promptGuidelines: [
970
1156
  "Input contract: no params. Scope is the cwd-derived runtime path context.",
971
- "Output contract: request + result + execution. Result exposes path_key, path_value, and path_present.",
1157
+ "Output contract: result + execution. Result exposes path_value and path_present.",
972
1158
  "Behavior contract: base-path equals the current working directory used by the extension command or tool.",
973
1159
  "Failure contract: configuration-loading failures surface through execution.code and execution.stderr_lines.",
974
1160
  ],
@@ -976,14 +1162,12 @@ function registerAgentTools(pi: ExtensionAPI): void {
976
1162
  async execute() {
977
1163
  const projectBase = getProjectBase(process.cwd());
978
1164
  const config = loadProjectConfig(process.cwd());
979
- const runtimePaths = buildSharedRuntimePathFacts(process.cwd(), config);
980
1165
  const result = runGetBasePath(projectBase, config);
981
1166
  const payload = buildPathQueryToolPayload(
982
1167
  "get-base-path",
983
1168
  process.cwd(),
984
1169
  projectBase,
985
1170
  result.stdout.trimEnd(),
986
- runtimePaths,
987
1171
  buildToolExecutionSection(result),
988
1172
  );
989
1173
  return buildStructuredToolExecuteResult(payload);
@@ -998,7 +1182,7 @@ function registerAgentTools(pi: ExtensionAPI): void {
998
1182
  ),
999
1183
  },
1000
1184
  {
1001
- description: "Input contract: files[]. Output contract: JSON object with request, summary, repository, files, and execution. File entries expose canonical paths, numeric line ranges, imports, symbols, structured Doxygen fields, standalone comments, and structured status facts. Missing or unsupported inputs become skipped entries. The tool fails when no source file can be analyzed.",
1185
+ description: "Input contract: files[]. Output contract: JSON object with summary, repository, files, and execution. File entries expose canonical paths, numeric line ranges, imports, symbols, structured Doxygen fields, standalone comments, and structured status facts. Missing or unsupported inputs become skipped entries. The tool fails when no source file can be analyzed.",
1002
1186
  },
1003
1187
  );
1004
1188
  const multiFileSchema = Type.Object(
@@ -1009,7 +1193,7 @@ function registerAgentTools(pi: ExtensionAPI): void {
1009
1193
  ),
1010
1194
  },
1011
1195
  {
1012
- description: "Input contract: files[]. Output contract: JSON object with request, summary, files, and execution. File entries expose canonical paths, detected language, configured checker modules, selection status, and error facts.",
1196
+ description: "Input contract: files[]. Output contract: JSON object with summary, files, and execution. File entries expose canonical paths, detected language, configured checker modules, selection status, and error facts.",
1013
1197
  },
1014
1198
  );
1015
1199
  const filesTokensSchema = Type.Object(
@@ -1020,19 +1204,19 @@ function registerAgentTools(pi: ExtensionAPI): void {
1020
1204
  ),
1021
1205
  },
1022
1206
  {
1023
- description: "Input contract: files[]. Output contract: JSON object with request, summary, files, guidance, and execution. File entries expose path identifiers, access facts, line ranges, sizes, token metrics, and optional heading or Doxygen metadata. Missing or non-file inputs become skipped entries. The tool fails when no processable files remain.",
1207
+ description: "Input contract: files[]. Output contract: JSON object with summary, files, and execution. File entries expose direct-access facts, token metrics, and optional heading or Doxygen metadata. Missing or non-file inputs become skipped entries. The tool fails when no processable files remain.",
1024
1208
  },
1025
1209
  );
1026
1210
 
1027
1211
  pi.registerTool({
1028
1212
  name: "files-tokens",
1029
1213
  label: "files-tokens",
1030
- description: "Scope: explicit files. Return an LLM-oriented JSON payload with request, summary, files, guidance, and execution sections. File entries expose direct-access path facts, status, line ranges, sizes, token metrics, and optional heading or Doxygen metadata.",
1214
+ description: "Scope: explicit files. Return a token-optimized JSON payload with summary, files, and execution sections. File entries expose direct-access path facts, status, size metrics, and optional heading or Doxygen metadata.",
1031
1215
  promptSnippet: "Return the structured token-analysis payload for caller-selected files.",
1032
1216
  promptGuidelines: [
1033
1217
  "Scope: explicit files selected by files[]; caller order is preserved; each item may be project-relative or absolute.",
1034
- "Output contract: request + summary + files + guidance + execution. File entries expose canonical paths, absolute paths, existence, file status, line range, line count, byte count, character count, token count, shares, and optional primary-heading or Doxygen file metadata.",
1035
- "Numeric contract: counts, sizes, shares, and line ranges remain in dedicated numeric fields; descriptive text is limited to stable reasons and guidance labels.",
1218
+ "Output contract: summary + files + execution. File entries expose canonical paths, absolute paths, existence, file status, line range, line count, byte count, character count, token count, shares, and optional primary-heading or Doxygen file metadata.",
1219
+ "Numeric contract: counts, sizes, shares, and line ranges remain in dedicated numeric fields; static request metadata and derived guidance are omitted from runtime responses.",
1036
1220
  "Behavior contract: missing or non-file inputs become skipped entries; read failures become error entries; the tool fails only when no processable files remain.",
1037
1221
  ],
1038
1222
  parameters: filesTokensSchema,
@@ -1051,11 +1235,11 @@ function registerAgentTools(pi: ExtensionAPI): void {
1051
1235
  pi.registerTool({
1052
1236
  name: "files-references",
1053
1237
  label: "files-references",
1054
- description: "Scope: explicit source files. Return an LLM-oriented JSON payload with request, summary, repository, files, and execution sections. File entries expose canonical paths, numeric line ranges, imports, symbols, structured Doxygen fields, standalone comments, and structured status facts.",
1238
+ description: "Scope: explicit source files. Return a token-optimized JSON payload with summary, repository, files, and execution sections. File entries expose canonical paths, numeric line ranges, imports, symbols, structured Doxygen fields, standalone comments, and structured status facts.",
1055
1239
  promptSnippet: "Return the structured references payload for caller-selected source files.",
1056
1240
  promptGuidelines: [
1057
1241
  "Scope: explicit source files selected by files[]; caller order is preserved; each item may be project-relative or absolute.",
1058
- "Output contract: request + summary + repository + files + execution. File entries expose canonical paths, absolute paths, file status, line counts, line ranges, imports, symbols, child relationships, standalone comments, and structured Doxygen metadata.",
1242
+ "Output contract: summary + repository + files + execution. File entries expose canonical paths, absolute paths, file status, line counts, line ranges, imports, symbols, child relationships, standalone comments, and structured Doxygen metadata.",
1059
1243
  "Numeric contract: line counts, line ranges, symbol counts, import counts, comment counts, and Doxygen counts remain in dedicated numeric fields; text is limited to residual comment or signature content that cannot be split safely.",
1060
1244
  "Behavior contract: missing inputs, non-file inputs, and unsupported extensions become structured skipped entries; analysis failures become structured error entries; the tool fails only when no source file can be analyzed.",
1061
1245
  ],
@@ -1080,18 +1264,18 @@ function registerAgentTools(pi: ExtensionAPI): void {
1080
1264
  enableLineNumbers: Type.Optional(Type.Boolean({ description: "When true, `compressed_source_text` and `compressed_lines[].display_text` include original source line-number prefixes" })),
1081
1265
  },
1082
1266
  {
1083
- description: "Input contract: files[] plus optional enableLineNumbers. Output contract: JSON object with request, summary, repository, files, and execution. File entries expose path identifiers, source and compressed line metrics, structured compressed lines, symbols, structured Doxygen fields, and stable status facts. Missing, unsupported, or invalid inputs become structured skipped entries. The tool fails when no file is compressed.",
1267
+ description: "Input contract: files[] plus optional enableLineNumbers. Output contract: JSON object with summary, repository, files, and execution. File entries expose path identifiers, source and compressed line metrics, structured compressed lines, symbols, structured Doxygen fields, and stable status facts. Missing, unsupported, or invalid inputs become structured skipped entries. The tool fails when no file is compressed.",
1084
1268
  },
1085
1269
  );
1086
1270
 
1087
1271
  pi.registerTool({
1088
1272
  name: "files-compress",
1089
1273
  label: "files-compress",
1090
- description: "Scope: explicit files. Return an LLM-oriented JSON payload with request, summary, repository, files, and execution sections. File entries expose canonical paths, source and compressed line metrics, structured compressed lines, symbols, structured Doxygen fields, and stable status facts.",
1274
+ description: "Scope: explicit files. Return a token-optimized JSON payload with summary, repository, files, and execution sections. File entries expose canonical paths, source and compressed line metrics, structured compressed lines, symbols, structured Doxygen fields, and stable status facts.",
1091
1275
  promptSnippet: "Return the structured compression payload for caller-selected source files.",
1092
1276
  promptGuidelines: [
1093
1277
  "Scope: explicit source files selected by files[]; caller order is preserved; each item may be project-relative or absolute.",
1094
- "Output contract: request + summary + repository + files + execution. File entries expose canonical paths, absolute paths, line_number_mode, source line counts, source line ranges, compressed line counts, removed line counts, compressed_lines, compressed_source_text, symbols, and file_doxygen.",
1278
+ "Output contract: summary + repository + files + execution. File entries expose canonical paths, absolute paths, line_number_mode, source line counts, source line ranges, compressed line counts, removed line counts, compressed_lines, compressed_source_text, symbols, and file_doxygen.",
1095
1279
  "Line-number behavior: enableLineNumbers changes only rendered display strings; numeric source_line_number facts remain dedicated fields on compressed_lines for direct access.",
1096
1280
  "Behavior contract: missing inputs, non-file inputs, and unsupported extensions become structured skipped entries; compression failures become structured error entries; symbol-analysis failures retain compressed output with symbol_analysis_status=error; the tool fails only when no file is compressed.",
1097
1281
  ],
@@ -1126,7 +1310,7 @@ function registerAgentTools(pi: ExtensionAPI): void {
1126
1310
  pi.registerTool({
1127
1311
  name: "files-find",
1128
1312
  label: "files-find",
1129
- description: "Scope: explicit source files. Return an LLM-oriented JSON payload with request, summary, repository, files, and execution sections. File entries expose structured statuses and match records with typed location, symbol, stripped-code, and Doxygen facts.",
1313
+ description: "Scope: explicit source files. Return a token-optimized JSON payload with summary, repository, files, and execution sections. File entries expose structured statuses and match records with typed location, symbol, stripped-code, and Doxygen facts.",
1130
1314
  promptSnippet: "Return the structured construct-search payload for caller-selected source files.",
1131
1315
  promptGuidelines: buildFindToolPromptGuidelines("explicit-files"),
1132
1316
  parameters: filesFindSchema,
@@ -1147,24 +1331,24 @@ function registerAgentTools(pi: ExtensionAPI): void {
1147
1331
  const referencesSchema = Type.Object(
1148
1332
  {},
1149
1333
  {
1150
- description: "Input contract: no params. Scope is the configured src-dir list resolved from the current project configuration. Output contract: JSON object with request, summary, repository, files, and execution. Repository exposes the structured directory tree; file entries expose canonical paths, numeric line ranges, imports, symbols, structured Doxygen fields, and status facts. The tool fails when no configured source file can be analyzed.",
1334
+ description: "Input contract: no params. Scope is the configured src-dir list resolved from the current project configuration. Output contract: JSON object with summary, repository, files, and execution. Repository exposes the structured directory tree; file entries expose canonical paths, numeric line ranges, imports, symbols, structured Doxygen fields, and status facts. The tool fails when no configured source file can be analyzed.",
1151
1335
  },
1152
1336
  );
1153
1337
  const tokensSchema = Type.Object(
1154
1338
  {},
1155
1339
  {
1156
- description: "Input contract: no params. Scope is the configured docs-dir plus canonical docs REQUIREMENTS.md, WORKFLOW.md, and REFERENCES.md. Output contract: same structured JSON shape as files-tokens, plus docs_dir_path and canonical_doc_names in request. Missing canonical docs become skipped entries. The tool fails when no processable canonical docs remain.",
1340
+ description: "Input contract: no params. Scope is the configured docs-dir plus canonical docs REQUIREMENTS.md, WORKFLOW.md, and REFERENCES.md. Output contract: same token-optimized JSON shape as files-tokens. Missing canonical docs become skipped entries. The tool fails when no processable canonical docs remain.",
1157
1341
  },
1158
1342
  );
1159
1343
 
1160
1344
  pi.registerTool({
1161
1345
  name: "references",
1162
1346
  label: "references",
1163
- description: "Scope: configured project source directories. Return an LLM-oriented JSON payload with request, summary, repository, files, and execution sections. The repository section exposes the structured directory tree; file entries expose canonical paths, numeric line ranges, imports, symbols, structured Doxygen fields, standalone comments, and status facts.",
1347
+ description: "Scope: configured project source directories. Return a token-optimized JSON payload with summary, repository, files, and execution sections. The repository section exposes the structured directory tree; file entries expose canonical paths, numeric line ranges, imports, symbols, structured Doxygen fields, standalone comments, and status facts.",
1164
1348
  promptSnippet: "Return the structured project references payload from the configured source directories.",
1165
1349
  promptGuidelines: [
1166
1350
  "Scope: no params; resolve src-dir from the current project configuration and scan the configured source surface from the current working directory.",
1167
- "Output contract: request + summary + repository + files + execution. Repository exposes source_directory_paths, file_canonical_paths, and directory_tree; file entries expose canonical paths, line counts, line ranges, imports, symbols, hierarchy, standalone comments, and structured Doxygen metadata.",
1351
+ "Output contract: summary + repository + files + execution. Repository exposes source_directory_paths, file_canonical_paths, and directory_tree; file entries expose canonical paths, line counts, line ranges, imports, symbols, hierarchy, standalone comments, and structured Doxygen metadata.",
1168
1352
  "Configuration contract: output changes with cwd-derived project config, src-dir values, and repository source discovery; the tool does not accept explicit file overrides.",
1169
1353
  "Behavior contract: configured source files are analyzed in deterministic order, analysis failures become structured error entries, and the tool fails when no configured source file can be analyzed.",
1170
1354
  ],
@@ -1188,18 +1372,18 @@ function registerAgentTools(pi: ExtensionAPI): void {
1188
1372
  enableLineNumbers: Type.Optional(Type.Boolean({ description: "When true, `compressed_source_text` and `compressed_lines[].display_text` include original source line-number prefixes" })),
1189
1373
  },
1190
1374
  {
1191
- description: "Input contract: optional enableLineNumbers boolean. Scope is the configured src-dir list resolved from the current project configuration. Output contract: JSON object with request, summary, repository, files, and execution. File entries expose path identifiers, source and compressed line metrics, structured compressed lines, symbols, structured Doxygen fields, and stable status facts. The tool fails when no configured source file is compressed.",
1375
+ description: "Input contract: optional enableLineNumbers boolean. Scope is the configured src-dir list resolved from the current project configuration. Output contract: JSON object with summary, repository, files, and execution. File entries expose path identifiers, source and compressed line metrics, structured compressed lines, symbols, structured Doxygen fields, and stable status facts. The tool fails when no configured source file is compressed.",
1192
1376
  },
1193
1377
  );
1194
1378
 
1195
1379
  pi.registerTool({
1196
1380
  name: "compress",
1197
1381
  label: "compress",
1198
- description: "Scope: configured project source directories. Return an LLM-oriented JSON payload with request, summary, repository, files, and execution sections. File entries expose canonical paths, source and compressed line metrics, structured compressed lines, symbols, structured Doxygen fields, and stable status facts.",
1382
+ description: "Scope: configured project source directories. Return a token-optimized JSON payload with summary, repository, files, and execution sections. File entries expose canonical paths, source and compressed line metrics, structured compressed lines, symbols, structured Doxygen fields, and stable status facts.",
1199
1383
  promptSnippet: "Return the structured project compression payload from the configured source directories.",
1200
1384
  promptGuidelines: [
1201
1385
  "Scope: resolve src-dir from the current project configuration and scan the configured source surface from the current working directory.",
1202
- "Output contract: request + summary + repository + files + execution. Repository exposes source_directory_paths and file_canonical_paths; file entries expose line_number_mode, source line counts, source line ranges, compressed line counts, removed line counts, compressed_lines, compressed_source_text, symbols, and file_doxygen.",
1386
+ "Output contract: summary + repository + files + execution. Repository exposes source_directory_paths and file_canonical_paths; file entries expose line_number_mode, source line counts, source line ranges, compressed line counts, removed line counts, compressed_lines, compressed_source_text, symbols, and file_doxygen.",
1203
1387
  "Configuration contract: output changes with cwd-derived project config, src-dir values, and repository source discovery; the tool does not accept explicit file overrides.",
1204
1388
  "Behavior contract: configured source files are processed in deterministic order, compression failures become structured error entries, symbol-analysis failures retain compressed output with symbol_analysis_status=error, and the tool fails only when no configured source file is compressed.",
1205
1389
  ],
@@ -1234,7 +1418,7 @@ function registerAgentTools(pi: ExtensionAPI): void {
1234
1418
  pi.registerTool({
1235
1419
  name: "find",
1236
1420
  label: "find",
1237
- description: "Scope: configured project source directories. Return an LLM-oriented JSON payload with request, summary, repository, files, and execution sections. File entries expose structured statuses and match records with typed location, symbol, stripped-code, and Doxygen facts.",
1421
+ description: "Scope: configured project source directories. Return a token-optimized JSON payload with summary, repository, files, and execution sections. File entries expose structured statuses and match records with typed location, symbol, stripped-code, and Doxygen facts.",
1238
1422
  promptSnippet: "Return the structured construct-search payload from the configured source directories.",
1239
1423
  promptGuidelines: buildFindToolPromptGuidelines("configured-source-directories"),
1240
1424
  parameters: findSchema,
@@ -1259,12 +1443,12 @@ function registerAgentTools(pi: ExtensionAPI): void {
1259
1443
  pi.registerTool({
1260
1444
  name: "tokens",
1261
1445
  label: "tokens",
1262
- description: "Scope: canonical docs from the configured docs-dir. Return the same LLM-oriented JSON contract as files-tokens, plus canonical-doc selection metadata in request for REQUIREMENTS.md, WORKFLOW.md, and REFERENCES.md.",
1446
+ description: "Scope: canonical docs from the configured docs-dir. Return the same token-optimized JSON contract as files-tokens, omitting canonical-doc request echoes from runtime responses.",
1263
1447
  promptSnippet: "Return the structured token-analysis payload for canonical documentation files.",
1264
1448
  promptGuidelines: [
1265
1449
  "Scope: no params; resolve docs-dir from project config; target canonical docs REQUIREMENTS.md, WORKFLOW.md, and REFERENCES.md.",
1266
- "Output contract: request + summary + files + guidance + execution. Request includes docs_dir_path and canonical_doc_names; file entries expose direct-access path facts, line ranges, sizes, token metrics, and optional metadata.",
1267
- "Numeric contract: counts, sizes, shares, and line ranges remain in dedicated numeric fields; guidance separates source observations, derived recommendations, and actionable next-step hints.",
1450
+ "Output contract: summary + files + execution. Static docs-dir and canonical-doc selection facts remain documented in registration metadata; file entries expose direct-access path facts, line ranges, sizes, token metrics, and optional metadata.",
1451
+ "Numeric contract: counts, sizes, shares, and line ranges remain in dedicated numeric fields; derived guidance is omitted from runtime responses to reduce token cost.",
1268
1452
  "Behavior contract: missing canonical docs become skipped entries; read failures become error entries; the tool fails only when no processable canonical docs remain.",
1269
1453
  ],
1270
1454
  parameters: tokensSchema,
@@ -1289,11 +1473,11 @@ function registerAgentTools(pi: ExtensionAPI): void {
1289
1473
  pi.registerTool({
1290
1474
  name: "files-static-check",
1291
1475
  label: "files-static-check",
1292
- description: "Scope: explicit files. Return a JSON-first payload with request, summary, files, and execution sections. File entries expose canonical paths, detected language, configured checker modules, selection status, and stable error facts.",
1476
+ description: "Scope: explicit files. Return a token-optimized JSON payload with summary, files, and execution sections. File entries expose canonical paths, detected language, configured checker modules, selection status, and stable error facts.",
1293
1477
  promptSnippet: "Return the structured explicit-file static-check payload for the current project configuration.",
1294
1478
  promptGuidelines: [
1295
1479
  "Input contract: files[]. Scope is explicit caller-selected files resolved from the current working directory.",
1296
- "Output contract: request + summary + files + execution. File entries expose canonical_path, language_name, configured_checker_modules, status, and error_message.",
1480
+ "Output contract: summary + files + execution. File entries expose canonical_path, language_name, configured_checker_modules, status, and error_message.",
1297
1481
  "Configuration contract: checker selection is derived from the cwd-resolved static-check configuration and file extensions only.",
1298
1482
  "Failure contract: execution.code mirrors aggregated checker failures; execution.stdout_lines and execution.stderr_lines preserve residual checker diagnostics.",
1299
1483
  ],
@@ -1301,7 +1485,6 @@ function registerAgentTools(pi: ExtensionAPI): void {
1301
1485
  async execute(_toolCallId, params) {
1302
1486
  const projectBase = getProjectBase(process.cwd());
1303
1487
  const config = loadProjectConfig(process.cwd());
1304
- const runtimePaths = buildSharedRuntimePathFacts(process.cwd(), config);
1305
1488
  const staticCheckConfig = config["static-check"] ?? {};
1306
1489
  const result = runFilesStaticCheck(params.files, projectBase, config);
1307
1490
  const payload = buildStaticCheckToolPayload(
@@ -1312,7 +1495,6 @@ function registerAgentTools(pi: ExtensionAPI): void {
1312
1495
  [],
1313
1496
  [],
1314
1497
  staticCheckConfig,
1315
- runtimePaths,
1316
1498
  buildToolExecutionSection(result),
1317
1499
  );
1318
1500
  return buildStructuredToolExecuteResult(payload);
@@ -1322,17 +1504,17 @@ function registerAgentTools(pi: ExtensionAPI): void {
1322
1504
  const staticCheckSchema = Type.Object(
1323
1505
  {},
1324
1506
  {
1325
- description: "Input contract: no params. Scope is the configured src-dir plus tests-dir selection after fixture exclusion. Output contract: JSON object with request, summary, files, and execution.",
1507
+ description: "Input contract: no params. Scope is the configured src-dir plus tests-dir selection after fixture exclusion. Output contract: JSON object with summary, files, and execution.",
1326
1508
  },
1327
1509
  );
1328
1510
  pi.registerTool({
1329
1511
  name: "static-check",
1330
1512
  label: "static-check",
1331
- description: "Scope: configured source and test directories. Return a JSON-first payload with request, summary, files, and execution sections. File entries expose selected-path facts, checker coverage, selection status, and residual diagnostics metadata.",
1513
+ description: "Scope: configured source and test directories. Return a token-optimized JSON payload with summary, files, and execution sections. File entries expose selected-path facts, checker coverage, selection status, and residual diagnostics metadata.",
1332
1514
  promptSnippet: "Return the structured project static-check payload for the current configuration.",
1333
1515
  promptGuidelines: [
1334
1516
  "Input contract: no params. Scope is src-dir plus tests-dir from the cwd-derived project configuration.",
1335
- "Output contract: request + summary + files + execution. Request exposes selection_directory_paths and excluded_directory_paths; file entries expose configured_checker_modules and status.",
1517
+ "Output contract: summary + files + execution. Selection-directory rules remain documented in registration metadata; file entries expose configured_checker_modules and status.",
1336
1518
  "Selection contract: tests/fixtures and <tests-dir>/fixtures are excluded before checker dispatch.",
1337
1519
  "Failure contract: execution.code mirrors aggregated checker failures or selection failures; execution.stderr_lines preserve residual diagnostics.",
1338
1520
  ],
@@ -1340,7 +1522,6 @@ function registerAgentTools(pi: ExtensionAPI): void {
1340
1522
  async execute() {
1341
1523
  const projectBase = getProjectBase(process.cwd());
1342
1524
  const config = loadProjectConfig(process.cwd());
1343
- const runtimePaths = buildSharedRuntimePathFacts(process.cwd(), config);
1344
1525
  const staticCheckConfig = config["static-check"] ?? {};
1345
1526
  const selectionDirectoryPaths = [...config["src-dir"], config["tests-dir"]];
1346
1527
  const testsDirRel = makeRelativeIfContainsProject(config["tests-dir"], projectBase)
@@ -1368,7 +1549,6 @@ function registerAgentTools(pi: ExtensionAPI): void {
1368
1549
  selectionDirectoryPaths,
1369
1550
  excludedDirectoryPaths,
1370
1551
  staticCheckConfig,
1371
- runtimePaths,
1372
1552
  execution,
1373
1553
  );
1374
1554
  return buildStructuredToolExecuteResult(payload);
@@ -1378,17 +1558,17 @@ function registerAgentTools(pi: ExtensionAPI): void {
1378
1558
  const gitCheckSchema = Type.Object(
1379
1559
  {},
1380
1560
  {
1381
- description: "Input contract: no params. Output contract: JSON object with request, result, and execution. Result exposes git-root presence plus clean-versus-error repository status fields.",
1561
+ description: "Input contract: no params. Output contract: JSON object with result and execution. Result exposes git-path presence plus aggregate repository status fields.",
1382
1562
  },
1383
1563
  );
1384
1564
  pi.registerTool({
1385
1565
  name: "git-check",
1386
1566
  label: "git-check",
1387
- description: "Scope: current runtime path. Return a JSON-first payload with request, result, and execution sections. Result exposes repository validation status through direct fields instead of empty-success text.",
1567
+ 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.",
1388
1568
  promptSnippet: "Return the structured git-validation payload for the runtime repository.",
1389
1569
  promptGuidelines: [
1390
1570
  "Input contract: no params. Scope is the cwd-derived runtime path context.",
1391
- "Output contract: request + result + execution. Result exposes git_path_present, status, worktree_status, and head_status.",
1571
+ "Output contract: result + execution. Result exposes git_path_present and aggregate status.",
1392
1572
  "Behavior contract: the tool checks work-tree membership, porcelain cleanliness, and symbolic-or-detached HEAD validity.",
1393
1573
  "Failure contract: execution.code and execution.stderr_lines surface git-path or repository-state errors.",
1394
1574
  ],
@@ -1396,14 +1576,13 @@ function registerAgentTools(pi: ExtensionAPI): void {
1396
1576
  async execute() {
1397
1577
  const projectBase = getProjectBase(process.cwd());
1398
1578
  const config = loadProjectConfig(process.cwd());
1399
- const runtimePaths = buildSharedRuntimePathFacts(process.cwd(), config);
1400
1579
  let execution;
1401
1580
  try {
1402
1581
  execution = buildToolExecutionSection(runGitCheck(projectBase, config));
1403
1582
  } catch (error) {
1404
1583
  execution = buildToolExecutionSection(normalizeToolFailure(error));
1405
1584
  }
1406
- const payload = buildGitCheckToolPayload(projectBase, resolveRuntimeGitPath(projectBase), runtimePaths, execution);
1585
+ const payload = buildGitCheckToolPayload(projectBase, resolveRuntimeGitPath(projectBase), execution);
1407
1586
  return buildStructuredToolExecuteResult(payload);
1408
1587
  },
1409
1588
  });
@@ -1411,17 +1590,17 @@ function registerAgentTools(pi: ExtensionAPI): void {
1411
1590
  const docsCheckSchema = Type.Object(
1412
1591
  {},
1413
1592
  {
1414
- description: "Input contract: no params. Output contract: JSON object with request, summary, files, and execution. File entries expose canonical paths, prompt_command remediation, and presence status for canonical docs.",
1593
+ 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.",
1415
1594
  },
1416
1595
  );
1417
1596
  pi.registerTool({
1418
1597
  name: "docs-check",
1419
1598
  label: "docs-check",
1420
- description: "Scope: canonical docs. Return a JSON-first payload with request, summary, files, and execution sections. File entries expose remediation prompt commands and direct presence facts for REQUIREMENTS.md, WORKFLOW.md, and REFERENCES.md.",
1599
+ 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.",
1421
1600
  promptSnippet: "Return the structured canonical-document validation payload.",
1422
1601
  promptGuidelines: [
1423
1602
  "Input contract: no params. Scope is docs-dir from the cwd-derived project configuration.",
1424
- "Output contract: request + summary + files + execution. File entries expose file_name, canonical_path, prompt_command, and status.",
1603
+ "Output contract: summary + files + execution. File entries expose file_name, canonical_path, prompt_command, and status.",
1425
1604
  "Specialization trigger: remediation differs per missing canonical file through prompt_command.",
1426
1605
  "Failure contract: execution.code is non-zero when any canonical document is missing; execution.stderr_lines enumerate missing files.",
1427
1606
  ],
@@ -1429,8 +1608,7 @@ function registerAgentTools(pi: ExtensionAPI): void {
1429
1608
  async execute() {
1430
1609
  const projectBase = getProjectBase(process.cwd());
1431
1610
  const config = loadProjectConfig(process.cwd());
1432
- const runtimePaths = buildSharedRuntimePathFacts(process.cwd(), config);
1433
- const payload = buildDocsCheckToolPayload(projectBase, config["docs-dir"], runtimePaths);
1611
+ const payload = buildDocsCheckToolPayload(projectBase, config["docs-dir"]);
1434
1612
  return buildStructuredToolExecuteResult(payload);
1435
1613
  },
1436
1614
  });
@@ -1438,17 +1616,17 @@ function registerAgentTools(pi: ExtensionAPI): void {
1438
1616
  const gitWtNameSchema = Type.Object(
1439
1617
  {},
1440
1618
  {
1441
- description: "Input contract: no params. Output contract: JSON object with request, result, and execution. Result exposes worktree_name and the normative useReq naming format string.",
1619
+ description: "Input contract: no params. Output contract: JSON object with result and execution. Result exposes worktree_name when generation succeeds.",
1442
1620
  },
1443
1621
  );
1444
1622
  pi.registerTool({
1445
1623
  name: "git-wt-name",
1446
1624
  label: "git-wt-name",
1447
- description: "Scope: current runtime path. Return a JSON-first payload with request, result, and execution sections. Result exposes the generated worktree name plus its normative format as direct fields.",
1625
+ 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.",
1448
1626
  promptSnippet: "Return the structured worktree-name generation payload.",
1449
1627
  promptGuidelines: [
1450
1628
  "Input contract: no params. Scope is the cwd-derived runtime path context.",
1451
- "Output contract: request + result + execution. Result exposes worktree_name and format_text.",
1629
+ "Output contract: result + execution. Result exposes worktree_name.",
1452
1630
  "Behavior contract: generation follows useReq-<project>-<sanitized-branch>-<YYYYMMDDHHMMSS>.",
1453
1631
  "Failure contract: execution.code and execution.stderr_lines surface git-path or branch-resolution errors.",
1454
1632
  ],
@@ -1456,14 +1634,13 @@ function registerAgentTools(pi: ExtensionAPI): void {
1456
1634
  async execute() {
1457
1635
  const projectBase = getProjectBase(process.cwd());
1458
1636
  const config = loadProjectConfig(process.cwd());
1459
- const runtimePaths = buildSharedRuntimePathFacts(process.cwd(), config);
1460
1637
  let execution;
1461
1638
  try {
1462
1639
  execution = buildToolExecutionSection(runGitWtName(projectBase, config));
1463
1640
  } catch (error) {
1464
1641
  execution = buildToolExecutionSection(normalizeToolFailure(error));
1465
1642
  }
1466
- const payload = buildWorktreeNameToolPayload(projectBase, resolveRuntimeGitPath(projectBase), runtimePaths, execution);
1643
+ const payload = buildWorktreeNameToolPayload(projectBase, resolveRuntimeGitPath(projectBase), execution);
1467
1644
  return buildStructuredToolExecuteResult(payload);
1468
1645
  },
1469
1646
  });
@@ -1473,17 +1650,17 @@ function registerAgentTools(pi: ExtensionAPI): void {
1473
1650
  wtName: Type.String({ description: "Exact target worktree name and branch name" }),
1474
1651
  },
1475
1652
  {
1476
- description: "Input contract: wtName. Output contract: JSON object with request, result, and execution. Result exposes operation, worktree_name, branch_name, derived worktree_path, and status.",
1653
+ description: "Input contract: wtName. Output contract: JSON object with result and execution. Result exposes worktree_name and derived worktree_path.",
1477
1654
  },
1478
1655
  );
1479
1656
  pi.registerTool({
1480
1657
  name: "git-wt-create",
1481
1658
  label: "git-wt-create",
1482
- description: "Scope: current runtime path. Return a JSON-first payload with request, result, and execution sections. Result exposes the requested create operation, exact worktree name, derived path, and mutation status.",
1659
+ 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.",
1483
1660
  promptSnippet: "Return the structured worktree-creation payload for the requested name.",
1484
1661
  promptGuidelines: [
1485
1662
  "Input contract: wtName is required and must match the exact worktree/branch name to create.",
1486
- "Output contract: request + result + execution. Result exposes operation=create, worktree_name, branch_name, worktree_path, and status.",
1663
+ "Output contract: result + execution. Result exposes worktree_name and worktree_path.",
1487
1664
  "Specialization trigger: worktree_path depends on the runtime git root parent directory.",
1488
1665
  "Failure contract: execution.code and execution.stderr_lines surface invalid-name, git, or finalization errors.",
1489
1666
  ],
@@ -1491,7 +1668,6 @@ function registerAgentTools(pi: ExtensionAPI): void {
1491
1668
  async execute(_toolCallId, params) {
1492
1669
  const projectBase = getProjectBase(process.cwd());
1493
1670
  const config = loadProjectConfig(process.cwd());
1494
- const runtimePaths = buildSharedRuntimePathFacts(process.cwd(), config);
1495
1671
  let execution;
1496
1672
  try {
1497
1673
  execution = buildToolExecutionSection(runGitWtCreate(projectBase, params.wtName, config));
@@ -1503,7 +1679,6 @@ function registerAgentTools(pi: ExtensionAPI): void {
1503
1679
  projectBase,
1504
1680
  resolveRuntimeGitPath(projectBase),
1505
1681
  params.wtName,
1506
- runtimePaths,
1507
1682
  execution,
1508
1683
  );
1509
1684
  return buildStructuredToolExecuteResult(payload);
@@ -1515,17 +1690,17 @@ function registerAgentTools(pi: ExtensionAPI): void {
1515
1690
  wtName: Type.String({ description: "Exact target worktree name and branch name" }),
1516
1691
  },
1517
1692
  {
1518
- description: "Input contract: wtName. Output contract: JSON object with request, result, and execution. Result exposes operation, worktree_name, branch_name, derived worktree_path, and status.",
1693
+ description: "Input contract: wtName. Output contract: JSON object with result and execution. Result exposes worktree_name and derived worktree_path.",
1519
1694
  },
1520
1695
  );
1521
1696
  pi.registerTool({
1522
1697
  name: "git-wt-delete",
1523
1698
  label: "git-wt-delete",
1524
- description: "Scope: current runtime path. Return a JSON-first payload with request, result, and execution sections. Result exposes the requested delete operation, exact worktree name, derived path, and mutation status.",
1699
+ 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.",
1525
1700
  promptSnippet: "Return the structured worktree-deletion payload for the requested name.",
1526
1701
  promptGuidelines: [
1527
1702
  "Input contract: wtName is required and must match the exact worktree/branch name to delete.",
1528
- "Output contract: request + result + execution. Result exposes operation=delete, worktree_name, branch_name, worktree_path, and status.",
1703
+ "Output contract: result + execution. Result exposes worktree_name and worktree_path.",
1529
1704
  "Specialization trigger: worktree_path depends on the runtime git root parent directory.",
1530
1705
  "Failure contract: execution.code and execution.stderr_lines surface missing-target or deletion errors.",
1531
1706
  ],
@@ -1533,7 +1708,6 @@ function registerAgentTools(pi: ExtensionAPI): void {
1533
1708
  async execute(_toolCallId, params) {
1534
1709
  const projectBase = getProjectBase(process.cwd());
1535
1710
  const config = loadProjectConfig(process.cwd());
1536
- const runtimePaths = buildSharedRuntimePathFacts(process.cwd(), config);
1537
1711
  let execution;
1538
1712
  try {
1539
1713
  execution = buildToolExecutionSection(runGitWtDelete(projectBase, params.wtName, config));
@@ -1545,7 +1719,6 @@ function registerAgentTools(pi: ExtensionAPI): void {
1545
1719
  projectBase,
1546
1720
  resolveRuntimeGitPath(projectBase),
1547
1721
  params.wtName,
1548
- runtimePaths,
1549
1722
  execution,
1550
1723
  );
1551
1724
  return buildStructuredToolExecuteResult(payload);
@@ -1721,7 +1894,7 @@ function formatStaticCheckLanguagesSummary(config: UseReqConfig): string {
1721
1894
 
1722
1895
  /**
1723
1896
  * @brief Renders the static-check configuration reference view.
1724
- * @details Produces a markdown-like summary containing configured entries, supported languages, supported modules, and example specifications. Runtime is O(l log l). No side effects occur.
1897
+ * @details Produces a markdown-like summary containing configured entries, supported languages, the Command-only user module surface, and canonical example specifications. Runtime is O(l log l). No side effects occur.
1725
1898
  * @param[in] config {UseReqConfig} Effective project configuration.
1726
1899
  * @return {string} Reference text for the editor view.
1727
1900
  */
@@ -1744,16 +1917,23 @@ function renderStaticCheckReference(config: UseReqConfig): string {
1744
1917
  for (const { language, extensions } of getSupportedStaticCheckLanguageSupport()) {
1745
1918
  lines.push(`- ${language}: ${extensions.join(", ")}`);
1746
1919
  }
1747
- lines.push("", `Supported modules: ${STATIC_CHECK_MODULES.join(", ")}`, "", "Examples:", "- Python=Ruff", "- Python=Command,mypy,--strict", "- TypeScript=Command,eslint,--max-warnings,0");
1920
+ lines.push(
1921
+ "",
1922
+ `Supported modules: ${STATIC_CHECK_MODULES.join(", ")}`,
1923
+ "",
1924
+ "Examples:",
1925
+ "- Python=Command,mypy,--strict",
1926
+ "- TypeScript=Command,eslint,--max-warnings,0",
1927
+ );
1748
1928
  return `${lines.join("\n")}\n`;
1749
1929
  }
1750
1930
 
1751
1931
  /**
1752
1932
  * @brief Builds the shared settings-menu choices for static-check management.
1753
- * @details Serializes static-check actions into right-valued menu rows consumed by the shared settings-menu renderer. Runtime is O(1). No external state is mutated.
1933
+ * @details Serializes Command-oriented static-check actions into right-valued menu rows consumed by the shared settings-menu renderer while omitting user-facing module selection. Runtime is O(1). No external state is mutated.
1754
1934
  * @param[in] config {UseReqConfig} Effective project configuration.
1755
1935
  * @return {PiUsereqSettingsMenuChoice[]} Ordered static-check menu choices.
1756
- * @satisfies REQ-008, REQ-151, REQ-152, REQ-153, REQ-154
1936
+ * @satisfies REQ-008, REQ-160, REQ-161, REQ-151, REQ-152, REQ-153, REQ-154
1757
1937
  */
1758
1938
  function buildStaticCheckMenuChoices(config: UseReqConfig): PiUsereqSettingsMenuChoice[] {
1759
1939
  const supportedLanguageCount = getSupportedStaticCheckLanguageSupport().length;
@@ -1763,13 +1943,13 @@ function buildStaticCheckMenuChoices(config: UseReqConfig): PiUsereqSettingsMenu
1763
1943
  id: "add-entry-supported-language",
1764
1944
  label: "Add entry for supported language",
1765
1945
  value: `${supportedLanguageCount} languages`,
1766
- description: "Select a supported language, then select one static-check module to add.",
1946
+ description: "Select a supported language, then configure the Command static-check executable.",
1767
1947
  },
1768
1948
  {
1769
1949
  id: "add-entry-raw-spec",
1770
1950
  label: "Add entry from LANG=MODULE[,CMD[,PARAM...]]",
1771
1951
  value: "raw spec",
1772
- description: "Enter one raw static-check specification string in canonical CLI format.",
1952
+ description: "Enter one raw Command-based static-check specification string in canonical CLI format.",
1773
1953
  },
1774
1954
  {
1775
1955
  id: "remove-language-entry",
@@ -1794,7 +1974,7 @@ function buildStaticCheckMenuChoices(config: UseReqConfig): PiUsereqSettingsMenu
1794
1974
 
1795
1975
  /**
1796
1976
  * @brief Builds the shared settings-menu choices for supported static-check languages.
1797
- * @details Exposes every supported language as one row whose right-side value reports extensions plus the current configured checker count. Runtime is O(l log l). No external state is mutated.
1977
+ * @details Exposes every supported language as one row whose right-side value reports extensions plus the current configured checker count for Command-oriented configuration flows. Runtime is O(l log l). No external state is mutated.
1798
1978
  * @param[in] config {UseReqConfig} Effective project configuration.
1799
1979
  * @return {PiUsereqSettingsMenuChoice[]} Ordered language-choice vector.
1800
1980
  */
@@ -1807,7 +1987,7 @@ function buildSupportedStaticCheckLanguageChoices(config: UseReqConfig): PiUsere
1807
1987
  id: language,
1808
1988
  label: language,
1809
1989
  value: `${extensions.join(", ")} • ${configuredCount} ${suffix}`,
1810
- description: `Configure static-check modules for ${language}. Supported extensions: ${extensions.join(", ")}.`,
1990
+ description: `Configure the Command static-check entry for ${language}. Supported extensions: ${extensions.join(", ")}.`,
1811
1991
  };
1812
1992
  }),
1813
1993
  {
@@ -1819,31 +1999,6 @@ function buildSupportedStaticCheckLanguageChoices(config: UseReqConfig): PiUsere
1819
1999
  ];
1820
2000
  }
1821
2001
 
1822
- /**
1823
- * @brief Builds the shared settings-menu choices for static-check modules.
1824
- * @details Exposes every supported static-check module as one selectable row with a concise execution description. Runtime is O(m) in module count. No external state is mutated.
1825
- * @param[in] language {string} Canonical selected language.
1826
- * @return {PiUsereqSettingsMenuChoice[]} Ordered module-choice vector.
1827
- */
1828
- function buildStaticCheckModuleChoices(language: string): PiUsereqSettingsMenuChoice[] {
1829
- return [
1830
- ...STATIC_CHECK_MODULES.map((moduleName) => ({
1831
- id: moduleName,
1832
- label: moduleName,
1833
- value: language,
1834
- description: moduleName === "Command"
1835
- ? `Run one explicit external command against ${language} files.`
1836
- : `Run the built-in ${moduleName} checker for ${language} files.`,
1837
- })),
1838
- {
1839
- id: "back",
1840
- label: "Back",
1841
- value: "",
1842
- description: "Return to language selection.",
1843
- },
1844
- ];
1845
- }
1846
-
1847
2002
  /**
1848
2003
  * @brief Builds the shared settings-menu choices for configured static-check languages.
1849
2004
  * @details Exposes only languages that currently have at least one configured checker so removal remains deterministic. Runtime is O(l log l). No external state is mutated.
@@ -1871,11 +2026,11 @@ function buildConfiguredStaticCheckLanguageChoices(config: UseReqConfig): PiUser
1871
2026
 
1872
2027
  /**
1873
2028
  * @brief Runs the interactive static-check configuration menu.
1874
- * @details Lets the user inspect support, add entries by guided prompts or raw spec strings, and remove configured language entries through the shared settings-menu renderer until the user exits. Runtime depends on user interaction count. Side effects include UI updates and config mutation.
2029
+ * @details Lets the user inspect support, add Command entries by guided prompts or raw spec strings, and remove configured language entries through the shared settings-menu renderer until the user exits. Runtime depends on user interaction count. Side effects include UI updates and config mutation.
1875
2030
  * @param[in] ctx {ExtensionCommandContext} Active command context.
1876
2031
  * @param[in,out] config {UseReqConfig} Mutable configuration object.
1877
2032
  * @return {Promise<void>} Promise resolved when the menu closes.
1878
- * @satisfies REQ-008, REQ-151, REQ-152, REQ-153, REQ-154
2033
+ * @satisfies REQ-008, REQ-160, REQ-161, REQ-151, REQ-152, REQ-153, REQ-154
1879
2034
  */
1880
2035
  async function configureStaticCheckMenu(ctx: ExtensionCommandContext, config: UseReqConfig): Promise<void> {
1881
2036
  while (true) {
@@ -1895,23 +2050,16 @@ async function configureStaticCheckMenu(ctx: ExtensionCommandContext, config: Us
1895
2050
  if (!selectedLanguage || selectedLanguage === "back") {
1896
2051
  continue;
1897
2052
  }
1898
- const moduleName = await showPiUsereqSettingsMenu(ctx, `static-check for ${selectedLanguage}`, buildStaticCheckModuleChoices(selectedLanguage));
1899
- if (!moduleName || moduleName === "back") {
1900
- continue;
1901
- }
1902
2053
 
1903
- const entry: StaticCheckEntry = { module: moduleName };
1904
- if (moduleName === "Command") {
1905
- const cmd = await ctx.ui.input(`Command executable for ${selectedLanguage}`, "");
1906
- if (!cmd?.trim()) {
1907
- ctx.ui.notify(`Command executable is required for ${selectedLanguage}`, "error");
1908
- continue;
1909
- }
1910
- entry.cmd = cmd.trim();
2054
+ const cmd = await ctx.ui.input(`Command executable for ${selectedLanguage}`, "");
2055
+ if (!cmd?.trim()) {
2056
+ ctx.ui.notify(`Command executable is required for ${selectedLanguage}`, "error");
2057
+ continue;
1911
2058
  }
1912
2059
 
2060
+ const entry: StaticCheckEntry = { module: "Command", cmd: cmd.trim() };
1913
2061
  const paramsInput = await ctx.ui.input(
1914
- `Additional parameters for ${moduleName} on ${selectedLanguage} (optional, shell-style)`,
2062
+ `Additional parameters for Command on ${selectedLanguage} (optional, shell-style)`,
1915
2063
  "",
1916
2064
  );
1917
2065
  const params = paramsInput?.trim() ? shellSplit(paramsInput.trim()) : [];
@@ -1926,7 +2074,7 @@ async function configureStaticCheckMenu(ctx: ExtensionCommandContext, config: Us
1926
2074
  }
1927
2075
 
1928
2076
  if (staticChoice === "add-entry-raw-spec") {
1929
- const spec = await ctx.ui.input("Static-check spec", "Python=Ruff");
2077
+ const spec = await ctx.ui.input("Static-check spec", "Python=Command,true");
1930
2078
  if (!spec?.trim()) {
1931
2079
  continue;
1932
2080
  }
@@ -1954,12 +2102,16 @@ async function configureStaticCheckMenu(ctx: ExtensionCommandContext, config: Us
1954
2102
 
1955
2103
  /**
1956
2104
  * @brief Builds the shared settings-menu choices for the top-level pi-usereq configuration UI.
1957
- * @details Serializes primary configuration actions into right-valued menu rows consumed by the shared settings-menu renderer. Runtime is O(s) in source-directory count. No external state is mutated.
2105
+ * @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.
2106
+ * @param[in] cwd {string} Current working directory.
1958
2107
  * @param[in] config {UseReqConfig} Effective project configuration.
1959
2108
  * @return {PiUsereqSettingsMenuChoice[]} Ordered top-level menu choices.
1960
- * @satisfies REQ-006, REQ-031, REQ-137, REQ-150, REQ-151, REQ-152
2109
+ * @satisfies REQ-006, REQ-031, REQ-137, REQ-150, REQ-151, REQ-152, REQ-162
1961
2110
  */
1962
- function buildPiUsereqMenuChoices(config: UseReqConfig): PiUsereqSettingsMenuChoice[] {
2111
+ function buildPiUsereqMenuChoices(
2112
+ cwd: string,
2113
+ config: UseReqConfig,
2114
+ ): PiUsereqSettingsMenuChoice[] {
1963
2115
  return [
1964
2116
  {
1965
2117
  id: "docs-dir",
@@ -1995,7 +2147,14 @@ function buildPiUsereqMenuChoices(config: UseReqConfig): PiUsereqSettingsMenuCho
1995
2147
  id: "notifications",
1996
2148
  label: "notifications",
1997
2149
  value: `beep:${formatPiNotifyBeepStatus(config)} • sound:${config["notify-sound"]}`,
1998
- description: "Manage terminal beep flags, selected notify command, hotkey bind, and per-level notify commands.",
2150
+ description: "Manage terminal beep flags, selected notify command, hotkey bind, per-level notify commands, and the nested Pushover submenu.",
2151
+ },
2152
+ {
2153
+ id: "show-config",
2154
+ label: "show-config",
2155
+ value: formatProjectConfigPathForMenu(cwd),
2156
+ valueTone: "dim",
2157
+ description: "Write the current project configuration JSON into the editor without saving additional changes.",
1999
2158
  },
2000
2159
  {
2001
2160
  id: "reset-defaults",
@@ -2003,12 +2162,6 @@ function buildPiUsereqMenuChoices(config: UseReqConfig): PiUsereqSettingsMenuCho
2003
2162
  value: "",
2004
2163
  description: "Restore the default pi-usereq configuration for the current project base.",
2005
2164
  },
2006
- {
2007
- id: "show-config",
2008
- label: "show-config",
2009
- value: "",
2010
- description: "Write the current project configuration JSON into the editor without saving additional changes.",
2011
- },
2012
2165
  {
2013
2166
  id: "save-and-close",
2014
2167
  label: "Save and close",
@@ -2074,12 +2227,12 @@ function buildSrcDirRemovalChoices(config: UseReqConfig): PiUsereqSettingsMenuCh
2074
2227
 
2075
2228
  /**
2076
2229
  * @brief Runs the top-level pi-usereq configuration menu.
2077
- * @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, and active-tool changes.
2230
+ * @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.
2078
2231
  * @param[in] pi {ExtensionAPI} Active extension API instance.
2079
2232
  * @param[in] ctx {ExtensionCommandContext} Active command context.
2080
2233
  * @param[in,out] statusController {PiUsereqStatusController} Mutable status controller.
2081
2234
  * @return {Promise<void>} Promise resolved when configuration is saved and the menu closes.
2082
- * @satisfies REQ-006, REQ-031, REQ-137, REQ-150, REQ-151, REQ-152, REQ-153, REQ-154
2235
+ * @satisfies REQ-006, REQ-031, REQ-137, REQ-150, REQ-151, REQ-152, REQ-153, REQ-154, REQ-162
2083
2236
  */
2084
2237
  async function configurePiUsereq(
2085
2238
  pi: ExtensionAPI,
@@ -2096,7 +2249,11 @@ async function configurePiUsereq(
2096
2249
  };
2097
2250
 
2098
2251
  while (true) {
2099
- const choice = await showPiUsereqSettingsMenu(ctx, "pi-usereq", buildPiUsereqMenuChoices(config));
2252
+ const choice = await showPiUsereqSettingsMenu(
2253
+ ctx,
2254
+ "pi-usereq",
2255
+ buildPiUsereqMenuChoices(ctx.cwd, config),
2256
+ );
2100
2257
  if (!choice || choice === "save-and-close") {
2101
2258
  ensureSaved();
2102
2259
  refreshStatus();
@@ -2190,18 +2347,20 @@ function registerConfigCommands(
2190
2347
  * configuration commands plus agent tools, registers the configurable
2191
2348
  * successful-run sound shortcut when the runtime supports shortcuts, and
2192
2349
  * installs shared wrappers for all supported pi lifecycle hooks so status
2193
- * telemetry, context usage, prompt timing, and pi-notify effects remain
2194
- * synchronized with runtime events. Runtime is O(h) in hook count during
2195
- * registration. Side effects include filesystem reads, command/tool/shortcut
2196
- * registration, UI updates, active-tool changes, and timer scheduling.
2350
+ * telemetry, context usage, prompt timing, cumulative runtime, prompt-specific
2351
+ * Pushover metadata, and pi-notify effects remain synchronized with runtime
2352
+ * events. Runtime is O(h) in hook
2353
+ * count during registration. Side effects include filesystem reads,
2354
+ * command/tool/shortcut registration, UI updates, active-tool changes, and
2355
+ * timer scheduling.
2197
2356
  * @param[in] pi {ExtensionAPI} Active extension API instance.
2198
2357
  * @return {void} No return value.
2199
- * @satisfies DES-002, REQ-004, REQ-005, REQ-009, REQ-044, REQ-045, REQ-067, REQ-068, REQ-109, REQ-110, 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-129, REQ-130, REQ-131, REQ-132, REQ-133, REQ-134, REQ-135, REQ-136, REQ-137
2358
+ * @satisfies DES-002, REQ-004, REQ-005, REQ-009, REQ-044, REQ-045, 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-129, REQ-130, REQ-131, REQ-132, REQ-133, REQ-134, REQ-135, REQ-136, REQ-137, REQ-148, REQ-159, REQ-163, REQ-164, REQ-165, REQ-166, REQ-167, REQ-168, REQ-169, REQ-170, REQ-171, REQ-172
2200
2359
  */
2201
2360
  export default function piUsereqExtension(pi: ExtensionAPI): void {
2202
- const statusController = createPiUsereqStatusController(() => pi.getActiveTools());
2361
+ const statusController = createPiUsereqStatusController();
2203
2362
  ensureBundledResourcesAccessible();
2204
- registerPromptCommands(pi);
2363
+ registerPromptCommands(pi, statusController);
2205
2364
  registerAgentTools(pi);
2206
2365
  registerConfigCommands(pi, statusController);
2207
2366
  registerPiNotifyShortcut(pi, statusController);