pi-usereq 0.11.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +59 -0
- package/README.md +6 -6
- package/package.json +1 -1
- package/pi-usereq/docs/REFERENCES.md +1135 -834
- package/pi-usereq/docs/REQUIREMENTS.md +177 -110
- package/pi-usereq/docs/WORKFLOW.md +244 -79
- package/scripts/lib/extension-debug-harness.ts +2 -2
- package/scripts/tool-args-to-params.ts +2 -2
- package/src/cli.ts +12 -12
- package/src/core/config.ts +541 -180
- package/src/core/extension-status.ts +69 -12
- package/src/core/path-context.ts +19 -4
- package/src/core/pi-notify.ts +5 -5
- package/src/core/pi-usereq-tools.ts +4 -2
- package/src/core/prompt-command-catalog.ts +4 -5
- package/src/core/prompt-command-runtime.ts +183 -44
- package/src/core/prompts.ts +0 -2
- package/src/core/req-references-command.ts +175 -0
- package/src/core/req-reset-command.ts +323 -0
- package/src/core/resources.ts +6 -23
- package/src/core/settings-menu.ts +85 -28
- package/src/core/tool-runner.ts +26 -6
- package/src/index.ts +601 -116
- package/tests/attended-results-scenarios.ts +15 -9
- package/tests/cli-command-option-parity.test.ts +53 -35
- package/tests/debug-extension-harness.test.ts +8 -10
- package/tests/extension-registration.test.ts +1204 -205
- package/tests/helpers.ts +29 -6
- package/tests/oracle-project.test.ts +4 -4
- package/tests/oracle-standalone.test.ts +5 -5
- package/src/core/reference-payload.ts +0 -752
- package/src/resources/prompts/references.md +0 -64
- /package/tests/fixtures_attended_results/project/{references.json → summarize.json} +0 -0
- /package/tests/fixtures_attended_results/standalone/{files-references → files-summarize}/fixture_c.c.json +0 -0
- /package/tests/fixtures_attended_results/standalone/{files-references → files-summarize}/fixture_cpp.cpp.json +0 -0
- /package/tests/fixtures_attended_results/standalone/{files-references → files-summarize}/fixture_csharp.cs.json +0 -0
- /package/tests/fixtures_attended_results/standalone/{files-references → files-summarize}/fixture_elixir.ex.json +0 -0
- /package/tests/fixtures_attended_results/standalone/{files-references → files-summarize}/fixture_go.go.json +0 -0
- /package/tests/fixtures_attended_results/standalone/{files-references → files-summarize}/fixture_haskell.hs.json +0 -0
- /package/tests/fixtures_attended_results/standalone/{files-references → files-summarize}/fixture_java.java.json +0 -0
- /package/tests/fixtures_attended_results/standalone/{files-references → files-summarize}/fixture_javascript.js.json +0 -0
- /package/tests/fixtures_attended_results/standalone/{files-references → files-summarize}/fixture_kotlin.kt.json +0 -0
- /package/tests/fixtures_attended_results/standalone/{files-references → files-summarize}/fixture_lua.lua.json +0 -0
- /package/tests/fixtures_attended_results/standalone/{files-references → files-summarize}/fixture_perl.pl.json +0 -0
- /package/tests/fixtures_attended_results/standalone/{files-references → files-summarize}/fixture_php.php.json +0 -0
- /package/tests/fixtures_attended_results/standalone/{files-references → files-summarize}/fixture_python.py.json +0 -0
- /package/tests/fixtures_attended_results/standalone/{files-references → files-summarize}/fixture_ruby.rb.json +0 -0
- /package/tests/fixtures_attended_results/standalone/{files-references → files-summarize}/fixture_rust.rs.json +0 -0
- /package/tests/fixtures_attended_results/standalone/{files-references → files-summarize}/fixture_scala.scala.json +0 -0
- /package/tests/fixtures_attended_results/standalone/{files-references → files-summarize}/fixture_shell.sh.json +0 -0
- /package/tests/fixtures_attended_results/standalone/{files-references → files-summarize}/fixture_swift.swift.json +0 -0
- /package/tests/fixtures_attended_results/standalone/{files-references → files-summarize}/fixture_typescript.ts.json +0 -0
- /package/tests/fixtures_attended_results/standalone/{files-references → files-summarize}/fixture_zig.zig.json +0 -0
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.
|
|
11
|
+
export const VERSION = "0.13.0";
|
|
12
12
|
|
|
13
13
|
import fs from "node:fs";
|
|
14
14
|
import path from "node:path";
|
|
@@ -32,6 +32,7 @@ import {
|
|
|
32
32
|
createStaticCheckLanguageConfig,
|
|
33
33
|
getDefaultConfig,
|
|
34
34
|
getDefaultStaticCheckConfig,
|
|
35
|
+
getGlobalConfigPath,
|
|
35
36
|
getProjectConfigPath,
|
|
36
37
|
loadConfig,
|
|
37
38
|
normalizeConfigPaths,
|
|
@@ -91,6 +92,18 @@ import {
|
|
|
91
92
|
restorePromptCommandExecution,
|
|
92
93
|
type PromptCommandExecutionPlan,
|
|
93
94
|
} from "./core/prompt-command-runtime.js";
|
|
95
|
+
import {
|
|
96
|
+
REQ_REFERENCES_COMMAND_DESCRIPTION,
|
|
97
|
+
executeReqReferencesCommandExecution,
|
|
98
|
+
prepareReqReferencesCommandExecution,
|
|
99
|
+
} from "./core/req-references-command.js";
|
|
100
|
+
import {
|
|
101
|
+
REQ_RESET_COMMAND_DESCRIPTION,
|
|
102
|
+
executeReqResetCommandExecution,
|
|
103
|
+
prepareReqResetCommandExecution,
|
|
104
|
+
type ReqResetCommandExecutionResult,
|
|
105
|
+
type ReqResetCommandPlan,
|
|
106
|
+
} from "./core/req-reset-command.js";
|
|
94
107
|
import {
|
|
95
108
|
DEBUG_PROMPT_NAMES,
|
|
96
109
|
DEBUG_WORKFLOW_STATES,
|
|
@@ -114,6 +127,7 @@ import {
|
|
|
114
127
|
import { PROMPT_COMMAND_NAMES } from "./core/prompt-command-catalog.js";
|
|
115
128
|
import { resolveRuntimeGitPath } from "./core/runtime-project-paths.js";
|
|
116
129
|
import {
|
|
130
|
+
clearPersistedPromptCommandRuntimeState,
|
|
117
131
|
readPersistedPromptCommandRuntimeState,
|
|
118
132
|
writePersistedPromptCommandRuntimeState,
|
|
119
133
|
} from "./core/prompt-command-state.js";
|
|
@@ -123,8 +137,10 @@ import {
|
|
|
123
137
|
PI_USEREQ_STATUS_HOOK_NAMES,
|
|
124
138
|
createPiUsereqStatusController,
|
|
125
139
|
disposePiUsereqStatusController,
|
|
140
|
+
getPiUsereqRuntimeSoundLevel,
|
|
126
141
|
isStaleExtensionContextError,
|
|
127
142
|
renderPiUsereqStatus,
|
|
143
|
+
setPiUsereqRuntimeSoundLevel,
|
|
128
144
|
setPiUsereqStatusConfig,
|
|
129
145
|
setPiUsereqWorkflowState,
|
|
130
146
|
shouldPreservePromptCommandStateOnShutdown,
|
|
@@ -135,13 +151,14 @@ import {
|
|
|
135
151
|
import {
|
|
136
152
|
runCompress,
|
|
137
153
|
runFilesCompress,
|
|
138
|
-
runFilesReferences,
|
|
139
154
|
runFilesSearch,
|
|
140
155
|
runFilesStaticCheck,
|
|
156
|
+
runFilesSummarize,
|
|
141
157
|
runFilesTokens,
|
|
142
158
|
runProjectStaticCheck,
|
|
143
159
|
runReferences,
|
|
144
160
|
runSearch,
|
|
161
|
+
runSummarize,
|
|
145
162
|
runTokens,
|
|
146
163
|
type ToolResult,
|
|
147
164
|
} from "./core/tool-runner.js";
|
|
@@ -252,12 +269,12 @@ function loadProjectConfig(cwd: string): UseReqConfig {
|
|
|
252
269
|
}
|
|
253
270
|
|
|
254
271
|
/**
|
|
255
|
-
* @brief Persists project configuration from the extension runtime.
|
|
256
|
-
* @details Resolves the project base, normalizes configured directory paths into project-relative form, and delegates persistence to `saveConfig` without serializing runtime-derived path metadata. Runtime is O(n) in config size. Side effects include config-file writes.
|
|
272
|
+
* @brief Persists effective project configuration from the extension runtime.
|
|
273
|
+
* @details Resolves the project base, normalizes configured local directory paths into project-relative form, and delegates split local/global persistence to `saveConfig` without serializing runtime-derived path metadata. Runtime is O(n) in config size. Side effects include config-file writes.
|
|
257
274
|
* @param[in] cwd {string} Current working directory.
|
|
258
|
-
* @param[in] config {UseReqConfig}
|
|
275
|
+
* @param[in] config {UseReqConfig} Effective configuration to persist.
|
|
259
276
|
* @return {void} No return value.
|
|
260
|
-
* @satisfies REQ-146
|
|
277
|
+
* @satisfies REQ-146, REQ-315
|
|
261
278
|
*/
|
|
262
279
|
function saveProjectConfig(cwd: string, config: UseReqConfig): void {
|
|
263
280
|
const projectBase = getProjectBase(cwd);
|
|
@@ -265,18 +282,28 @@ function saveProjectConfig(cwd: string, config: UseReqConfig): void {
|
|
|
265
282
|
}
|
|
266
283
|
|
|
267
284
|
/**
|
|
268
|
-
* @brief Formats the current
|
|
269
|
-
* @details Resolves `<base-path>/.pi-usereq.json` from the cwd-derived project base
|
|
285
|
+
* @brief Formats the current local config path for top-level menu display.
|
|
286
|
+
* @details Resolves `<base-path>/.pi-usereq.json` from the cwd-derived project base and reuses the shared runtime-path formatter so the `Show local configuration` row uses the documented `~`-relative display contract. Runtime is O(p) in path length. No external state is mutated.
|
|
270
287
|
* @param[in] cwd {string} Current working directory.
|
|
271
|
-
* @return {string} `~`-relative or absolute config
|
|
288
|
+
* @return {string} `~`-relative or absolute local config-path display value.
|
|
272
289
|
* @satisfies REQ-162
|
|
273
290
|
*/
|
|
274
|
-
function
|
|
291
|
+
function formatLocalConfigPathForMenu(cwd: string): string {
|
|
275
292
|
return formatRuntimePathForDisplay(
|
|
276
293
|
getProjectConfigPath(getProjectBase(cwd)),
|
|
277
294
|
);
|
|
278
295
|
}
|
|
279
296
|
|
|
297
|
+
/**
|
|
298
|
+
* @brief Formats the current global config path for top-level menu display.
|
|
299
|
+
* @details Resolves `~/.config/pi-usereq/config.json` through the shared runtime-path formatter so the `Show global configuration` row uses the documented `~`-relative display contract. Runtime is O(p) in path length. No external state is mutated.
|
|
300
|
+
* @return {string} `~`-relative or absolute global config-path display value.
|
|
301
|
+
* @satisfies REQ-319
|
|
302
|
+
*/
|
|
303
|
+
function formatGlobalConfigPathForMenu(): string {
|
|
304
|
+
return formatRuntimePathForDisplay(getGlobalConfigPath());
|
|
305
|
+
}
|
|
306
|
+
|
|
280
307
|
/**
|
|
281
308
|
* @brief Builds the standardized terminal rows appended to every configuration menu.
|
|
282
309
|
* @details Returns the canonical value-less `Reset defaults` row so all configuration menus and descendant selector menus share the same terminal ordering contract without rendering `Save and close`. Runtime is O(1). No external state is mutated.
|
|
@@ -393,21 +420,46 @@ async function confirmResetChanges(
|
|
|
393
420
|
}
|
|
394
421
|
|
|
395
422
|
/**
|
|
396
|
-
* @brief Writes
|
|
397
|
-
* @details Reads the
|
|
423
|
+
* @brief Writes one already-persisted config file text into the editor.
|
|
424
|
+
* @details Reads the target config file from disk after the caller has saved any pending changes and forwards the exact persisted text into the editor. Runtime is O(n) in serialized config size. Side effects include filesystem reads and editor-text mutation.
|
|
425
|
+
* @param[in] ctx {ExtensionCommandContext} Active command context.
|
|
426
|
+
* @param[in] configPath {string} Absolute persisted config path.
|
|
427
|
+
* @return {void} No return value.
|
|
428
|
+
*/
|
|
429
|
+
function writePersistedConfigToEditor(
|
|
430
|
+
ctx: ExtensionCommandContext,
|
|
431
|
+
configPath: string,
|
|
432
|
+
): void {
|
|
433
|
+
ctx.ui.setEditorText(fs.readFileSync(configPath, "utf8"));
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* @brief Writes the already-persisted local configuration file text into the editor.
|
|
438
|
+
* @details Reads `<base-path>/.pi-usereq.json` from disk after the caller has saved any pending local and global configuration changes, then forwards that exact persisted text into the editor. Runtime is O(n) in serialized config size. Side effects include filesystem reads and editor-text mutation.
|
|
398
439
|
* @param[in] ctx {ExtensionCommandContext} Active command context.
|
|
399
440
|
* @param[in] cwd {string} Current working directory.
|
|
400
|
-
* @param[in] _config {UseReqConfig} Unused effective project configuration retained for stable call-site shape.
|
|
401
441
|
* @return {void} No return value.
|
|
402
442
|
* @satisfies REQ-031
|
|
403
443
|
*/
|
|
404
|
-
function
|
|
444
|
+
function writePersistedLocalConfigToEditor(
|
|
405
445
|
ctx: ExtensionCommandContext,
|
|
406
446
|
cwd: string,
|
|
407
|
-
_config: UseReqConfig,
|
|
408
447
|
): void {
|
|
409
448
|
const projectBase = getProjectBase(cwd);
|
|
410
|
-
ctx
|
|
449
|
+
writePersistedConfigToEditor(ctx, getProjectConfigPath(projectBase));
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
/**
|
|
453
|
+
* @brief Writes the already-persisted global configuration file text into the editor.
|
|
454
|
+
* @details Reads `~/.config/pi-usereq/config.json` from disk after the caller has saved any pending local and global configuration changes, then forwards that exact persisted text into the editor. Runtime is O(n) in serialized config size. Side effects include filesystem reads and editor-text mutation.
|
|
455
|
+
* @param[in] ctx {ExtensionCommandContext} Active command context.
|
|
456
|
+
* @return {void} No return value.
|
|
457
|
+
* @satisfies REQ-318
|
|
458
|
+
*/
|
|
459
|
+
function writePersistedGlobalConfigToEditor(
|
|
460
|
+
ctx: ExtensionCommandContext,
|
|
461
|
+
): void {
|
|
462
|
+
writePersistedConfigToEditor(ctx, getGlobalConfigPath());
|
|
411
463
|
}
|
|
412
464
|
|
|
413
465
|
/**
|
|
@@ -686,6 +738,44 @@ function executeMonolithicTool(operation: () => ToolResult): ReturnType<typeof b
|
|
|
686
738
|
}
|
|
687
739
|
}
|
|
688
740
|
|
|
741
|
+
/**
|
|
742
|
+
* @brief Executes one CLI-style runner for a status-only agent tool.
|
|
743
|
+
* @details Reuses the standalone tool-runner contract, preserves `content[0].text` as the status-only `success` or `error: <diagnostic>` payload, and strips success-path `stdout_lines` so `details.execution` stays limited to the numeric code plus optional residual stderr diagnostics. Runtime is dominated by the delegated runner. Side effects depend on the selected tool.
|
|
744
|
+
* @param[in] operation {() => ToolResult} Runner callback.
|
|
745
|
+
* @return {ReturnType<typeof buildMonolithicToolExecuteResult>} Status-only tool execute result.
|
|
746
|
+
* @satisfies REQ-294, REQ-295, REQ-296
|
|
747
|
+
*/
|
|
748
|
+
function executeStatusTool(operation: () => ToolResult): ReturnType<typeof buildMonolithicToolExecuteResult> {
|
|
749
|
+
const normalizeExecution = (
|
|
750
|
+
payload: ReturnType<typeof buildMonolithicToolExecuteResult>,
|
|
751
|
+
): ReturnType<typeof buildMonolithicToolExecuteResult> => {
|
|
752
|
+
const execution = payload.details.execution;
|
|
753
|
+
return {
|
|
754
|
+
content: payload.content,
|
|
755
|
+
details: {
|
|
756
|
+
execution: {
|
|
757
|
+
code: execution.code,
|
|
758
|
+
...(Array.isArray(execution.stderr_lines) && execution.stderr_lines.length > 0
|
|
759
|
+
? { stderr_lines: execution.stderr_lines }
|
|
760
|
+
: {}),
|
|
761
|
+
},
|
|
762
|
+
},
|
|
763
|
+
};
|
|
764
|
+
};
|
|
765
|
+
|
|
766
|
+
try {
|
|
767
|
+
return normalizeExecution(buildMonolithicToolExecuteResult(operation()));
|
|
768
|
+
} catch (error) {
|
|
769
|
+
const failure = normalizeToolFailure(error);
|
|
770
|
+
const diagnostic = failure.stderr.trim().replace(/^(Error|error):\s*/u, "") || "unknown failure";
|
|
771
|
+
return normalizeExecution(buildMonolithicToolExecuteResult({
|
|
772
|
+
stdout: "",
|
|
773
|
+
stderr: `error: ${diagnostic}`,
|
|
774
|
+
code: failure.code,
|
|
775
|
+
}));
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
|
|
689
779
|
/**
|
|
690
780
|
* @brief Starts delivery of one rendered prompt into the current active session.
|
|
691
781
|
* @details Prefers the replacement-session `sendUserMessage(...)` helper exposed by `withSession(...)` callbacks after session replacement so post-switch prompt delivery never reuses stale pre-switch session-bound extension objects. Returns the underlying delivery promise without awaiting it so callers can record the `running` workflow transition as soon as prompt handoff is accepted instead of waiting for the full agent turn to complete on runtimes whose async replacement-session helpers resolve only after `agent_end`. When pi later invalidates that replacement-session context during successful prompt-end restoration, the helper suppresses the documented stale-extension-context rejection because the prompt was already accepted and late rethrow would surface a false orchestration failure. Falls back to `pi.sendUserMessage(...)` only for non-replacement flows or runtimes that do not expose replacement-session helpers. Runtime is O(n) in prompt length. Side effects are limited to user-message delivery.
|
|
@@ -838,7 +928,7 @@ function transitionPromptWorkflowState(
|
|
|
838
928
|
|
|
839
929
|
/**
|
|
840
930
|
* @brief Resolves the runtime slash-command description for one bundled prompt.
|
|
841
|
-
* @details Reads the bundled prompt
|
|
931
|
+
* @details Reads the bundled prompt markdown, extracts the first `# ` heading payload, and falls back to the historical generated label when the prompt omits a level-one heading. Runtime is O(n) in prompt length. Side effects are limited to filesystem reads.
|
|
842
932
|
* @param[in] promptName {import("./core/prompt-command-catalog.js").PromptCommandName} Bundled prompt name.
|
|
843
933
|
* @return {string} Runtime command description.
|
|
844
934
|
*/
|
|
@@ -889,6 +979,38 @@ function notifyContextSafely(
|
|
|
889
979
|
}
|
|
890
980
|
}
|
|
891
981
|
|
|
982
|
+
/**
|
|
983
|
+
* @brief Rejects one non-`idle` req-command invocation and records the workflow error state.
|
|
984
|
+
* @details Builds a deterministic busy-state diagnostic from the current workflow state, transitions the shared workflow state to `error`, preserves any pending or active prompt execution metadata for later closure handling, emits an error notification, and throws `ReqError`. Bundled prompt commands reuse `transitionPromptWorkflowState(...)` when cached configuration is available so prompt debug logging captures the actual state transition; specialized non-prompt commands fall back to direct status mutation. Runtime is O(1). Side effects include workflow-state mutation, status-bar rendering, optional debug-log writes, and user notification delivery.
|
|
985
|
+
* @param[in,out] statusController {PiUsereqStatusController} Mutable status controller.
|
|
986
|
+
* @param[in] ctx {ExtensionContext | ExtensionCommandContext} Active extension context.
|
|
987
|
+
* @param[in] promptName {import("./core/prompt-command-catalog.js").PromptCommandName | undefined} Optional bundled prompt name used for prompt debug logging.
|
|
988
|
+
* @return {never} This helper always throws a deterministic `ReqError`.
|
|
989
|
+
* @throws {ReqError} Always throws because non-`idle` req commands are rejected.
|
|
990
|
+
* @satisfies REQ-224
|
|
991
|
+
*/
|
|
992
|
+
function rejectNonIdleReqCommand(
|
|
993
|
+
statusController: PiUsereqStatusController,
|
|
994
|
+
ctx: ExtensionContext | ExtensionCommandContext,
|
|
995
|
+
promptName?: import("./core/prompt-command-catalog.js").PromptCommandName,
|
|
996
|
+
): never {
|
|
997
|
+
const message = `ERROR: Prompt workflow state is ${statusController.state.workflowState}, expected idle.`;
|
|
998
|
+
if (promptName !== undefined && statusController.config !== undefined) {
|
|
999
|
+
transitionPromptWorkflowState(
|
|
1000
|
+
statusController,
|
|
1001
|
+
ctx,
|
|
1002
|
+
resolveDebugProjectBase(ctx.cwd, statusController),
|
|
1003
|
+
statusController.config,
|
|
1004
|
+
promptName,
|
|
1005
|
+
"error",
|
|
1006
|
+
);
|
|
1007
|
+
} else {
|
|
1008
|
+
setPiUsereqWorkflowState(statusController, "error", ctx);
|
|
1009
|
+
}
|
|
1010
|
+
notifyContextSafely(ctx, message, "error");
|
|
1011
|
+
throw new ReqError(message, 1);
|
|
1012
|
+
}
|
|
1013
|
+
|
|
892
1014
|
/**
|
|
893
1015
|
* @brief Returns the configurable active-tool inventory visible to the extension.
|
|
894
1016
|
* @details Filters runtime tools against the canonical configurable-tool set, keeps only builtin-backed embedded tools, and orders the result by the documented custom/files/embedded/default-disabled grouping. Runtime is O(t log t). No external state is mutated.
|
|
@@ -947,14 +1069,14 @@ function applyConfiguredPiUsereqTools(pi: ExtensionAPI, config: UseReqConfig): v
|
|
|
947
1069
|
|
|
948
1070
|
/**
|
|
949
1071
|
* @brief Handles one intercepted pi lifecycle hook for pi-usereq status updates.
|
|
950
|
-
* @details Applies session-start-specific resource validation, project-config refresh, startup-tool enablement, and selected debug-tool logging before forwarding the originating hook name and payload into the shared `updateExtensionStatus(...)` pipeline. Before `agent_start`, re-verifies any prepared prompt execution session switch. On `agent_end`, dispatches configured command-notify, sound, and prompt-specific Pushover effects, logs dedicated workflow-closure diagnostics, restores the original session-backed `base-path` for every matched worktree-backed completion by reusing persisted replacement-session command contexts when event contexts omit `switchSession()`,
|
|
1072
|
+
* @details Applies session-start-specific resource validation, project-config refresh, startup-tool enablement, and selected debug-tool logging before forwarding the originating hook name and payload into the shared `updateExtensionStatus(...)` pipeline. Before `agent_start`, re-verifies any prepared prompt execution session switch. On `agent_end`, dispatches configured command-notify, sound, and prompt-specific Pushover effects, logs dedicated workflow-closure diagnostics, restores the original session-backed `base-path` for every matched worktree-backed completion by reusing persisted replacement-session command contexts when event contexts omit `switchSession()`, executes the stash-assisted merge-and-delete finalization path for every matched successful worktree-backed completion even when a later busy-command rejection already moved workflow state to `error`, emits a warning-only notification when restored `base-path` changes are reapplied after merge, tolerates stale replacement-session notification contexts after session replacement, retains the worktree plus notifies closure failure for interrupted or failed outcomes, logs selected prompt workflow transitions, and transitions workflow state through `merging`, `error`, and `idle` as required. On `session_shutdown`, captures pre-update prompt snapshots so workflow-shutdown diagnostics and same-runtime command continuation preserve the active prompt workflow state across switch-triggered rebinding, then disposes the shared controller. Runtime is dominated by configuration loading during `session_start` and git finalization during matched successful `agent_end` handling; all other hooks are O(1). Side effects include resource checks, active-tool mutation, active-session replacement, status updates, live-ticker disposal on shutdown, optional child-process spawning, outbound HTTPS requests, branch merges, worktree deletion, and optional debug-log writes.
|
|
951
1073
|
* @param[in] pi {ExtensionAPI} Active extension API instance.
|
|
952
1074
|
* @param[in,out] statusController {PiUsereqStatusController} Mutable status controller.
|
|
953
1075
|
* @param[in] hookName {PiUsereqStatusHookName} Intercepted hook name.
|
|
954
1076
|
* @param[in] event {unknown} Hook payload forwarded by pi.
|
|
955
1077
|
* @param[in] ctx {ExtensionContext} Active extension context.
|
|
956
1078
|
* @return {Promise<void>} Promise resolved when hook processing completes.
|
|
957
|
-
* @satisfies REQ-117, REQ-118, REQ-119, REQ-131, REQ-132, REQ-133, REQ-166, REQ-167, REQ-168, REQ-169, REQ-172, REQ-176, REQ-178, REQ-184, REQ-185, REQ-186, REQ-187, REQ-208, REQ-209, REQ-221, REQ-228, REQ-229, REQ-230, REQ-244, REQ-245, REQ-246, REQ-247, REQ-276, REQ-277, REQ-278, REQ-279, REQ-280
|
|
1079
|
+
* @satisfies REQ-117, REQ-118, REQ-119, REQ-131, REQ-132, REQ-133, REQ-166, REQ-167, REQ-168, REQ-169, REQ-172, REQ-176, REQ-178, REQ-184, REQ-185, REQ-186, REQ-187, REQ-208, REQ-209, REQ-221, REQ-228, REQ-229, REQ-230, REQ-244, REQ-245, REQ-246, REQ-247, REQ-276, REQ-277, REQ-278, REQ-279, REQ-280, REQ-291, REQ-292
|
|
958
1080
|
*/
|
|
959
1081
|
async function handleExtensionStatusEvent(
|
|
960
1082
|
pi: ExtensionAPI,
|
|
@@ -1025,7 +1147,10 @@ async function handleExtensionStatusEvent(
|
|
|
1025
1147
|
if (hookName === "agent_end") {
|
|
1026
1148
|
if (statusController.config) {
|
|
1027
1149
|
runPiNotifyEffects(
|
|
1028
|
-
|
|
1150
|
+
{
|
|
1151
|
+
...statusController.config,
|
|
1152
|
+
"notify-sound": getPiUsereqRuntimeSoundLevel(statusController),
|
|
1153
|
+
},
|
|
1029
1154
|
event as { messages: AgentEndEvent["messages"] },
|
|
1030
1155
|
notifyRequest,
|
|
1031
1156
|
);
|
|
@@ -1041,7 +1166,6 @@ async function handleExtensionStatusEvent(
|
|
|
1041
1166
|
? `ERROR: Prompt closure retained worktree ${activePromptRequest.worktreeDir} after ${outcome} outcome.`
|
|
1042
1167
|
: undefined;
|
|
1043
1168
|
const shouldFinalizeMatchedSuccess = outcome === "completed"
|
|
1044
|
-
&& statusController.state.workflowState === "running"
|
|
1045
1169
|
&& activePromptRequest.worktreeDir !== undefined;
|
|
1046
1170
|
if (debugConfig) {
|
|
1047
1171
|
logPromptWorkflowEvent(
|
|
@@ -1080,6 +1204,7 @@ async function handleExtensionStatusEvent(
|
|
|
1080
1204
|
mergeSucceeded: boolean;
|
|
1081
1205
|
cleanupSucceeded: boolean;
|
|
1082
1206
|
errorMessage?: string;
|
|
1207
|
+
warningMessage?: string;
|
|
1083
1208
|
activeContext?: unknown;
|
|
1084
1209
|
}
|
|
1085
1210
|
| undefined;
|
|
@@ -1134,6 +1259,14 @@ async function handleExtensionStatusEvent(
|
|
|
1134
1259
|
}
|
|
1135
1260
|
notifyContextSafely(promptContext, finalization.errorMessage, "error");
|
|
1136
1261
|
}
|
|
1262
|
+
if (
|
|
1263
|
+
finalization.warningMessage
|
|
1264
|
+
&& finalization.cleanupSucceeded
|
|
1265
|
+
&& finalization.mergeSucceeded
|
|
1266
|
+
&& !finalization.errorMessage
|
|
1267
|
+
) {
|
|
1268
|
+
notifyContextSafely(promptContext, finalization.warningMessage, "info");
|
|
1269
|
+
}
|
|
1137
1270
|
} else {
|
|
1138
1271
|
try {
|
|
1139
1272
|
promptContext = (await restorePromptCommandExecution(
|
|
@@ -1383,6 +1516,7 @@ function buildDebugMenuChoices(config: UseReqConfig): PiUsereqSettingsMenuChoice
|
|
|
1383
1516
|
id: "debug-enabled",
|
|
1384
1517
|
label: "Debug",
|
|
1385
1518
|
value: config.DEBUG_ENABLED,
|
|
1519
|
+
values: ["enable", "disable"],
|
|
1386
1520
|
description: "Enable or disable all debug logging behavior and unlock the remaining Debug rows.",
|
|
1387
1521
|
},
|
|
1388
1522
|
buildDebugMenuChoice(
|
|
@@ -1408,6 +1542,7 @@ function buildDebugMenuChoices(config: UseReqConfig): PiUsereqSettingsMenuChoice
|
|
|
1408
1542
|
id: "debug-status-changes",
|
|
1409
1543
|
label: "Status changes",
|
|
1410
1544
|
value: normalizeDebugStatusChanges(config.DEBUG_STATUS_CHANGES),
|
|
1545
|
+
values: ["enable", "disable"],
|
|
1411
1546
|
description: "Enable or disable `workflow_state` debug entries for prompt-orchestration transitions.",
|
|
1412
1547
|
},
|
|
1413
1548
|
debugEnabled,
|
|
@@ -1417,6 +1552,7 @@ function buildDebugMenuChoices(config: UseReqConfig): PiUsereqSettingsMenuChoice
|
|
|
1417
1552
|
id: "debug-workflow-events",
|
|
1418
1553
|
label: "Workflow events",
|
|
1419
1554
|
value: normalizeDebugWorkflowEvents(config.DEBUG_WORKFLOW_EVENTS),
|
|
1555
|
+
values: ["enable", "disable"],
|
|
1420
1556
|
description: "Enable or disable dedicated workflow debug entries for activation, restoration, closure, and session-shutdown diagnostics.",
|
|
1421
1557
|
},
|
|
1422
1558
|
debugEnabled,
|
|
@@ -1426,6 +1562,7 @@ function buildDebugMenuChoices(config: UseReqConfig): PiUsereqSettingsMenuChoice
|
|
|
1426
1562
|
id: `debug-tool:${toolName}`,
|
|
1427
1563
|
label: toolName,
|
|
1428
1564
|
value: enabledTools.has(toolName) ? "enable" : "disable",
|
|
1565
|
+
values: ["enable", "disable"],
|
|
1429
1566
|
description: PI_USEREQ_CUSTOM_TOOL_NAMES.includes(toolName as never)
|
|
1430
1567
|
? `Toggle debug logging for custom tool ${toolName}.`
|
|
1431
1568
|
: `Toggle debug logging for embedded tool ${toolName}.`,
|
|
@@ -1437,6 +1574,7 @@ function buildDebugMenuChoices(config: UseReqConfig): PiUsereqSettingsMenuChoice
|
|
|
1437
1574
|
id: `debug-prompt:${promptName}`,
|
|
1438
1575
|
label: promptName,
|
|
1439
1576
|
value: enabledPrompts.has(promptName) ? "enable" : "disable",
|
|
1577
|
+
values: ["enable", "disable"],
|
|
1440
1578
|
description: `Toggle prompt-orchestration debug logging for /${promptName}.`,
|
|
1441
1579
|
},
|
|
1442
1580
|
debugEnabled,
|
|
@@ -1464,6 +1602,58 @@ async function configureDebugMenu(
|
|
|
1464
1602
|
while (true) {
|
|
1465
1603
|
const choice = await showPiUsereqSettingsMenu(ctx, "Debug", buildDebugMenuChoices(config), {
|
|
1466
1604
|
initialSelectedId: focusedChoiceId,
|
|
1605
|
+
getChoices: () => buildDebugMenuChoices(config),
|
|
1606
|
+
onChange: (choiceId, newValue) => {
|
|
1607
|
+
if (choiceId === "debug-enabled") {
|
|
1608
|
+
config.DEBUG_ENABLED = newValue === "enable" ? "enable" : "disable";
|
|
1609
|
+
onConfigChange();
|
|
1610
|
+
ctx.ui.notify(`Debug ${config.DEBUG_ENABLED}`, "info");
|
|
1611
|
+
return;
|
|
1612
|
+
}
|
|
1613
|
+
if (choiceId === "debug-status-changes") {
|
|
1614
|
+
config.DEBUG_STATUS_CHANGES = normalizeDebugStatusChanges(newValue);
|
|
1615
|
+
onConfigChange();
|
|
1616
|
+
ctx.ui.notify(`Debug status-change logging ${config.DEBUG_STATUS_CHANGES}`, "info");
|
|
1617
|
+
return;
|
|
1618
|
+
}
|
|
1619
|
+
if (choiceId === "debug-workflow-events") {
|
|
1620
|
+
config.DEBUG_WORKFLOW_EVENTS = normalizeDebugWorkflowEvents(newValue);
|
|
1621
|
+
onConfigChange();
|
|
1622
|
+
ctx.ui.notify(`Debug workflow-event logging ${config.DEBUG_WORKFLOW_EVENTS}`, "info");
|
|
1623
|
+
return;
|
|
1624
|
+
}
|
|
1625
|
+
if (choiceId.startsWith("debug-tool:")) {
|
|
1626
|
+
const toolName = choiceId.slice("debug-tool:".length) as PiUsereqStartupToolName;
|
|
1627
|
+
const enabledTools = new Set(normalizeDebugEnabledTools(config.DEBUG_ENABLED_TOOLS));
|
|
1628
|
+
if (newValue === "enable") {
|
|
1629
|
+
enabledTools.add(toolName);
|
|
1630
|
+
} else {
|
|
1631
|
+
enabledTools.delete(toolName);
|
|
1632
|
+
}
|
|
1633
|
+
config.DEBUG_ENABLED_TOOLS = getDebugToolToggleNames().filter((name) => enabledTools.has(name));
|
|
1634
|
+
onConfigChange();
|
|
1635
|
+
ctx.ui.notify(
|
|
1636
|
+
`${newValue === "enable" ? "Enabled" : "Disabled"} debug logging for ${toolName}`,
|
|
1637
|
+
"info",
|
|
1638
|
+
);
|
|
1639
|
+
return;
|
|
1640
|
+
}
|
|
1641
|
+
if (choiceId.startsWith("debug-prompt:")) {
|
|
1642
|
+
const promptName = choiceId.slice("debug-prompt:".length) as (typeof DEBUG_PROMPT_NAMES)[number];
|
|
1643
|
+
const enabledPrompts = new Set(normalizeDebugEnabledPrompts(config.DEBUG_ENABLED_PROMPTS));
|
|
1644
|
+
if (newValue === "enable") {
|
|
1645
|
+
enabledPrompts.add(promptName);
|
|
1646
|
+
} else {
|
|
1647
|
+
enabledPrompts.delete(promptName);
|
|
1648
|
+
}
|
|
1649
|
+
config.DEBUG_ENABLED_PROMPTS = DEBUG_PROMPT_NAMES.filter((name) => enabledPrompts.has(name));
|
|
1650
|
+
onConfigChange();
|
|
1651
|
+
ctx.ui.notify(
|
|
1652
|
+
`${newValue === "enable" ? "Enabled" : "Disabled"} debug logging for ${promptName}`,
|
|
1653
|
+
"info",
|
|
1654
|
+
);
|
|
1655
|
+
}
|
|
1656
|
+
},
|
|
1467
1657
|
});
|
|
1468
1658
|
if (!choice) {
|
|
1469
1659
|
return;
|
|
@@ -1751,6 +1941,13 @@ const PI_NOTIFY_EVENT_MENU_DEFINITIONS: Record<
|
|
|
1751
1941
|
},
|
|
1752
1942
|
};
|
|
1753
1943
|
|
|
1944
|
+
/**
|
|
1945
|
+
* @brief Defines the canonical label used for persisted boot-sound menu rows.
|
|
1946
|
+
* @details Reuses one shared string literal across notification menu rows, selectors, reset previews, and tests so the persisted boot-sound terminology remains stable. Access complexity is O(1).
|
|
1947
|
+
* @satisfies REQ-149, REQ-179
|
|
1948
|
+
*/
|
|
1949
|
+
const PI_NOTIFY_BOOT_SOUND_LABEL = "Enable sound (boot value)";
|
|
1950
|
+
|
|
1754
1951
|
/**
|
|
1755
1952
|
* @brief Formats the top-level summary value for one notification event submenu.
|
|
1756
1953
|
* @details Counts enabled completed/interrupted/failed toggles for the selected transport and renders the result as `n/3 on` for right-aligned menu display. Runtime is O(1). No external state is mutated.
|
|
@@ -1806,6 +2003,7 @@ function buildPiNotifyEventMenuChoices(
|
|
|
1806
2003
|
id: eventMenu.keys[row.eventId],
|
|
1807
2004
|
label: row.label,
|
|
1808
2005
|
value: config[eventMenu.keys[row.eventId]] ? "on" : "off",
|
|
2006
|
+
values: ["on", "off"],
|
|
1809
2007
|
description: `${eventMenu.systemLabel}: ${row.description}`,
|
|
1810
2008
|
})),
|
|
1811
2009
|
...buildTerminalSettingsMenuChoices({
|
|
@@ -1870,7 +2068,23 @@ async function configurePiNotifyEventMenu(
|
|
|
1870
2068
|
ctx,
|
|
1871
2069
|
eventMenu.submenuTitle,
|
|
1872
2070
|
buildPiNotifyEventMenuChoices(config, eventMenu),
|
|
1873
|
-
{
|
|
2071
|
+
{
|
|
2072
|
+
initialSelectedId: focusedChoiceId,
|
|
2073
|
+
getChoices: () => buildPiNotifyEventMenuChoices(config, eventMenu),
|
|
2074
|
+
onChange: (choiceId, newValue) => {
|
|
2075
|
+
const enabled = newValue === "on";
|
|
2076
|
+
config[choiceId as PiNotifyEventBooleanConfigKey] = enabled;
|
|
2077
|
+
onConfigChange();
|
|
2078
|
+
const eventLabel = resolvePiNotifyEventLabel(
|
|
2079
|
+
choiceId as PiNotifyEventBooleanConfigKey,
|
|
2080
|
+
eventMenu,
|
|
2081
|
+
);
|
|
2082
|
+
ctx.ui.notify(
|
|
2083
|
+
`${eventMenu.systemLabel} ${eventLabel} ${enabled ? "enabled" : "disabled"}`,
|
|
2084
|
+
"info",
|
|
2085
|
+
);
|
|
2086
|
+
},
|
|
2087
|
+
},
|
|
1874
2088
|
);
|
|
1875
2089
|
if (!choice) {
|
|
1876
2090
|
return;
|
|
@@ -1922,7 +2136,7 @@ async function configurePiNotifyEventMenu(
|
|
|
1922
2136
|
|
|
1923
2137
|
/**
|
|
1924
2138
|
* @brief Builds the direct Pushover rows rendered inside `Notifications`.
|
|
1925
|
-
* @details Serializes the global enable flag, shared-event submenu launcher, priority, title, text, and credential rows into right-valued menu items appended after the sound-command rows, dims and disables the enable row until both credentials are populated, and escapes control characters for the single-line `Pushover text` value. Runtime is O(n) in the rendered text-template length. No external state is mutated.
|
|
2139
|
+
* @details Serializes the global enable flag, shared-event submenu launcher, priority, title, text, and credential rows into right-valued menu items appended after the sound-command rows, dims and disables the enable row until both credentials are populated, renders the locked value as `configure user/token keys first`, and escapes control characters for the single-line `Pushover text` value. Runtime is O(n) in the rendered text-template length. No external state is mutated.
|
|
1926
2140
|
* @param[in] config {UseReqConfig} Effective project configuration.
|
|
1927
2141
|
* @return {PiUsereqSettingsMenuChoice[]} Ordered direct Pushover rows.
|
|
1928
2142
|
* @satisfies REQ-163, REQ-165, REQ-172, REQ-184, REQ-185, REQ-198, REQ-234, REQ-235
|
|
@@ -1934,8 +2148,9 @@ function buildPiNotifyPushoverRows(config: UseReqConfig): PiUsereqSettingsMenuCh
|
|
|
1934
2148
|
id: "notify-pushover-enabled",
|
|
1935
2149
|
label: "Enable pushover",
|
|
1936
2150
|
labelTone: pushoverCredentialsReady ? undefined : "dim",
|
|
1937
|
-
value: pushoverCredentialsReady ? formatPiNotifyPushoverStatus(config) : "
|
|
2151
|
+
value: pushoverCredentialsReady ? formatPiNotifyPushoverStatus(config) : "configure user/token keys first",
|
|
1938
2152
|
valueTone: pushoverCredentialsReady ? undefined : "dim",
|
|
2153
|
+
values: ["on", "off"],
|
|
1939
2154
|
disabled: !pushoverCredentialsReady,
|
|
1940
2155
|
description: pushoverCredentialsReady
|
|
1941
2156
|
? "Enable or disable all Pushover delivery globally."
|
|
@@ -2018,10 +2233,10 @@ async function selectPiNotifyPushoverPriority(
|
|
|
2018
2233
|
|
|
2019
2234
|
/**
|
|
2020
2235
|
* @brief Builds the shared settings-menu choices for notification configuration.
|
|
2021
|
-
* @details Serializes command-notify, sound, and Pushover blocks with dedicated shared-event submenu launchers so the settings-menu renderer can expose one unified but modular configuration surface, including locked Pushover enablement and escaped single-line rendering for `Pushover text`. Runtime is O(n) in the longest rendered command or text field. No external state is mutated.
|
|
2236
|
+
* @details Serializes command-notify, sound, and Pushover blocks with dedicated shared-event submenu launchers so the settings-menu renderer can expose one unified but modular configuration surface, including locked Pushover enablement, persisted boot-sound rows that stay decoupled from the active runtime sound level, and escaped single-line rendering for `Pushover text`. Runtime is O(n) in the longest rendered command or text field. No external state is mutated.
|
|
2022
2237
|
* @param[in] config {UseReqConfig} Effective project configuration.
|
|
2023
2238
|
* @return {PiUsereqSettingsMenuChoice[]} Ordered notification-menu choice vector.
|
|
2024
|
-
* @satisfies REQ-137, REQ-149, REQ-150, REQ-151, REQ-152, REQ-163, REQ-164, REQ-165, REQ-172, REQ-179, REQ-181, REQ-183, REQ-188, REQ-193, REQ-198, REQ-234, REQ-235
|
|
2239
|
+
* @satisfies REQ-137, REQ-149, REQ-150, REQ-151, REQ-152, REQ-163, REQ-164, REQ-165, REQ-172, REQ-179, REQ-181, REQ-183, REQ-188, REQ-193, REQ-198, REQ-234, REQ-235, REQ-289
|
|
2025
2240
|
*/
|
|
2026
2241
|
function buildPiNotifyMenuChoices(config: UseReqConfig): PiUsereqSettingsMenuChoice[] {
|
|
2027
2242
|
return [
|
|
@@ -2029,6 +2244,7 @@ function buildPiNotifyMenuChoices(config: UseReqConfig): PiUsereqSettingsMenuCho
|
|
|
2029
2244
|
id: "notify-enabled",
|
|
2030
2245
|
label: "Enable notification",
|
|
2031
2246
|
value: formatPiNotifyStatus(config),
|
|
2247
|
+
values: ["on", "off"],
|
|
2032
2248
|
description: "Enable or disable command-notify delivery globally.",
|
|
2033
2249
|
},
|
|
2034
2250
|
buildPiNotifyEventLauncherChoice(
|
|
@@ -2043,9 +2259,9 @@ function buildPiNotifyMenuChoices(config: UseReqConfig): PiUsereqSettingsMenuCho
|
|
|
2043
2259
|
},
|
|
2044
2260
|
{
|
|
2045
2261
|
id: "selected-sound-command",
|
|
2046
|
-
label:
|
|
2262
|
+
label: PI_NOTIFY_BOOT_SOUND_LABEL,
|
|
2047
2263
|
value: config["notify-sound"],
|
|
2048
|
-
description: "
|
|
2264
|
+
description: "Edit the persisted boot sound level without changing the active runtime sound level.",
|
|
2049
2265
|
},
|
|
2050
2266
|
buildPiNotifyEventLauncherChoice(
|
|
2051
2267
|
config,
|
|
@@ -2055,25 +2271,25 @@ function buildPiNotifyMenuChoices(config: UseReqConfig): PiUsereqSettingsMenuCho
|
|
|
2055
2271
|
id: "sound-toggle-hotkey-bind",
|
|
2056
2272
|
label: "Sound toggle hotkey bind",
|
|
2057
2273
|
value: config["notify-sound-toggle-shortcut"],
|
|
2058
|
-
description: "Edit the keyboard shortcut that cycles the
|
|
2274
|
+
description: "Edit the keyboard shortcut that cycles the active runtime sound level.",
|
|
2059
2275
|
},
|
|
2060
2276
|
{
|
|
2061
2277
|
id: "sound-command-low",
|
|
2062
2278
|
label: "Sound command (low vol.)",
|
|
2063
2279
|
value: config.PI_NOTIFY_SOUND_LOW_CMD,
|
|
2064
|
-
description: "Edit the shell command used when the
|
|
2280
|
+
description: "Edit the shell command used when the active runtime sound level is `low`.",
|
|
2065
2281
|
},
|
|
2066
2282
|
{
|
|
2067
2283
|
id: "sound-command-mid",
|
|
2068
2284
|
label: "Sound command (mid vol.)",
|
|
2069
2285
|
value: config.PI_NOTIFY_SOUND_MID_CMD,
|
|
2070
|
-
description: "Edit the shell command used when the
|
|
2286
|
+
description: "Edit the shell command used when the active runtime sound level is `mid`.",
|
|
2071
2287
|
},
|
|
2072
2288
|
{
|
|
2073
2289
|
id: "sound-command-high",
|
|
2074
2290
|
label: "Sound command (high vol.)",
|
|
2075
2291
|
value: config.PI_NOTIFY_SOUND_HIGH_CMD,
|
|
2076
|
-
description: "Edit the shell command used when the
|
|
2292
|
+
description: "Edit the shell command used when the active runtime sound level is `high`.",
|
|
2077
2293
|
},
|
|
2078
2294
|
...buildPiNotifyPushoverRows(config),
|
|
2079
2295
|
...buildTerminalSettingsMenuChoices({
|
|
@@ -2083,44 +2299,44 @@ function buildPiNotifyMenuChoices(config: UseReqConfig): PiUsereqSettingsMenuCho
|
|
|
2083
2299
|
}
|
|
2084
2300
|
|
|
2085
2301
|
/**
|
|
2086
|
-
* @brief Opens the shared settings-menu selector for the
|
|
2087
|
-
* @details Reuses the pi-usereq settings-menu renderer so sound
|
|
2302
|
+
* @brief Opens the shared settings-menu selector for the persisted boot sound level.
|
|
2303
|
+
* @details Reuses the pi-usereq settings-menu renderer so boot-sound selection remains stylistically aligned with the notification menu, keeps the active runtime sound level unchanged, and appends a value-less subtree-local `Reset defaults` row. Runtime depends on user interaction count. Side effects are limited to transient custom-UI rendering.
|
|
2088
2304
|
* @param[in] ctx {ExtensionCommandContext} Active command context.
|
|
2089
|
-
* @param[in] currentLevel {PiNotifySoundLevel}
|
|
2090
|
-
* @return {Promise<PiNotifySoundLevel | "reset-defaults" | undefined>} Selected sound level, reset action, or `undefined` when cancelled.
|
|
2091
|
-
* @satisfies REQ-131, REQ-179, REQ-192
|
|
2305
|
+
* @param[in] currentLevel {PiNotifySoundLevel} Persisted boot sound level.
|
|
2306
|
+
* @return {Promise<PiNotifySoundLevel | "reset-defaults" | undefined>} Selected boot sound level, reset action, or `undefined` when cancelled.
|
|
2307
|
+
* @satisfies REQ-131, REQ-179, REQ-192, REQ-289
|
|
2092
2308
|
*/
|
|
2093
2309
|
async function selectPiNotifySoundLevel(
|
|
2094
2310
|
ctx: ExtensionCommandContext,
|
|
2095
2311
|
currentLevel: PiNotifySoundLevel,
|
|
2096
2312
|
): Promise<PiNotifySoundLevel | "reset-defaults" | undefined> {
|
|
2097
|
-
const choice = await showPiUsereqSettingsMenu(ctx,
|
|
2313
|
+
const choice = await showPiUsereqSettingsMenu(ctx, PI_NOTIFY_BOOT_SOUND_LABEL, [
|
|
2098
2314
|
{
|
|
2099
2315
|
id: "none",
|
|
2100
2316
|
label: "none",
|
|
2101
2317
|
value: currentLevel === "none" ? "selected" : "",
|
|
2102
|
-
description: "
|
|
2318
|
+
description: "Persist `none` as the boot sound level loaded during the next session start.",
|
|
2103
2319
|
},
|
|
2104
2320
|
{
|
|
2105
2321
|
id: "low",
|
|
2106
2322
|
label: "low",
|
|
2107
2323
|
value: currentLevel === "low" ? "selected" : "",
|
|
2108
|
-
description: "
|
|
2324
|
+
description: "Persist `low` as the boot sound level loaded during the next session start.",
|
|
2109
2325
|
},
|
|
2110
2326
|
{
|
|
2111
2327
|
id: "mid",
|
|
2112
2328
|
label: "mid",
|
|
2113
2329
|
value: currentLevel === "mid" ? "selected" : "",
|
|
2114
|
-
description: "
|
|
2330
|
+
description: "Persist `mid` as the boot sound level loaded during the next session start.",
|
|
2115
2331
|
},
|
|
2116
2332
|
{
|
|
2117
2333
|
id: "high",
|
|
2118
2334
|
label: "high",
|
|
2119
2335
|
value: currentLevel === "high" ? "selected" : "",
|
|
2120
|
-
description: "
|
|
2336
|
+
description: "Persist `high` as the boot sound level loaded during the next session start.",
|
|
2121
2337
|
},
|
|
2122
2338
|
...buildTerminalSettingsMenuChoices({
|
|
2123
|
-
resetDefaultsDescription: "Restore the documented default sound level.",
|
|
2339
|
+
resetDefaultsDescription: "Restore the documented default boot sound level.",
|
|
2124
2340
|
}),
|
|
2125
2341
|
], { initialSelectedId: currentLevel });
|
|
2126
2342
|
if (!choice) {
|
|
@@ -2134,11 +2350,11 @@ async function selectPiNotifySoundLevel(
|
|
|
2134
2350
|
|
|
2135
2351
|
/**
|
|
2136
2352
|
* @brief Runs the interactive notification-configuration menu.
|
|
2137
|
-
* @details Exposes command-notify, sound, and Pushover controls through the shared settings-menu renderer, delegates completed/interrupted/failed toggles to dedicated event submenus, keeps `Enable pushover` locked until both credentials are populated, decodes escaped control-sequence input for `Pushover text`, and preserves row focus across menu re-renders. Runtime depends on user interaction count. Side effects include UI updates and config mutation.
|
|
2353
|
+
* @details Exposes command-notify, sound, and Pushover controls through the shared settings-menu renderer, persists every notification subtree mutation into global configuration, delegates completed/interrupted/failed toggles to dedicated event submenus, preserves boot-sound edits without altering the active runtime sound level, keeps `Enable pushover` locked until both credentials are populated, decodes escaped control-sequence input for `Pushover text`, and preserves row focus across menu re-renders. Runtime depends on user interaction count. Side effects include UI updates and config mutation.
|
|
2138
2354
|
* @param[in] ctx {ExtensionCommandContext} Active command context.
|
|
2139
2355
|
* @param[in,out] config {UseReqConfig} Mutable configuration object.
|
|
2140
2356
|
* @return {Promise<boolean>} `true` when the sound-toggle shortcut changed.
|
|
2141
|
-
* @satisfies REQ-131, REQ-133, REQ-134, REQ-137, REQ-163, REQ-164, REQ-165, REQ-172, REQ-179, REQ-181, REQ-183, REQ-184, REQ-188, REQ-192, REQ-193, REQ-195, REQ-196, REQ-198, REQ-234, REQ-235
|
|
2357
|
+
* @satisfies REQ-131, REQ-133, REQ-134, REQ-137, REQ-163, REQ-164, REQ-165, REQ-172, REQ-179, REQ-181, REQ-183, REQ-184, REQ-188, REQ-192, REQ-193, REQ-195, REQ-196, REQ-198, REQ-234, REQ-235, REQ-288, REQ-289
|
|
2142
2358
|
*/
|
|
2143
2359
|
async function configurePiNotifyMenu(
|
|
2144
2360
|
ctx: ExtensionCommandContext,
|
|
@@ -2152,7 +2368,27 @@ async function configurePiNotifyMenu(
|
|
|
2152
2368
|
ctx,
|
|
2153
2369
|
"Notifications",
|
|
2154
2370
|
buildPiNotifyMenuChoices(config),
|
|
2155
|
-
{
|
|
2371
|
+
{
|
|
2372
|
+
initialSelectedId: focusedChoiceId,
|
|
2373
|
+
getChoices: () => buildPiNotifyMenuChoices(config),
|
|
2374
|
+
onChange: (choiceId, newValue) => {
|
|
2375
|
+
if (choiceId === "notify-enabled" || choiceId === "notify-pushover-enabled") {
|
|
2376
|
+
if (choiceId === "notify-pushover-enabled" && !hasPiNotifyPushoverCredentials(config)) {
|
|
2377
|
+
config["notify-pushover-enabled"] = false;
|
|
2378
|
+
ctx.ui.notify("Populate both Pushover credential fields before enabling Pushover", "info");
|
|
2379
|
+
return;
|
|
2380
|
+
}
|
|
2381
|
+
const enabled = newValue === "on";
|
|
2382
|
+
config[choiceId as PiNotifyBooleanConfigKey] = enabled;
|
|
2383
|
+
onConfigChange();
|
|
2384
|
+
const labelMap: Record<string, string> = {
|
|
2385
|
+
"notify-enabled": "Notification",
|
|
2386
|
+
"notify-pushover-enabled": "Pushover",
|
|
2387
|
+
};
|
|
2388
|
+
ctx.ui.notify(`${labelMap[choiceId]} ${enabled ? "enabled" : "disabled"}`, "info");
|
|
2389
|
+
}
|
|
2390
|
+
},
|
|
2391
|
+
},
|
|
2156
2392
|
);
|
|
2157
2393
|
if (!choice) {
|
|
2158
2394
|
return config["notify-sound-toggle-shortcut"] !== originalShortcut;
|
|
@@ -2216,22 +2452,22 @@ async function configurePiNotifyMenu(
|
|
|
2216
2452
|
const approved = await confirmResetChanges(
|
|
2217
2453
|
ctx,
|
|
2218
2454
|
"Confirm sound reset",
|
|
2219
|
-
[{ label:
|
|
2455
|
+
[{ label: PI_NOTIFY_BOOT_SOUND_LABEL, previousValue: config["notify-sound"], nextValue: defaultSoundLevel }]
|
|
2220
2456
|
.filter((change) => change.previousValue !== change.nextValue),
|
|
2221
|
-
"Approve restoring the documented default sound level.",
|
|
2222
|
-
"Abort the sound reset and keep the current value.",
|
|
2457
|
+
"Approve restoring the documented default boot sound level.",
|
|
2458
|
+
"Abort the boot sound reset and keep the current value.",
|
|
2223
2459
|
);
|
|
2224
2460
|
if (!approved) {
|
|
2225
|
-
ctx.ui.notify("Aborted sound reset", "info");
|
|
2461
|
+
ctx.ui.notify("Aborted boot sound reset", "info");
|
|
2226
2462
|
} else {
|
|
2227
2463
|
config["notify-sound"] = defaultSoundLevel;
|
|
2228
2464
|
onConfigChange();
|
|
2229
|
-
ctx.ui.notify("Restored default sound level", "info");
|
|
2465
|
+
ctx.ui.notify("Restored default boot sound level; active runtime sound is unchanged", "info");
|
|
2230
2466
|
}
|
|
2231
2467
|
} else if (nextLevel !== undefined) {
|
|
2232
2468
|
config["notify-sound"] = nextLevel;
|
|
2233
2469
|
onConfigChange();
|
|
2234
|
-
ctx.ui.notify(`
|
|
2470
|
+
ctx.ui.notify(`Stored ${PI_NOTIFY_BOOT_SOUND_LABEL.toLowerCase()} as ${nextLevel}; active runtime sound is unchanged`, "info");
|
|
2235
2471
|
}
|
|
2236
2472
|
continue;
|
|
2237
2473
|
}
|
|
@@ -2376,7 +2612,7 @@ async function configurePiNotifyMenu(
|
|
|
2376
2612
|
const defaults = getDefaultConfig("");
|
|
2377
2613
|
const resetPreview: ResetConfirmationChange[] = [
|
|
2378
2614
|
{ label: "Enable notification", previousValue: formatPiNotifyStatus(config), nextValue: formatPiNotifyStatus(defaults) },
|
|
2379
|
-
{ label:
|
|
2615
|
+
{ label: PI_NOTIFY_BOOT_SOUND_LABEL, previousValue: config["notify-sound"], nextValue: defaults["notify-sound"] },
|
|
2380
2616
|
{ label: "Sound toggle hotkey bind", previousValue: config["notify-sound-toggle-shortcut"], nextValue: defaults["notify-sound-toggle-shortcut"] },
|
|
2381
2617
|
{ label: "Notify command", previousValue: config.PI_NOTIFY_CMD, nextValue: defaults.PI_NOTIFY_CMD },
|
|
2382
2618
|
{ label: "Sound command (low vol.)", previousValue: config.PI_NOTIFY_SOUND_LOW_CMD, nextValue: defaults.PI_NOTIFY_SOUND_LOW_CMD },
|
|
@@ -2410,16 +2646,16 @@ async function configurePiNotifyMenu(
|
|
|
2410
2646
|
|
|
2411
2647
|
/**
|
|
2412
2648
|
* @brief Registers the configurable notification-sound shortcut when supported.
|
|
2413
|
-
* @details Loads the current
|
|
2414
|
-
* the runtime exposes `registerShortcut(...)`, cycles
|
|
2415
|
-
*
|
|
2416
|
-
* notification. Runtime is O(1) for registration
|
|
2417
|
-
* use. Side effects include shortcut
|
|
2418
|
-
* updates.
|
|
2649
|
+
* @details Loads the current effective config, registers one raw pi shortcut when
|
|
2650
|
+
* the runtime exposes `registerShortcut(...)`, cycles only the active runtime
|
|
2651
|
+
* sound level on invocation, leaves persisted local and global configuration
|
|
2652
|
+
* unchanged, refreshes the status bar, and emits one info notification. Runtime is O(1) for registration
|
|
2653
|
+
* plus one status update per shortcut use. Side effects include shortcut
|
|
2654
|
+
* registration and status updates.
|
|
2419
2655
|
* @param[in] pi {ExtensionAPI} Active extension API instance.
|
|
2420
2656
|
* @param[in,out] statusController {PiUsereqStatusController} Mutable status controller.
|
|
2421
2657
|
* @return {void} No return value.
|
|
2422
|
-
* @satisfies REQ-
|
|
2658
|
+
* @satisfies REQ-134, REQ-180, REQ-286, REQ-287
|
|
2423
2659
|
*/
|
|
2424
2660
|
function registerPiNotifyShortcut(
|
|
2425
2661
|
pi: ExtensionAPI,
|
|
@@ -2433,19 +2669,170 @@ function registerPiNotifyShortcut(
|
|
|
2433
2669
|
shortcutRegistrar.registerShortcut(config["notify-sound-toggle-shortcut"], {
|
|
2434
2670
|
description: "Cycle pi-usereq notification sound level",
|
|
2435
2671
|
handler: async (ctx) => {
|
|
2436
|
-
const
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2672
|
+
const nextRuntimeSoundLevel = cyclePiNotifySoundLevel(
|
|
2673
|
+
getPiUsereqRuntimeSoundLevel(statusController),
|
|
2674
|
+
);
|
|
2675
|
+
setPiUsereqRuntimeSoundLevel(
|
|
2676
|
+
statusController,
|
|
2677
|
+
nextRuntimeSoundLevel,
|
|
2678
|
+
ctx,
|
|
2679
|
+
);
|
|
2680
|
+
ctx.ui.notify(`pi-usereq sound:${nextRuntimeSoundLevel}`, "info");
|
|
2681
|
+
},
|
|
2682
|
+
});
|
|
2683
|
+
}
|
|
2684
|
+
|
|
2685
|
+
/**
|
|
2686
|
+
* @brief Resolves the prompt execution plan targeted by `req-reset` recovery.
|
|
2687
|
+
* @details Prefers the current in-memory active request, then the current in-memory pending request, then the process-scoped persisted prompt runtime state so the dedicated reset command can recover from same-host unclean prompt termination after session replacement. Runtime is O(1). No external state is mutated.
|
|
2688
|
+
* @param[in] statusController {PiUsereqStatusController} Mutable status controller.
|
|
2689
|
+
* @return {PromptCommandExecutionPlan | undefined} Recoverable prompt execution plan when one remains available.
|
|
2690
|
+
*/
|
|
2691
|
+
function resolveReqResetPromptRequest(
|
|
2692
|
+
statusController: PiUsereqStatusController,
|
|
2693
|
+
): PromptCommandExecutionPlan | undefined {
|
|
2694
|
+
const isWorktreeBacked = (request: PromptCommandExecutionPlan | undefined): request is PromptCommandExecutionPlan =>
|
|
2695
|
+
request?.worktreeDir !== undefined
|
|
2696
|
+
&& request.worktreeRootPath !== undefined
|
|
2697
|
+
&& request.worktreePath !== undefined;
|
|
2698
|
+
const inMemoryActiveRequest = statusController.state.activePromptRequest;
|
|
2699
|
+
if (isWorktreeBacked(inMemoryActiveRequest)) {
|
|
2700
|
+
return inMemoryActiveRequest;
|
|
2701
|
+
}
|
|
2702
|
+
const inMemoryPendingRequest = statusController.state.pendingPromptRequest;
|
|
2703
|
+
if (isWorktreeBacked(inMemoryPendingRequest)) {
|
|
2704
|
+
return inMemoryPendingRequest;
|
|
2705
|
+
}
|
|
2706
|
+
const persistedRuntimeState = readPersistedPromptCommandRuntimeState();
|
|
2707
|
+
if (isWorktreeBacked(persistedRuntimeState.activePromptRequest)) {
|
|
2708
|
+
return persistedRuntimeState.activePromptRequest;
|
|
2709
|
+
}
|
|
2710
|
+
if (isWorktreeBacked(persistedRuntimeState.pendingPromptRequest)) {
|
|
2711
|
+
return persistedRuntimeState.pendingPromptRequest;
|
|
2712
|
+
}
|
|
2713
|
+
return inMemoryActiveRequest
|
|
2714
|
+
?? inMemoryPendingRequest
|
|
2715
|
+
?? persistedRuntimeState.activePromptRequest
|
|
2716
|
+
?? persistedRuntimeState.pendingPromptRequest;
|
|
2717
|
+
}
|
|
2718
|
+
|
|
2719
|
+
/**
|
|
2720
|
+
* @brief Registers the specialized `req-reset` slash command.
|
|
2721
|
+
* @details Registers the non-agentic prompt-recovery command that accepts any current workflow state, reuses persisted prompt runtime state when available, restores the original session-backed `base-path`, force-removes matching generated worktrees plus branches, clears recoverable prompt state when restoration succeeds, and notifies pi without starting an LLM session or creating a worktree. Runtime is dominated by session restoration plus git cleanup. Side effects include command registration, status-controller mutation, active-session replacement, worktree deletion, branch deletion, and user notifications.
|
|
2722
|
+
* @param[in] pi {ExtensionAPI} Active extension API instance.
|
|
2723
|
+
* @param[in,out] statusController {PiUsereqStatusController} Mutable status controller.
|
|
2724
|
+
* @return {void} No return value.
|
|
2725
|
+
* @satisfies REQ-304, REQ-305, REQ-306, REQ-307, REQ-308, REQ-309, REQ-310, REQ-311, REQ-312, REQ-313
|
|
2726
|
+
*/
|
|
2727
|
+
function registerReqResetCommand(
|
|
2728
|
+
pi: ExtensionAPI,
|
|
2729
|
+
statusController: PiUsereqStatusController,
|
|
2730
|
+
): void {
|
|
2731
|
+
pi.registerCommand("req-reset", {
|
|
2732
|
+
description: REQ_RESET_COMMAND_DESCRIPTION,
|
|
2733
|
+
handler: async (_args, ctx) => {
|
|
2734
|
+
const promptRequest = resolveReqResetPromptRequest(statusController);
|
|
2735
|
+
const commandCwd = resolveLiveBootstrapCwd(ctx.cwd);
|
|
2736
|
+
syncContextCwdMirror(ctx, commandCwd);
|
|
2737
|
+
const projectBase = path.resolve(promptRequest?.basePath ?? getProjectBase(commandCwd));
|
|
2738
|
+
bootstrapRuntimePathState(projectBase, {
|
|
2739
|
+
gitPath: resolveRuntimeGitPath(projectBase),
|
|
2740
|
+
});
|
|
2741
|
+
const config = loadProjectConfig(projectBase);
|
|
2742
|
+
let executionPlan: ReqResetCommandPlan;
|
|
2743
|
+
let executionResult: ReqResetCommandExecutionResult;
|
|
2744
|
+
let resetContext = ctx;
|
|
2745
|
+
|
|
2746
|
+
setPiUsereqStatusConfig(statusController, config);
|
|
2747
|
+
setPiUsereqWorkflowState(statusController, "running", ctx);
|
|
2748
|
+
|
|
2749
|
+
try {
|
|
2750
|
+
executionPlan = prepareReqResetCommandExecution(projectBase, config, promptRequest);
|
|
2751
|
+
executionResult = await executeReqResetCommandExecution(executionPlan, ctx);
|
|
2752
|
+
resetContext = (executionResult.activeContext ?? resetContext) as typeof ctx;
|
|
2753
|
+
} catch (error) {
|
|
2754
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2755
|
+
setPiUsereqWorkflowState(statusController, "error", resetContext);
|
|
2756
|
+
notifyContextSafely(resetContext, message, "error");
|
|
2757
|
+
throw error;
|
|
2758
|
+
}
|
|
2759
|
+
|
|
2760
|
+
const shouldClearPromptState = executionResult.restoredBasePath
|
|
2761
|
+
|| executionPlan.promptRequest === undefined;
|
|
2762
|
+
if (shouldClearPromptState) {
|
|
2763
|
+
statusController.state.pendingPromptRequest = undefined;
|
|
2764
|
+
statusController.state.activePromptRequest = undefined;
|
|
2765
|
+
clearPersistedPromptCommandRuntimeState();
|
|
2766
|
+
}
|
|
2767
|
+
|
|
2768
|
+
if (executionResult.errorMessage) {
|
|
2769
|
+
setPiUsereqWorkflowState(statusController, "error", resetContext);
|
|
2770
|
+
notifyContextSafely(resetContext, executionResult.errorMessage, "error");
|
|
2771
|
+
throw new ReqError(executionResult.errorMessage, 1);
|
|
2772
|
+
}
|
|
2773
|
+
|
|
2774
|
+
setPiUsereqWorkflowState(statusController, "idle", resetContext);
|
|
2775
|
+
const successMessage = executionPlan.promptRequest !== undefined
|
|
2776
|
+
? `SUCCESS: req-reset restored base-path and removed ${executionResult.removedWorktreeDirs.length} worktree(s) plus ${executionResult.removedBranchNames.length} branch(es).`
|
|
2777
|
+
: `SUCCESS: req-reset removed ${executionResult.removedWorktreeDirs.length} worktree(s) plus ${executionResult.removedBranchNames.length} branch(es) and restored idle state.`;
|
|
2778
|
+
notifyContextSafely(resetContext, successMessage, "info");
|
|
2442
2779
|
},
|
|
2443
2780
|
});
|
|
2444
2781
|
}
|
|
2445
2782
|
|
|
2446
2783
|
/**
|
|
2447
|
-
* @brief Registers
|
|
2448
|
-
* @details
|
|
2784
|
+
* @brief Registers the specialized `req-references` slash command.
|
|
2785
|
+
* @details Registers the non-agentic references-maintenance command that rejects non-`idle` invocations by transitioning workflow state to `error` before direct execution, otherwise reuses slash-command-owned git validation, transitions workflow state through `checking|running|idle`, regenerates `REFERENCES.md` directly from configured source directories, stages only the generated file, creates the fixed-message git commit, verifies repository cleanliness, and notifies pi without starting an LLM session or creating a worktree. Runtime is dominated by git subprocess execution plus source-summary generation. Side effects include command registration, status-controller mutation, filesystem writes, git index/history mutation, and user notifications.
|
|
2786
|
+
* @param[in] pi {ExtensionAPI} Active extension API instance.
|
|
2787
|
+
* @param[in,out] statusController {PiUsereqStatusController} Mutable status controller.
|
|
2788
|
+
* @return {void} No return value.
|
|
2789
|
+
* @satisfies REQ-200, REQ-221, REQ-224, REQ-298, REQ-299, REQ-300, REQ-301, REQ-302, REQ-303
|
|
2790
|
+
*/
|
|
2791
|
+
function registerReqReferencesCommand(
|
|
2792
|
+
pi: ExtensionAPI,
|
|
2793
|
+
statusController: PiUsereqStatusController,
|
|
2794
|
+
): void {
|
|
2795
|
+
pi.registerCommand("req-references", {
|
|
2796
|
+
description: REQ_REFERENCES_COMMAND_DESCRIPTION,
|
|
2797
|
+
handler: async (_args, ctx) => {
|
|
2798
|
+
if (statusController.state.workflowState !== "idle") {
|
|
2799
|
+
rejectNonIdleReqCommand(statusController, ctx);
|
|
2800
|
+
}
|
|
2801
|
+
const commandCwd = resolveLiveBootstrapCwd(ctx.cwd);
|
|
2802
|
+
syncContextCwdMirror(ctx, commandCwd);
|
|
2803
|
+
bootstrapRuntimePathState(commandCwd, {
|
|
2804
|
+
gitPath: resolveRuntimeGitPath(commandCwd),
|
|
2805
|
+
});
|
|
2806
|
+
const projectBase = getProjectBase(commandCwd);
|
|
2807
|
+
const config = loadProjectConfig(commandCwd);
|
|
2808
|
+
setPiUsereqStatusConfig(statusController, config);
|
|
2809
|
+
statusController.state.pendingPromptRequest = undefined;
|
|
2810
|
+
statusController.state.activePromptRequest = undefined;
|
|
2811
|
+
setPiUsereqWorkflowState(statusController, "checking", ctx);
|
|
2812
|
+
try {
|
|
2813
|
+
const executionPlan = prepareReqReferencesCommandExecution(projectBase, config);
|
|
2814
|
+
setPiUsereqWorkflowState(statusController, "running", ctx);
|
|
2815
|
+
executeReqReferencesCommandExecution(executionPlan, config);
|
|
2816
|
+
setPiUsereqWorkflowState(statusController, "idle", ctx);
|
|
2817
|
+
ctx.ui.notify(
|
|
2818
|
+
`SUCCESS: Updated ${formatRuntimePathForDisplay(executionPlan.referencesPath)} and committed changes.`,
|
|
2819
|
+
"info",
|
|
2820
|
+
);
|
|
2821
|
+
} catch (error) {
|
|
2822
|
+
statusController.state.pendingPromptRequest = undefined;
|
|
2823
|
+
statusController.state.activePromptRequest = undefined;
|
|
2824
|
+
setPiUsereqWorkflowState(statusController, "error", ctx);
|
|
2825
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2826
|
+
ctx.ui.notify(message, "error");
|
|
2827
|
+
throw error;
|
|
2828
|
+
}
|
|
2829
|
+
},
|
|
2830
|
+
});
|
|
2831
|
+
}
|
|
2832
|
+
|
|
2833
|
+
/**
|
|
2834
|
+
* @brief Registers bundled prompt-backed commands with the extension.
|
|
2835
|
+
* @details Creates one prompt-template-backed `req-<prompt>` command per bundled prompt name. Each handler rejects non-`idle` workflow state by transitioning the shared workflow state to `error` before command-side preflight, otherwise transitions the shared workflow state through `checking`, `error`, and `running`, runs dedicated prompt-command git and required-doc preflight checks, optionally prepares a dedicated worktree execution plan using the active session directory, persists the prompt metadata needed for switch-triggered rebinding, switches the active session to the verified execution cwd before prompt handoff, logs dedicated workflow-activation diagnostics, renders the prompt, starts prompt delivery into the forked active session, records `running` immediately after delivery handoff begins, and then awaits the wrapped prompt-delivery promise whose stale post-restore rejections are suppressed. Runtime is O(p) for registration; handler cost depends on prompt preflight, worktree preparation, session switching, prompt rendering, prompt dispatch, and optional debug logging. Side effects include command registration, status-controller mutation, worktree creation, active-session replacement, optional worktree rollback, user-message delivery during execution, and optional debug-log writes.
|
|
2449
2836
|
* @param[in] pi {ExtensionAPI} Active extension API instance.
|
|
2450
2837
|
* @param[in,out] statusController {PiUsereqStatusController} Mutable status controller.
|
|
2451
2838
|
* @return {void} No return value.
|
|
@@ -2460,9 +2847,7 @@ function registerPromptCommands(
|
|
|
2460
2847
|
description: resolvePromptCommandDescription(promptName),
|
|
2461
2848
|
handler: async (args, ctx) => {
|
|
2462
2849
|
if (statusController.state.workflowState !== "idle") {
|
|
2463
|
-
|
|
2464
|
-
ctx.ui.notify(message, "error");
|
|
2465
|
-
throw new ReqError(message, 1);
|
|
2850
|
+
rejectNonIdleReqCommand(statusController, ctx, promptName);
|
|
2466
2851
|
}
|
|
2467
2852
|
const commandCwd = resolveLiveBootstrapCwd(ctx.cwd);
|
|
2468
2853
|
syncContextCwdMirror(ctx, commandCwd);
|
|
@@ -2577,10 +2962,10 @@ function registerPromptCommands(
|
|
|
2577
2962
|
* @details Defines the tool schemas, prompt metadata, and execution handlers that bridge extension tool calls into tool-runner operations without registering duplicate custom slash commands for the same capabilities. Runtime is O(t) for registration; execution cost depends on the selected tool. Side effects include tool registration.
|
|
2578
2963
|
* @param[in] pi {ExtensionAPI} Active extension API instance.
|
|
2579
2964
|
* @return {void} No return value.
|
|
2580
|
-
* @satisfies REQ-005, REQ-010, REQ-011, REQ-014, REQ-017, REQ-044, REQ-069, REQ-070, REQ-071, REQ-072, REQ-073, REQ-074, REQ-075, REQ-076, REQ-077, REQ-078, REQ-079, REQ-080, REQ-089, REQ-090, REQ-091, REQ-092, REQ-093, REQ-094, REQ-095, REQ-096, REQ-097, REQ-098, REQ-099, REQ-100, REQ-101, REQ-102
|
|
2965
|
+
* @satisfies REQ-005, REQ-010, REQ-011, REQ-014, REQ-017, REQ-044, REQ-069, REQ-070, REQ-071, REQ-072, REQ-073, REQ-074, REQ-075, REQ-076, REQ-077, REQ-078, REQ-079, REQ-080, REQ-089, REQ-090, REQ-091, REQ-092, REQ-093, REQ-094, REQ-095, REQ-096, REQ-097, REQ-098, REQ-099, REQ-100, REQ-101, REQ-102, REQ-293, REQ-294, REQ-295, REQ-296, REQ-297
|
|
2581
2966
|
*/
|
|
2582
2967
|
function registerAgentTools(pi: ExtensionAPI): void {
|
|
2583
|
-
const
|
|
2968
|
+
const filesSummarizeSchema = Type.Object(
|
|
2584
2969
|
{
|
|
2585
2970
|
files: Type.Array(
|
|
2586
2971
|
Type.String({ description: "Project-relative or absolute source file path resolved from the current working directory when not already absolute" }),
|
|
@@ -2632,21 +3017,21 @@ function registerAgentTools(pi: ExtensionAPI): void {
|
|
|
2632
3017
|
});
|
|
2633
3018
|
|
|
2634
3019
|
pi.registerTool({
|
|
2635
|
-
name: "files-
|
|
2636
|
-
label: "files-
|
|
2637
|
-
description: "Scope: explicit source files. Return the monolithic
|
|
2638
|
-
promptSnippet: "Return the monolithic
|
|
3020
|
+
name: "files-summarize",
|
|
3021
|
+
label: "files-summarize",
|
|
3022
|
+
description: "Scope: explicit source files. Return the monolithic summary markdown report in content[0].text and keep only execution metadata in details.execution.",
|
|
3023
|
+
promptSnippet: "Return the monolithic summary markdown report for caller-selected source files.",
|
|
2639
3024
|
promptGuidelines: [
|
|
2640
3025
|
"Scope: explicit source files selected by files[]; caller order is preserved; each item may be project-relative or absolute.",
|
|
2641
3026
|
"Output contract: monolithic markdown in content[0].text; details.execution preserves only exit code and residual diagnostics.",
|
|
2642
|
-
"Formatting contract: content matches the Python
|
|
3027
|
+
"Formatting contract: content matches the Python summary renderer used by `generate_markdown.py`.",
|
|
2643
3028
|
"Behavior contract: missing inputs, non-file inputs, unsupported extensions, and analysis failures surface through details.execution diagnostics.",
|
|
2644
3029
|
],
|
|
2645
|
-
renderResult: buildStructuredToolRenderResult("files-
|
|
2646
|
-
parameters:
|
|
3030
|
+
renderResult: buildStructuredToolRenderResult("files-summarize"),
|
|
3031
|
+
parameters: filesSummarizeSchema,
|
|
2647
3032
|
async execute(_toolCallId, params) {
|
|
2648
3033
|
const contextPath = getRuntimeContextPath(process.cwd());
|
|
2649
|
-
return executeMonolithicTool(() =>
|
|
3034
|
+
return executeMonolithicTool(() => runFilesSummarize(params.files, contextPath));
|
|
2650
3035
|
},
|
|
2651
3036
|
});
|
|
2652
3037
|
|
|
@@ -2717,12 +3102,18 @@ function registerAgentTools(pi: ExtensionAPI): void {
|
|
|
2717
3102
|
},
|
|
2718
3103
|
});
|
|
2719
3104
|
|
|
2720
|
-
const
|
|
3105
|
+
const summarizeSchema = Type.Object(
|
|
2721
3106
|
{},
|
|
2722
3107
|
{
|
|
2723
3108
|
description: "Input contract: no params. Scope is the configured src-dir list resolved from the current project configuration. Output contract: monolithic markdown in content[0].text plus details.execution diagnostics.",
|
|
2724
3109
|
},
|
|
2725
3110
|
);
|
|
3111
|
+
const referencesSchema = Type.Object(
|
|
3112
|
+
{},
|
|
3113
|
+
{
|
|
3114
|
+
description: "Input contract: no params. Scope is the configured src-dir list plus configured docs-dir resolved from the current project configuration. Output contract: content[0].text returns only `success` or `error: <diagnostic>`; the tool overwrites `<docs-dir>/REFERENCES.md` and keeps details.execution diagnostics.",
|
|
3115
|
+
},
|
|
3116
|
+
);
|
|
2726
3117
|
const tokensSchema = Type.Object(
|
|
2727
3118
|
{},
|
|
2728
3119
|
{
|
|
@@ -2731,23 +3122,44 @@ function registerAgentTools(pi: ExtensionAPI): void {
|
|
|
2731
3122
|
);
|
|
2732
3123
|
|
|
2733
3124
|
pi.registerTool({
|
|
2734
|
-
name: "
|
|
2735
|
-
label: "
|
|
2736
|
-
description: "Scope: configured project source directories. Return the monolithic
|
|
2737
|
-
promptSnippet: "Return the monolithic project
|
|
3125
|
+
name: "summarize",
|
|
3126
|
+
label: "summarize",
|
|
3127
|
+
description: "Scope: configured project source directories. Return the monolithic summary markdown report in content[0].text and keep only execution metadata in details.execution.",
|
|
3128
|
+
promptSnippet: "Return the monolithic project summary markdown report from the configured source directories.",
|
|
2738
3129
|
promptGuidelines: [
|
|
2739
3130
|
"Scope: no params; resolve src-dir from the current project configuration and scan the configured source surface from the current working directory.",
|
|
2740
3131
|
"Output contract: monolithic markdown in content[0].text; details.execution preserves only exit code and residual diagnostics.",
|
|
2741
3132
|
"Formatting contract: content prepends the file-structure markdown block before the per-file markdown produced by `generate_markdown.py`.",
|
|
2742
3133
|
"Configuration contract: output changes with cwd-derived project config and src-dir values; the tool does not accept explicit file overrides.",
|
|
2743
3134
|
],
|
|
3135
|
+
renderResult: buildStructuredToolRenderResult("summarize"),
|
|
3136
|
+
parameters: summarizeSchema,
|
|
3137
|
+
async execute() {
|
|
3138
|
+
const contextPath = getRuntimeContextPath(process.cwd());
|
|
3139
|
+
const projectBase = getProjectBase(contextPath);
|
|
3140
|
+
const config = loadProjectConfig(projectBase);
|
|
3141
|
+
return executeMonolithicTool(() => runSummarize(contextPath, config));
|
|
3142
|
+
},
|
|
3143
|
+
});
|
|
3144
|
+
|
|
3145
|
+
pi.registerTool({
|
|
3146
|
+
name: "references",
|
|
3147
|
+
label: "references",
|
|
3148
|
+
description: "Scope: configured project source directories and configured docs-dir. Overwrite REFERENCES.md and return only success or error in content[0].text while keeping execution metadata in details.execution.",
|
|
3149
|
+
promptSnippet: "Generate REFERENCES.md from the configured source directories without returning the generated markdown.",
|
|
3150
|
+
promptGuidelines: [
|
|
3151
|
+
"Scope: no params; resolve src-dir and docs-dir from the current project configuration and current working directory.",
|
|
3152
|
+
"Behavior contract: generate the same file-structure-plus-summary markdown as `summarize` and overwrite `<docs-dir>/REFERENCES.md`.",
|
|
3153
|
+
"Output contract: `content[0].text` is `success` on success or `error: <diagnostic>` on failure; generated markdown is never returned to the LLM.",
|
|
3154
|
+
"Failure contract: source discovery, markdown generation, and file-write errors surface through the status text plus details.execution diagnostics.",
|
|
3155
|
+
],
|
|
2744
3156
|
renderResult: buildStructuredToolRenderResult("references"),
|
|
2745
3157
|
parameters: referencesSchema,
|
|
2746
3158
|
async execute() {
|
|
2747
3159
|
const contextPath = getRuntimeContextPath(process.cwd());
|
|
2748
3160
|
const projectBase = getProjectBase(contextPath);
|
|
2749
3161
|
const config = loadProjectConfig(projectBase);
|
|
2750
|
-
return
|
|
3162
|
+
return executeStatusTool(() => runReferences(contextPath, config));
|
|
2751
3163
|
},
|
|
2752
3164
|
});
|
|
2753
3165
|
|
|
@@ -2938,6 +3350,7 @@ function buildPiUsereqToolToggleChoices(pi: ExtensionAPI, config: UseReqConfig):
|
|
|
2938
3350
|
id: tool.name,
|
|
2939
3351
|
label: tool.name,
|
|
2940
3352
|
value: enabledTools.has(tool.name) ? "on" : "off",
|
|
3353
|
+
values: ["on", "off"],
|
|
2941
3354
|
description: tool.description ?? `Toggle startup activation for ${tool.name}.`,
|
|
2942
3355
|
})),
|
|
2943
3356
|
...buildTerminalSettingsMenuChoices({
|
|
@@ -2948,7 +3361,7 @@ function buildPiUsereqToolToggleChoices(pi: ExtensionAPI, config: UseReqConfig):
|
|
|
2948
3361
|
|
|
2949
3362
|
/**
|
|
2950
3363
|
* @brief Runs the interactive active-tool configuration menu.
|
|
2951
|
-
* @details Synchronizes runtime active tools with
|
|
3364
|
+
* @details Synchronizes runtime active tools with the effective config, renders startup-tool actions through the shared settings-menu UI, persists enablement changes into global configuration, preserves the documented per-tool ordering, and updates configuration state in response to selections until the user exits. Runtime depends on user interaction count. Side effects include UI updates, active-tool changes, and config mutation.
|
|
2952
3365
|
* @param[in] pi {ExtensionAPI} Active extension API instance.
|
|
2953
3366
|
* @param[in] ctx {ExtensionCommandContext} Active command context.
|
|
2954
3367
|
* @param[in,out] config {UseReqConfig} Mutable configuration object.
|
|
@@ -3012,7 +3425,29 @@ async function configurePiUsereqToolsMenu(
|
|
|
3012
3425
|
}
|
|
3013
3426
|
|
|
3014
3427
|
if (choice === "enable-tools") {
|
|
3015
|
-
const selectedToolName = await showPiUsereqSettingsMenu(ctx, "Enable tools", buildPiUsereqToolToggleChoices(pi, config)
|
|
3428
|
+
const selectedToolName = await showPiUsereqSettingsMenu(ctx, "Enable tools", buildPiUsereqToolToggleChoices(pi, config), {
|
|
3429
|
+
getChoices: () => buildPiUsereqToolToggleChoices(pi, config),
|
|
3430
|
+
onChange: (toolName, newValue) => {
|
|
3431
|
+
const enabledTools = new Set(getConfiguredEnabledPiUsereqTools(config));
|
|
3432
|
+
if (newValue === "on") {
|
|
3433
|
+
enabledTools.add(toolName as PiUsereqStartupToolName);
|
|
3434
|
+
} else {
|
|
3435
|
+
enabledTools.delete(toolName as PiUsereqStartupToolName);
|
|
3436
|
+
}
|
|
3437
|
+
setConfiguredPiUsereqTools(
|
|
3438
|
+
pi,
|
|
3439
|
+
config,
|
|
3440
|
+
getPiUsereqStartupTools(pi)
|
|
3441
|
+
.map((tool) => tool.name)
|
|
3442
|
+
.filter((currentToolName) => enabledTools.has(currentToolName)),
|
|
3443
|
+
);
|
|
3444
|
+
onConfigChange();
|
|
3445
|
+
ctx.ui.notify(
|
|
3446
|
+
`${newValue === "on" ? "Enabled" : "Disabled"} ${toolName}`,
|
|
3447
|
+
"info",
|
|
3448
|
+
);
|
|
3449
|
+
},
|
|
3450
|
+
});
|
|
3016
3451
|
if (!selectedToolName) {
|
|
3017
3452
|
continue;
|
|
3018
3453
|
}
|
|
@@ -3138,6 +3573,7 @@ function buildStaticCheckMenuChoices(config: UseReqConfig): PiUsereqSettingsMenu
|
|
|
3138
3573
|
id: `toggle-static-check-language:${language}`,
|
|
3139
3574
|
label: language,
|
|
3140
3575
|
value: languageConfig.enabled === "enable" ? "on" : "off",
|
|
3576
|
+
values: ["on", "off"],
|
|
3141
3577
|
description: `Toggle static-check execution for ${language}. Configured ${configuredCount} ${suffix}. Supported extensions: ${extensions.join(", ")}.`,
|
|
3142
3578
|
};
|
|
3143
3579
|
}),
|
|
@@ -3199,7 +3635,7 @@ function buildConfiguredStaticCheckLanguageChoices(config: UseReqConfig): PiUser
|
|
|
3199
3635
|
|
|
3200
3636
|
/**
|
|
3201
3637
|
* @brief Runs the interactive static-check configuration menu.
|
|
3202
|
-
* @details Lets the user add
|
|
3638
|
+
* @details Lets the user add and remove global Command entries, toggle direct local per-language enable flags, and reset the subtree to documented defaults through the shared settings-menu renderer until the user exits. Runtime depends on user interaction count. Side effects include UI updates and config mutation.
|
|
3203
3639
|
* @param[in] ctx {ExtensionCommandContext} Active command context.
|
|
3204
3640
|
* @param[in,out] config {UseReqConfig} Mutable configuration object.
|
|
3205
3641
|
* @return {Promise<void>} Promise resolved when the menu closes.
|
|
@@ -3214,6 +3650,19 @@ async function configureStaticCheckMenu(
|
|
|
3214
3650
|
while (true) {
|
|
3215
3651
|
const staticChoice = await showPiUsereqSettingsMenu(ctx, "Language static code checkers", buildStaticCheckMenuChoices(config), {
|
|
3216
3652
|
initialSelectedId: focusedChoiceId,
|
|
3653
|
+
getChoices: () => buildStaticCheckMenuChoices(config),
|
|
3654
|
+
onChange: (choiceId, newValue) => {
|
|
3655
|
+
if (choiceId.startsWith("toggle-static-check-language:")) {
|
|
3656
|
+
const language = choiceId.slice("toggle-static-check-language:".length);
|
|
3657
|
+
config["static-check"][language] ??= createStaticCheckLanguageConfig([]);
|
|
3658
|
+
config["static-check"][language]!.enabled = newValue === "on" ? "enable" : "disable";
|
|
3659
|
+
onConfigChange();
|
|
3660
|
+
ctx.ui.notify(
|
|
3661
|
+
`${newValue === "on" ? "Enabled" : "Disabled"} static-check for ${language}`,
|
|
3662
|
+
"info",
|
|
3663
|
+
);
|
|
3664
|
+
}
|
|
3665
|
+
},
|
|
3217
3666
|
});
|
|
3218
3667
|
|
|
3219
3668
|
if (!staticChoice) {
|
|
@@ -3342,11 +3791,11 @@ async function configureStaticCheckMenu(
|
|
|
3342
3791
|
|
|
3343
3792
|
/**
|
|
3344
3793
|
* @brief Builds the shared settings-menu choices for the top-level pi-usereq configuration UI.
|
|
3345
|
-
* @details Serializes primary configuration actions into right-valued menu rows consumed by the shared settings-menu renderer, including automatic git-commit mode, effective prompt-command worktree state, notification summary, debug summary, locked worktree rows when automatic git commit is disabled, and
|
|
3794
|
+
* @details Serializes primary configuration actions into right-valued menu rows consumed by the shared settings-menu renderer, including automatic git-commit mode, effective prompt-command worktree state, notification summary, debug summary, locked worktree rows when automatic git commit is disabled, and display-only local plus global config paths. Runtime is O(s) in source-directory count. No external state is mutated.
|
|
3346
3795
|
* @param[in] cwd {string} Current working directory.
|
|
3347
3796
|
* @param[in] config {UseReqConfig} Effective project configuration.
|
|
3348
3797
|
* @return {PiUsereqSettingsMenuChoice[]} Ordered top-level menu choices.
|
|
3349
|
-
* @satisfies REQ-006, REQ-031, REQ-137, REQ-150, REQ-151, REQ-152, REQ-162, REQ-190, REQ-191, REQ-197, REQ-204, REQ-205, REQ-212, REQ-215, REQ-216, REQ-236, REQ-237, REQ-238, REQ-239, REQ-240
|
|
3798
|
+
* @satisfies REQ-006, REQ-031, REQ-137, REQ-150, REQ-151, REQ-152, REQ-162, REQ-190, REQ-191, REQ-197, REQ-204, REQ-205, REQ-212, REQ-215, REQ-216, REQ-236, REQ-237, REQ-238, REQ-239, REQ-240, REQ-314, REQ-318, REQ-319, REQ-320
|
|
3350
3799
|
*/
|
|
3351
3800
|
function buildPiUsereqMenuChoices(
|
|
3352
3801
|
cwd: string,
|
|
@@ -3380,6 +3829,7 @@ function buildPiUsereqMenuChoices(
|
|
|
3380
3829
|
id: "auto-git-commit",
|
|
3381
3830
|
label: "Auto git commit",
|
|
3382
3831
|
value: config.AUTO_GIT_COMMIT,
|
|
3832
|
+
values: ["enable", "disable"],
|
|
3383
3833
|
description: "Select bundled `git_commit.md` or `git_read-only.md` for `%%COMMIT%%`; disabling also forces prompt-command worktrees off.",
|
|
3384
3834
|
},
|
|
3385
3835
|
{
|
|
@@ -3388,6 +3838,8 @@ function buildPiUsereqMenuChoices(
|
|
|
3388
3838
|
labelTone: autoGitCommitDisabled ? "dim" : undefined,
|
|
3389
3839
|
value: effectiveGitWorktreeEnabled,
|
|
3390
3840
|
valueTone: autoGitCommitDisabled ? "dim" : undefined,
|
|
3841
|
+
values: ["enable", "disable"],
|
|
3842
|
+
disabled: autoGitCommitDisabled,
|
|
3391
3843
|
description: autoGitCommitDisabled
|
|
3392
3844
|
? "Forced to `disable` while `Auto git commit` is disabled."
|
|
3393
3845
|
: "Enable or disable prompt-command worktree orchestration.",
|
|
@@ -3406,32 +3858,39 @@ function buildPiUsereqMenuChoices(
|
|
|
3406
3858
|
id: "static-check",
|
|
3407
3859
|
label: "Language static code checkers",
|
|
3408
3860
|
value: formatStaticCheckLanguagesSummary(config),
|
|
3409
|
-
description: "Manage
|
|
3861
|
+
description: "Manage global Command static-check entries and local per-language enable flags.",
|
|
3410
3862
|
},
|
|
3411
3863
|
{
|
|
3412
3864
|
id: "startup-tools",
|
|
3413
3865
|
label: "Enable tools",
|
|
3414
3866
|
value: `${getConfiguredEnabledPiUsereqTools(config).length} enabled`,
|
|
3415
|
-
description: "Manage
|
|
3867
|
+
description: "Manage the global configurable tool set activated during session_start.",
|
|
3416
3868
|
},
|
|
3417
3869
|
{
|
|
3418
3870
|
id: "notifications",
|
|
3419
3871
|
label: "Notifications",
|
|
3420
3872
|
value: `notification:${formatPiNotifyStatus(config)} • sound:${config["notify-sound"]} • pushover:${formatPiNotifyPushoverStatus(config)}`,
|
|
3421
|
-
description: "Manage command-notify, sound, and Pushover settings with dedicated event submenus.",
|
|
3873
|
+
description: "Manage global command-notify, sound, and Pushover settings with dedicated event submenus.",
|
|
3422
3874
|
},
|
|
3423
3875
|
{
|
|
3424
3876
|
id: "debug",
|
|
3425
3877
|
label: "Debug",
|
|
3426
3878
|
value: formatDebugMenuSummary(config),
|
|
3427
|
-
description: "Manage debug logging for tools and `req-*` prompt orchestration.",
|
|
3879
|
+
description: "Manage project-local debug logging for tools and `req-*` prompt orchestration.",
|
|
3880
|
+
},
|
|
3881
|
+
{
|
|
3882
|
+
id: "show-local-config",
|
|
3883
|
+
label: "Show local configuration",
|
|
3884
|
+
value: formatLocalConfigPathForMenu(cwd),
|
|
3885
|
+
valueTone: "dim",
|
|
3886
|
+
description: "Persist pending configuration changes and write the exact local config file text into the editor.",
|
|
3428
3887
|
},
|
|
3429
3888
|
{
|
|
3430
|
-
id: "show-config",
|
|
3431
|
-
label: "Show configuration",
|
|
3432
|
-
value:
|
|
3889
|
+
id: "show-global-config",
|
|
3890
|
+
label: "Show global configuration",
|
|
3891
|
+
value: formatGlobalConfigPathForMenu(),
|
|
3433
3892
|
valueTone: "dim",
|
|
3434
|
-
description: "Persist
|
|
3893
|
+
description: "Persist pending configuration changes and write the exact global config file text into the editor.",
|
|
3435
3894
|
},
|
|
3436
3895
|
...buildTerminalSettingsMenuChoices({
|
|
3437
3896
|
resetDefaultsDescription: "Restore the default pi-usereq configuration for the current project base.",
|
|
@@ -3489,12 +3948,12 @@ function buildSrcDirRemovalChoices(config: UseReqConfig): PiUsereqSettingsMenuCh
|
|
|
3489
3948
|
|
|
3490
3949
|
/**
|
|
3491
3950
|
* @brief Runs the top-level pi-usereq configuration menu.
|
|
3492
|
-
* @details Loads
|
|
3951
|
+
* @details Loads the effective merged config, exposes docs/test/source/automatic-commit/worktree/static-check/startup-tool/notification/debug actions through the shared settings-menu renderer, forces worktree disablement when automatic git commit is disabled, prevents locked row edits, persists changes on exit, closes immediately after `Show local configuration` or `Show global configuration`, and refreshes the single-line status bar. Runtime depends on user interaction count. Side effects include UI updates, config writes, active-tool changes, and editor text updates.
|
|
3493
3952
|
* @param[in] pi {ExtensionAPI} Active extension API instance.
|
|
3494
3953
|
* @param[in] ctx {ExtensionCommandContext} Active command context.
|
|
3495
3954
|
* @param[in,out] statusController {PiUsereqStatusController} Mutable status controller.
|
|
3496
3955
|
* @return {Promise<void>} Promise resolved when configuration is saved and the menu closes.
|
|
3497
|
-
* @satisfies REQ-006, REQ-031, REQ-137, REQ-150, REQ-151, REQ-152, REQ-153, REQ-154, REQ-162, REQ-190, REQ-191, REQ-192, REQ-194, REQ-195, REQ-204, REQ-205, REQ-212, REQ-215, REQ-216, REQ-236, REQ-237, REQ-238, REQ-239, REQ-240, REQ-241, REQ-242, REQ-243
|
|
3956
|
+
* @satisfies REQ-006, REQ-031, REQ-137, REQ-150, REQ-151, REQ-152, REQ-153, REQ-154, REQ-162, REQ-190, REQ-191, REQ-192, REQ-194, REQ-195, REQ-204, REQ-205, REQ-212, REQ-215, REQ-216, REQ-236, REQ-237, REQ-238, REQ-239, REQ-240, REQ-241, REQ-242, REQ-243, REQ-314, REQ-318, REQ-319, REQ-320
|
|
3498
3957
|
*/
|
|
3499
3958
|
async function configurePiUsereq(
|
|
3500
3959
|
pi: ExtensionAPI,
|
|
@@ -3520,7 +3979,36 @@ async function configurePiUsereq(
|
|
|
3520
3979
|
ctx,
|
|
3521
3980
|
"pi-usereq",
|
|
3522
3981
|
buildPiUsereqMenuChoices(ctx.cwd, config),
|
|
3523
|
-
{
|
|
3982
|
+
{
|
|
3983
|
+
initialSelectedId: focusedChoiceId,
|
|
3984
|
+
getChoices: () => buildPiUsereqMenuChoices(ctx.cwd, config),
|
|
3985
|
+
onChange: (choiceId, newValue) => {
|
|
3986
|
+
if (choiceId === "auto-git-commit") {
|
|
3987
|
+
config.AUTO_GIT_COMMIT = newValue === "enable" ? "enable" : "disable";
|
|
3988
|
+
if (config.AUTO_GIT_COMMIT === "disable") {
|
|
3989
|
+
config.GIT_WORKTREE_ENABLED = "disable";
|
|
3990
|
+
persistConfigChange();
|
|
3991
|
+
ctx.ui.notify("Auto git commit disabled; Git worktree forced off", "info");
|
|
3992
|
+
} else {
|
|
3993
|
+
persistConfigChange();
|
|
3994
|
+
ctx.ui.notify("Auto git commit enabled", "info");
|
|
3995
|
+
}
|
|
3996
|
+
return;
|
|
3997
|
+
}
|
|
3998
|
+
if (choiceId === "git-worktree-enabled") {
|
|
3999
|
+
if (config.AUTO_GIT_COMMIT === "disable") {
|
|
4000
|
+
ctx.ui.notify("Git worktree is locked while Auto git commit is disabled", "info");
|
|
4001
|
+
return;
|
|
4002
|
+
}
|
|
4003
|
+
config.GIT_WORKTREE_ENABLED = newValue === "enable" ? "enable" : "disable";
|
|
4004
|
+
persistConfigChange();
|
|
4005
|
+
ctx.ui.notify(
|
|
4006
|
+
`Git worktree ${resolveEffectiveGitWorktreeEnabled(config.AUTO_GIT_COMMIT, config.GIT_WORKTREE_ENABLED) === "enable" ? "enabled" : "disabled"}`,
|
|
4007
|
+
"info",
|
|
4008
|
+
);
|
|
4009
|
+
}
|
|
4010
|
+
},
|
|
4011
|
+
},
|
|
3524
4012
|
);
|
|
3525
4013
|
if (!choice) {
|
|
3526
4014
|
if (config["notify-sound-toggle-shortcut"] !== initialShortcut) {
|
|
@@ -3707,12 +4195,16 @@ async function configurePiUsereq(
|
|
|
3707
4195
|
ctx.ui.notify("Restored all default configuration values", "info");
|
|
3708
4196
|
continue;
|
|
3709
4197
|
}
|
|
3710
|
-
if (choice === "show-config") {
|
|
4198
|
+
if (choice === "show-local-config" || choice === "show-global-config") {
|
|
3711
4199
|
persistConfigChange();
|
|
3712
4200
|
if (config["notify-sound-toggle-shortcut"] !== initialShortcut) {
|
|
3713
4201
|
ctx.ui.notify("Sound toggle hotkey bind updated; run /reload to apply the new binding", "info");
|
|
3714
4202
|
}
|
|
3715
|
-
|
|
4203
|
+
if (choice === "show-local-config") {
|
|
4204
|
+
writePersistedLocalConfigToEditor(ctx, ctx.cwd);
|
|
4205
|
+
} else {
|
|
4206
|
+
writePersistedGlobalConfigToEditor(ctx);
|
|
4207
|
+
}
|
|
3716
4208
|
return;
|
|
3717
4209
|
}
|
|
3718
4210
|
}
|
|
@@ -3740,23 +4232,16 @@ function registerConfigCommands(
|
|
|
3740
4232
|
|
|
3741
4233
|
/**
|
|
3742
4234
|
* @brief Registers the complete pi-usereq extension.
|
|
3743
|
-
* @details Validates installation-owned bundled resources, registers prompt and
|
|
3744
|
-
* configuration commands plus agent tools, registers the configurable
|
|
3745
|
-
* notification-sound shortcut when the runtime supports shortcuts, and
|
|
3746
|
-
* installs shared wrappers for all supported pi lifecycle hooks so status
|
|
3747
|
-
* telemetry, context usage, prompt timing, cumulative runtime, prompt-specific
|
|
3748
|
-
* Pushover metadata, tool-result debug logging, and prompt-orchestration debug
|
|
3749
|
-
* effects remain synchronized with runtime events. Runtime is O(h) in hook
|
|
3750
|
-
* count during registration. Side effects include filesystem reads,
|
|
3751
|
-
* command/tool/shortcut registration, UI updates, active-tool changes,
|
|
3752
|
-
* optional debug-log writes, and timer scheduling.
|
|
4235
|
+
* @details Validates installation-owned bundled resources, registers the specialized `req-reset` and `req-references` commands plus bundled prompt-backed commands and agent tools, registers configuration commands, registers the configurable notification-sound shortcut when the runtime supports shortcuts, and installs shared wrappers for all supported pi lifecycle hooks so status telemetry, context usage, prompt timing, cumulative runtime, prompt-specific Pushover metadata, tool-result debug logging, and prompt-orchestration effects remain synchronized with runtime events. Runtime is O(h) in hook count during registration. Side effects include filesystem reads, command/tool/shortcut registration, UI updates, active-tool changes, optional debug-log writes, and timer scheduling.
|
|
3753
4236
|
* @param[in] pi {ExtensionAPI} Active extension API instance.
|
|
3754
4237
|
* @return {void} No return value.
|
|
3755
|
-
* @satisfies DES-002, REQ-004, REQ-005, REQ-009, REQ-044, REQ-067, REQ-068, REQ-109, REQ-111, REQ-112, REQ-113, REQ-114, REQ-115, REQ-116, REQ-117, REQ-118, REQ-119, REQ-120, REQ-121, REQ-122, REQ-123, REQ-124, REQ-125, REQ-126, REQ-127, REQ-128, REQ-131, REQ-132, REQ-133, REQ-134, REQ-137, REQ-159, REQ-163, REQ-164, REQ-165, REQ-166, REQ-167, REQ-168, REQ-169, REQ-172, REQ-174, REQ-179, REQ-180, REQ-184, REQ-188, REQ-190, REQ-191, REQ-192, REQ-193, REQ-194, REQ-195, REQ-196, REQ-197, REQ-236, REQ-237, REQ-238, REQ-239, REQ-240, REQ-241, REQ-242, REQ-243, REQ-244, REQ-245, REQ-246, REQ-247
|
|
4238
|
+
* @satisfies DES-002, REQ-004, REQ-005, REQ-009, REQ-044, REQ-067, REQ-068, REQ-109, REQ-111, REQ-112, REQ-113, REQ-114, REQ-115, REQ-116, REQ-117, REQ-118, REQ-119, REQ-120, REQ-121, REQ-122, REQ-123, REQ-124, REQ-125, REQ-126, REQ-127, REQ-128, REQ-131, REQ-132, REQ-133, REQ-134, REQ-137, REQ-159, REQ-163, REQ-164, REQ-165, REQ-166, REQ-167, REQ-168, REQ-169, REQ-172, REQ-174, REQ-179, REQ-180, REQ-184, REQ-188, REQ-190, REQ-191, REQ-192, REQ-193, REQ-194, REQ-195, REQ-196, REQ-197, REQ-236, REQ-237, REQ-238, REQ-239, REQ-240, REQ-241, REQ-242, REQ-243, REQ-244, REQ-245, REQ-246, REQ-247, REQ-298, REQ-299, REQ-300, REQ-301, REQ-302, REQ-303, REQ-304, REQ-305, REQ-306, REQ-312, REQ-313
|
|
3756
4239
|
*/
|
|
3757
4240
|
export default function piUsereqExtension(pi: ExtensionAPI): void {
|
|
3758
4241
|
const statusController = createPiUsereqStatusController();
|
|
3759
4242
|
ensureBundledResourcesAccessible();
|
|
4243
|
+
registerReqResetCommand(pi, statusController);
|
|
4244
|
+
registerReqReferencesCommand(pi, statusController);
|
|
3760
4245
|
registerPromptCommands(pi, statusController);
|
|
3761
4246
|
registerAgentTools(pi);
|
|
3762
4247
|
registerConfigCommands(pi, statusController);
|