pi-usereq 0.13.0 → 0.33.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/.github/workflows/release-npm.yml +8 -7
- package/.gitignore +1 -2
- package/CHANGELOG.md +140 -0
- package/README.md +1 -1
- package/package.json +1 -1
- package/pi-usereq/docs/REFERENCES.md +407 -348
- package/pi-usereq/docs/REQUIREMENTS.md +19 -8
- package/pi-usereq/docs/WORKFLOW.md +16 -6
- package/src/core/config.ts +19 -7
- package/src/core/debug-runtime.ts +24 -0
- package/src/index.ts +167 -10
- package/tests/extension-registration.test.ts +144 -5
- package/tests/release-workflow.test.ts +3 -2
- package/scripts/lib/extension-debug-harness.ts +0 -443
- package/scripts/lib/recording-extension-api.ts +0 -855
- package/scripts/lib/sdk-smoke.ts +0 -503
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.33.0";
|
|
12
12
|
|
|
13
13
|
import fs from "node:fs";
|
|
14
14
|
import path from "node:path";
|
|
@@ -110,6 +110,7 @@ import {
|
|
|
110
110
|
DEFAULT_DEBUG_LOG_FILE,
|
|
111
111
|
DEFAULT_DEBUG_LOG_ON_STATUS,
|
|
112
112
|
DEFAULT_DEBUG_STATUS_CHANGES,
|
|
113
|
+
DEFAULT_DEBUG_TOOL_COMMANDS_ENABLED,
|
|
113
114
|
DEFAULT_DEBUG_WORKFLOW_EVENTS,
|
|
114
115
|
logDebugPromptEvent,
|
|
115
116
|
logDebugPromptWorkflowEvent,
|
|
@@ -119,6 +120,7 @@ import {
|
|
|
119
120
|
normalizeDebugLogFile,
|
|
120
121
|
normalizeDebugLogOnStatus,
|
|
121
122
|
normalizeDebugStatusChanges,
|
|
123
|
+
normalizeDebugToolCommandsEnabled,
|
|
122
124
|
normalizeDebugWorkflowEvents,
|
|
123
125
|
shouldLogDebugPromptWorkflowState,
|
|
124
126
|
type DebugLogOnStatus,
|
|
@@ -268,6 +270,125 @@ function loadProjectConfig(cwd: string): UseReqConfig {
|
|
|
268
270
|
return normalizeConfigPaths(projectBase, loadConfig(projectBase));
|
|
269
271
|
}
|
|
270
272
|
|
|
273
|
+
/**
|
|
274
|
+
* @brief Describes the execute-result surface reused by debug tool wrapper commands.
|
|
275
|
+
* @details Narrows debug slash-command handlers to the same monolithic content-plus-execution wrapper returned by agent tools so editor output and notifications can reuse shared extraction helpers. The alias is compile-time only and introduces no runtime cost.
|
|
276
|
+
*/
|
|
277
|
+
type DebugToolCommandExecuteResult = ReturnType<typeof buildMonolithicToolExecuteResult>;
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* @brief Tests whether debug tool wrapper commands should be registered for one runtime cwd.
|
|
281
|
+
* @details Loads the effective project configuration for the supplied cwd and returns `true` only when `DEBUG_TOOL_COMMANDS_ENABLED` resolves to `enable`. Malformed or unreadable config payloads degrade to `false` so extension activation never aborts while deciding whether to register optional debug commands. Runtime is dominated by config I/O. Side effects are limited to filesystem reads.
|
|
282
|
+
* @param[in] cwd {string} Candidate runtime working directory.
|
|
283
|
+
* @return {boolean} `true` when debug tool wrapper commands should be registered.
|
|
284
|
+
* @satisfies REQ-323
|
|
285
|
+
*/
|
|
286
|
+
function shouldRegisterDebugToolCommands(cwd: string): boolean {
|
|
287
|
+
try {
|
|
288
|
+
return normalizeDebugToolCommandsEnabled(loadProjectConfig(cwd).DEBUG_TOOL_COMMANDS_ENABLED) === "enable";
|
|
289
|
+
} catch {
|
|
290
|
+
return false;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* @brief Writes one debug tool-wrapper result into the editor and emits a status notification.
|
|
296
|
+
* @details Extracts the primary monolithic content text from the wrapped tool result, forwards that exact text to the editor, and emits an informational or error notification keyed by the tool exit code. Runtime is O(n) in output length. Side effects include editor-text mutation and UI notifications.
|
|
297
|
+
* @param[in] ctx {ExtensionCommandContext} Active command context.
|
|
298
|
+
* @param[in] commandName {string} Invoked debug slash-command name without the leading `/`.
|
|
299
|
+
* @param[in] result {DebugToolCommandExecuteResult} Wrapped tool execution result.
|
|
300
|
+
* @return {void} No return value.
|
|
301
|
+
* @satisfies REQ-324
|
|
302
|
+
*/
|
|
303
|
+
function writeDebugToolCommandResultToEditor(
|
|
304
|
+
ctx: ExtensionCommandContext,
|
|
305
|
+
commandName: string,
|
|
306
|
+
result: DebugToolCommandExecuteResult,
|
|
307
|
+
): void {
|
|
308
|
+
const outputText = getMonolithicToolText(result);
|
|
309
|
+
ctx.ui.setEditorText(outputText);
|
|
310
|
+
const succeeded = result.details.execution.code === 0;
|
|
311
|
+
ctx.ui.notify(
|
|
312
|
+
succeeded
|
|
313
|
+
? `Wrote /${commandName} output to the editor.`
|
|
314
|
+
: `FAILED: Wrote /${commandName} output to the editor.`,
|
|
315
|
+
succeeded ? "info" : "error",
|
|
316
|
+
);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* @brief Executes one config-gated debug tool wrapper slash command.
|
|
321
|
+
* @details Resolves a live cwd for the invoking command context, refreshes runtime path state, loads the effective project configuration, rejects execution when debug tool wrapper commands are disabled, and otherwise writes the selected wrapped tool output into the editor. Runtime is dominated by the delegated tool runner. Side effects include runtime-path bootstrap, filesystem reads, optional tool side effects, editor-text mutation, and UI notifications.
|
|
322
|
+
* @param[in] ctx {ExtensionCommandContext} Active command context.
|
|
323
|
+
* @param[in] commandName {string} Invoked debug slash-command name without the leading `/`.
|
|
324
|
+
* @param[in] operation {(projectBase: string, config: UseReqConfig) => DebugToolCommandExecuteResult} Wrapped tool executor.
|
|
325
|
+
* @return {void} No return value.
|
|
326
|
+
* @throws {ReqError} Throws when debug tool wrapper commands are disabled for the active project.
|
|
327
|
+
* @satisfies REQ-324, REQ-325
|
|
328
|
+
*/
|
|
329
|
+
function executeDebugToolCommand(
|
|
330
|
+
ctx: ExtensionCommandContext,
|
|
331
|
+
commandName: string,
|
|
332
|
+
operation: (projectBase: string, config: UseReqConfig) => DebugToolCommandExecuteResult,
|
|
333
|
+
): void {
|
|
334
|
+
const commandCwd = resolveLiveBootstrapCwd(ctx.cwd);
|
|
335
|
+
syncContextCwdMirror(ctx, commandCwd);
|
|
336
|
+
bootstrapRuntimePathState(commandCwd, {
|
|
337
|
+
gitPath: resolveRuntimeGitPath(commandCwd),
|
|
338
|
+
});
|
|
339
|
+
const projectBase = getProjectBase(commandCwd);
|
|
340
|
+
const config = loadProjectConfig(commandCwd);
|
|
341
|
+
if (normalizeDebugToolCommandsEnabled(config.DEBUG_TOOL_COMMANDS_ENABLED) !== "enable") {
|
|
342
|
+
const message = `ERROR: /${commandName} is disabled by Debug > Enable debug commands for tools.`;
|
|
343
|
+
ctx.ui.notify(message, "error");
|
|
344
|
+
throw new ReqError(message, 1);
|
|
345
|
+
}
|
|
346
|
+
writeDebugToolCommandResultToEditor(ctx, commandName, operation(projectBase, config));
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* @brief Registers config-gated debug slash-command wrappers for selected built-in analysis tools.
|
|
351
|
+
* @details Registers `debug-compress`, `debug-references`, `debug-static-check`, and `debug-tokens` as extension commands that reuse the same runner paths as the corresponding agent tools and write the resulting monolithic text into the editor instead of the LLM content channel. Re-registering the same commands is idempotent because pi keeps the latest same-extension command definition per name. Runtime is O(1) for registration; handler cost depends on the selected runner. Side effects include command registration.
|
|
352
|
+
* @param[in] pi {ExtensionAPI} Active extension API instance.
|
|
353
|
+
* @return {void} No return value.
|
|
354
|
+
* @satisfies DES-015, REQ-323, REQ-324, REQ-325
|
|
355
|
+
*/
|
|
356
|
+
function registerDebugToolCommands(pi: ExtensionAPI): void {
|
|
357
|
+
const registerDebugToolCommand = (
|
|
358
|
+
commandName: string,
|
|
359
|
+
description: string,
|
|
360
|
+
operation: (projectBase: string, config: UseReqConfig) => DebugToolCommandExecuteResult,
|
|
361
|
+
): void => {
|
|
362
|
+
pi.registerCommand(commandName, {
|
|
363
|
+
description,
|
|
364
|
+
handler: async (_args, ctx) => {
|
|
365
|
+
executeDebugToolCommand(ctx, commandName, operation);
|
|
366
|
+
},
|
|
367
|
+
});
|
|
368
|
+
};
|
|
369
|
+
|
|
370
|
+
registerDebugToolCommand(
|
|
371
|
+
"debug-compress",
|
|
372
|
+
"Run compress and write the tool output into the editor",
|
|
373
|
+
(projectBase, config) => executeMonolithicTool(() => runCompress(projectBase, config)),
|
|
374
|
+
);
|
|
375
|
+
registerDebugToolCommand(
|
|
376
|
+
"debug-references",
|
|
377
|
+
"Run references and write the tool output into the editor",
|
|
378
|
+
(projectBase, config) => executeStatusTool(() => runReferences(projectBase, config)),
|
|
379
|
+
);
|
|
380
|
+
registerDebugToolCommand(
|
|
381
|
+
"debug-static-check",
|
|
382
|
+
"Run static-check and write the tool output into the editor",
|
|
383
|
+
(projectBase, config) => executeMonolithicTool(() => runProjectStaticCheck(projectBase, config)),
|
|
384
|
+
);
|
|
385
|
+
registerDebugToolCommand(
|
|
386
|
+
"debug-tokens",
|
|
387
|
+
"Run tokens and write the tool output into the editor",
|
|
388
|
+
(projectBase, config) => executeMonolithicTool(() => runTokens(projectBase, config)),
|
|
389
|
+
);
|
|
390
|
+
}
|
|
391
|
+
|
|
271
392
|
/**
|
|
272
393
|
* @brief Persists effective project configuration from the extension runtime.
|
|
273
394
|
* @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.
|
|
@@ -1411,16 +1532,17 @@ function getDebugToolToggleNames(): PiUsereqStartupToolName[] {
|
|
|
1411
1532
|
|
|
1412
1533
|
/**
|
|
1413
1534
|
* @brief Restores the debug configuration subtree to its documented defaults.
|
|
1414
|
-
* @details Resets global debug enablement, log path, workflow-state filter, dedicated workflow-event logging, and selected tool plus prompt debug toggles without mutating unrelated settings. Runtime is O(1). Side effect: mutates `config`.
|
|
1535
|
+
* @details Resets global debug enablement, log path, tool-wrapper command registration, workflow-state filter, dedicated workflow-event logging, and selected tool plus prompt debug toggles without mutating unrelated settings. Runtime is O(1). Side effect: mutates `config`.
|
|
1415
1536
|
* @param[in,out] config {UseReqConfig} Mutable configuration object.
|
|
1416
1537
|
* @return {void} No return value.
|
|
1417
|
-
* @satisfies REQ-236, REQ-237, REQ-238, REQ-239, REQ-195, REQ-277
|
|
1538
|
+
* @satisfies REQ-236, REQ-237, REQ-238, REQ-239, REQ-195, REQ-277, REQ-322
|
|
1418
1539
|
*/
|
|
1419
1540
|
function resetDebugConfigToDefaults(config: UseReqConfig): void {
|
|
1420
1541
|
config.DEBUG_ENABLED = "disable";
|
|
1421
1542
|
config.DEBUG_LOG_FILE = DEFAULT_DEBUG_LOG_FILE;
|
|
1422
1543
|
config.DEBUG_STATUS_CHANGES = DEFAULT_DEBUG_STATUS_CHANGES;
|
|
1423
1544
|
config.DEBUG_WORKFLOW_EVENTS = DEFAULT_DEBUG_WORKFLOW_EVENTS;
|
|
1545
|
+
config.DEBUG_TOOL_COMMANDS_ENABLED = DEFAULT_DEBUG_TOOL_COMMANDS_ENABLED;
|
|
1424
1546
|
config.DEBUG_LOG_ON_STATUS = DEFAULT_DEBUG_LOG_ON_STATUS;
|
|
1425
1547
|
config.DEBUG_ENABLED_TOOLS = [];
|
|
1426
1548
|
config.DEBUG_ENABLED_PROMPTS = [];
|
|
@@ -1502,10 +1624,10 @@ async function selectDebugLogOnStatus(
|
|
|
1502
1624
|
|
|
1503
1625
|
/**
|
|
1504
1626
|
* @brief Builds the shared settings-menu choices for debug logging configuration.
|
|
1505
|
-
* @details Serializes global debug controls plus workflow-state, dedicated workflow-event, per-tool, and per-prompt toggles into one submenu, deriving inventories from the canonical tool and prompt lists and dimming locked rows while debug is disabled. Runtime is O(t + p). No external state is mutated.
|
|
1627
|
+
* @details Serializes global debug controls plus tool-wrapper command registration, workflow-state, dedicated workflow-event, per-tool, and per-prompt toggles into one submenu, deriving inventories from the canonical tool and prompt lists and dimming locked rows while debug is disabled. Runtime is O(t + p). No external state is mutated.
|
|
1506
1628
|
* @param[in] config {UseReqConfig} Effective project configuration.
|
|
1507
1629
|
* @return {PiUsereqSettingsMenuChoice[]} Ordered debug-menu choices.
|
|
1508
|
-
* @satisfies REQ-240, REQ-241, REQ-242, REQ-243, REQ-193, REQ-277
|
|
1630
|
+
* @satisfies REQ-240, REQ-241, REQ-242, REQ-243, REQ-193, REQ-277, REQ-321, REQ-322
|
|
1509
1631
|
*/
|
|
1510
1632
|
function buildDebugMenuChoices(config: UseReqConfig): PiUsereqSettingsMenuChoice[] {
|
|
1511
1633
|
const debugEnabled = config.DEBUG_ENABLED === "enable";
|
|
@@ -1528,6 +1650,16 @@ function buildDebugMenuChoices(config: UseReqConfig): PiUsereqSettingsMenuChoice
|
|
|
1528
1650
|
},
|
|
1529
1651
|
debugEnabled,
|
|
1530
1652
|
),
|
|
1653
|
+
buildDebugMenuChoice(
|
|
1654
|
+
{
|
|
1655
|
+
id: "debug-tool-commands-enabled",
|
|
1656
|
+
label: "Enable debug commands for tools",
|
|
1657
|
+
value: normalizeDebugToolCommandsEnabled(config.DEBUG_TOOL_COMMANDS_ENABLED),
|
|
1658
|
+
values: ["enable", "disable"],
|
|
1659
|
+
description: "Enable or disable slash-command wrappers for `compress`, `references`, `static-check`, and `tokens`.",
|
|
1660
|
+
},
|
|
1661
|
+
debugEnabled,
|
|
1662
|
+
),
|
|
1531
1663
|
buildDebugMenuChoice(
|
|
1532
1664
|
{
|
|
1533
1665
|
id: "debug-log-on-status",
|
|
@@ -1587,17 +1719,28 @@ function buildDebugMenuChoices(config: UseReqConfig): PiUsereqSettingsMenuChoice
|
|
|
1587
1719
|
|
|
1588
1720
|
/**
|
|
1589
1721
|
* @brief Runs the interactive Debug submenu.
|
|
1590
|
-
* @details Lets the user toggle global debug enablement,
|
|
1722
|
+
* @details Lets the user toggle global debug enablement, tool-wrapper command registration, debug file and workflow filters, dedicated workflow-event logging, per-tool selectors, and per-prompt selectors while preserving row focus across re-renders. Runtime depends on user interaction count. Side effects include UI updates, config mutation, and optional debug command registration.
|
|
1723
|
+
* @param[in] pi {ExtensionAPI} Active extension API instance.
|
|
1591
1724
|
* @param[in] ctx {ExtensionCommandContext} Active command context.
|
|
1592
1725
|
* @param[in,out] config {UseReqConfig} Mutable configuration object.
|
|
1593
1726
|
* @return {Promise<void>} Promise resolved when the submenu closes.
|
|
1594
|
-
* @satisfies REQ-236, REQ-237, REQ-238, REQ-239, REQ-240, REQ-241, REQ-242, REQ-243, REQ-192, REQ-193, REQ-195, REQ-277
|
|
1727
|
+
* @satisfies REQ-236, REQ-237, REQ-238, REQ-239, REQ-240, REQ-241, REQ-242, REQ-243, REQ-192, REQ-193, REQ-195, REQ-277, REQ-321, REQ-322, REQ-323
|
|
1595
1728
|
*/
|
|
1596
1729
|
async function configureDebugMenu(
|
|
1730
|
+
pi: ExtensionAPI,
|
|
1597
1731
|
ctx: ExtensionCommandContext,
|
|
1598
1732
|
config: UseReqConfig,
|
|
1599
1733
|
onConfigChange: () => void,
|
|
1600
1734
|
): Promise<void> {
|
|
1735
|
+
const updateDebugToolCommandsEnabled = (nextValue: unknown): void => {
|
|
1736
|
+
config.DEBUG_TOOL_COMMANDS_ENABLED = normalizeDebugToolCommandsEnabled(nextValue);
|
|
1737
|
+
onConfigChange();
|
|
1738
|
+
if (config.DEBUG_TOOL_COMMANDS_ENABLED === "enable") {
|
|
1739
|
+
registerDebugToolCommands(pi);
|
|
1740
|
+
}
|
|
1741
|
+
ctx.ui.notify(`Debug tool commands ${config.DEBUG_TOOL_COMMANDS_ENABLED}`, "info");
|
|
1742
|
+
};
|
|
1743
|
+
|
|
1601
1744
|
let focusedChoiceId: string | undefined;
|
|
1602
1745
|
while (true) {
|
|
1603
1746
|
const choice = await showPiUsereqSettingsMenu(ctx, "Debug", buildDebugMenuChoices(config), {
|
|
@@ -1610,6 +1753,10 @@ async function configureDebugMenu(
|
|
|
1610
1753
|
ctx.ui.notify(`Debug ${config.DEBUG_ENABLED}`, "info");
|
|
1611
1754
|
return;
|
|
1612
1755
|
}
|
|
1756
|
+
if (choiceId === "debug-tool-commands-enabled") {
|
|
1757
|
+
updateDebugToolCommandsEnabled(newValue);
|
|
1758
|
+
return;
|
|
1759
|
+
}
|
|
1613
1760
|
if (choiceId === "debug-status-changes") {
|
|
1614
1761
|
config.DEBUG_STATUS_CHANGES = normalizeDebugStatusChanges(newValue);
|
|
1615
1762
|
onConfigChange();
|
|
@@ -1670,6 +1817,7 @@ async function configureDebugMenu(
|
|
|
1670
1817
|
const resetPreview: ResetConfirmationChange[] = [
|
|
1671
1818
|
{ label: "Debug", previousValue: config.DEBUG_ENABLED, nextValue: "disable" },
|
|
1672
1819
|
{ label: "Log file", previousValue: config.DEBUG_LOG_FILE, nextValue: DEFAULT_DEBUG_LOG_FILE },
|
|
1820
|
+
{ label: "Enable debug commands for tools", previousValue: normalizeDebugToolCommandsEnabled(config.DEBUG_TOOL_COMMANDS_ENABLED), nextValue: DEFAULT_DEBUG_TOOL_COMMANDS_ENABLED },
|
|
1673
1821
|
{ label: "Status changes", previousValue: normalizeDebugStatusChanges(config.DEBUG_STATUS_CHANGES), nextValue: DEFAULT_DEBUG_STATUS_CHANGES },
|
|
1674
1822
|
{ label: "Workflow events", previousValue: normalizeDebugWorkflowEvents(config.DEBUG_WORKFLOW_EVENTS), nextValue: DEFAULT_DEBUG_WORKFLOW_EVENTS },
|
|
1675
1823
|
{ label: "Log on status", previousValue: config.DEBUG_LOG_ON_STATUS, nextValue: DEFAULT_DEBUG_LOG_ON_STATUS },
|
|
@@ -1705,6 +1853,12 @@ async function configureDebugMenu(
|
|
|
1705
1853
|
}
|
|
1706
1854
|
continue;
|
|
1707
1855
|
}
|
|
1856
|
+
if (choice === "debug-tool-commands-enabled") {
|
|
1857
|
+
updateDebugToolCommandsEnabled(
|
|
1858
|
+
config.DEBUG_TOOL_COMMANDS_ENABLED === "enable" ? "disable" : "enable",
|
|
1859
|
+
);
|
|
1860
|
+
continue;
|
|
1861
|
+
}
|
|
1708
1862
|
if (choice === "debug-status-changes") {
|
|
1709
1863
|
config.DEBUG_STATUS_CHANGES = normalizeDebugStatusChanges(
|
|
1710
1864
|
config.DEBUG_STATUS_CHANGES === "enable" ? "disable" : "enable",
|
|
@@ -4163,7 +4317,7 @@ async function configurePiUsereq(
|
|
|
4163
4317
|
continue;
|
|
4164
4318
|
}
|
|
4165
4319
|
if (choice === "debug") {
|
|
4166
|
-
await configureDebugMenu(ctx, config, persistConfigChange);
|
|
4320
|
+
await configureDebugMenu(pi, ctx, config, persistConfigChange);
|
|
4167
4321
|
continue;
|
|
4168
4322
|
}
|
|
4169
4323
|
if (choice === "reset-defaults") {
|
|
@@ -4232,10 +4386,10 @@ function registerConfigCommands(
|
|
|
4232
4386
|
|
|
4233
4387
|
/**
|
|
4234
4388
|
* @brief Registers the complete pi-usereq extension.
|
|
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.
|
|
4389
|
+
* @details Validates installation-owned bundled resources, registers the specialized `req-reset` and `req-references` commands plus bundled prompt-backed commands and agent tools, conditionally registers config-gated debug tool wrapper commands when the current project enables them, 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.
|
|
4236
4390
|
* @param[in] pi {ExtensionAPI} Active extension API instance.
|
|
4237
4391
|
* @return {void} No return value.
|
|
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
|
|
4392
|
+
* @satisfies DES-002, DES-015, 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, REQ-323, REQ-324, REQ-325
|
|
4239
4393
|
*/
|
|
4240
4394
|
export default function piUsereqExtension(pi: ExtensionAPI): void {
|
|
4241
4395
|
const statusController = createPiUsereqStatusController();
|
|
@@ -4243,6 +4397,9 @@ export default function piUsereqExtension(pi: ExtensionAPI): void {
|
|
|
4243
4397
|
registerReqResetCommand(pi, statusController);
|
|
4244
4398
|
registerReqReferencesCommand(pi, statusController);
|
|
4245
4399
|
registerPromptCommands(pi, statusController);
|
|
4400
|
+
if (shouldRegisterDebugToolCommands(getProcessCwdSafe())) {
|
|
4401
|
+
registerDebugToolCommands(pi);
|
|
4402
|
+
}
|
|
4246
4403
|
registerAgentTools(pi);
|
|
4247
4404
|
registerConfigCommands(pi, statusController);
|
|
4248
4405
|
registerPiNotifyShortcut(pi, statusController);
|
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
DEBUG_PROMPT_NAMES,
|
|
25
25
|
DEFAULT_DEBUG_LOG_FILE,
|
|
26
26
|
DEFAULT_DEBUG_LOG_ON_STATUS,
|
|
27
|
+
DEFAULT_DEBUG_TOOL_COMMANDS_ENABLED,
|
|
27
28
|
DEFAULT_DEBUG_WORKFLOW_EVENTS,
|
|
28
29
|
} from "../src/core/debug-runtime.js";
|
|
29
30
|
import { PROMPT_COMMAND_NAMES } from "../src/core/prompt-command-catalog.js";
|
|
@@ -838,6 +839,10 @@ test("extension registers prompt and config commands while exposing tool capabil
|
|
|
838
839
|
"tokens",
|
|
839
840
|
"files-static-check",
|
|
840
841
|
"static-check",
|
|
842
|
+
"debug-compress",
|
|
843
|
+
"debug-references",
|
|
844
|
+
"debug-static-check",
|
|
845
|
+
"debug-tokens",
|
|
841
846
|
"pi-usereq-show-config",
|
|
842
847
|
"test-static-check",
|
|
843
848
|
]) {
|
|
@@ -862,6 +867,54 @@ test("extension registers prompt and config commands while exposing tool capabil
|
|
|
862
867
|
}
|
|
863
868
|
});
|
|
864
869
|
|
|
870
|
+
/**
|
|
871
|
+
* @brief Verifies debug tool wrapper commands register only when the local debug flag is enabled.
|
|
872
|
+
* @details Activates the extension once from a default-disabled fixture and once from a fixture whose local config enables debug tool wrapper commands, then asserts the four debug slash commands appear only in the enabled runtime. Runtime is dominated by fixture setup and extension registration. Side effects are limited to temporary filesystem mutation and process cwd changes.
|
|
873
|
+
* @return {void} No return value.
|
|
874
|
+
* @throws {AssertionError} Throws when command registration ignores the local debug tool wrapper flag.
|
|
875
|
+
* @satisfies TST-115
|
|
876
|
+
*/
|
|
877
|
+
test("debug tool commands register only when enabled in local config", () => {
|
|
878
|
+
const disabledFixture = initFixtureRepo({ fixtures: [] });
|
|
879
|
+
const enabledFixture = initFixtureRepo({ fixtures: [] });
|
|
880
|
+
const debugCommandNames = [
|
|
881
|
+
"debug-compress",
|
|
882
|
+
"debug-references",
|
|
883
|
+
"debug-static-check",
|
|
884
|
+
"debug-tokens",
|
|
885
|
+
];
|
|
886
|
+
|
|
887
|
+
try {
|
|
888
|
+
writeProjectConfigOverrides(enabledFixture.projectBase, {
|
|
889
|
+
DEBUG_TOOL_COMMANDS_ENABLED: "enable",
|
|
890
|
+
});
|
|
891
|
+
|
|
892
|
+
const previousCwd = process.cwd();
|
|
893
|
+
try {
|
|
894
|
+
process.chdir(disabledFixture.projectBase);
|
|
895
|
+
const disabledPi = createFakePi();
|
|
896
|
+
piUsereqExtension(disabledPi);
|
|
897
|
+
const disabledCommandNames = [...disabledPi.commands.keys()];
|
|
898
|
+
for (const commandName of debugCommandNames) {
|
|
899
|
+
assert.equal(disabledCommandNames.includes(commandName), false, `unexpected command ${commandName}`);
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
process.chdir(enabledFixture.projectBase);
|
|
903
|
+
const enabledPi = createFakePi();
|
|
904
|
+
piUsereqExtension(enabledPi);
|
|
905
|
+
const enabledCommandNames = [...enabledPi.commands.keys()];
|
|
906
|
+
for (const commandName of debugCommandNames) {
|
|
907
|
+
assert.ok(enabledCommandNames.includes(commandName), `missing command ${commandName}`);
|
|
908
|
+
}
|
|
909
|
+
} finally {
|
|
910
|
+
process.chdir(previousCwd);
|
|
911
|
+
}
|
|
912
|
+
} finally {
|
|
913
|
+
fs.rmSync(disabledFixture.projectBase, { recursive: true, force: true });
|
|
914
|
+
fs.rmSync(enabledFixture.projectBase, { recursive: true, force: true });
|
|
915
|
+
}
|
|
916
|
+
});
|
|
917
|
+
|
|
865
918
|
/**
|
|
866
919
|
* @brief Verifies `req-references` registration is independent from bundled prompt inventory.
|
|
867
920
|
* @details Confirms `references` is absent from `PROMPT_COMMAND_NAMES` while extension activation still registers `req-references` with the fixed dedicated description. Runtime is O(1). Side effects are limited to extension registration.
|
|
@@ -1551,6 +1604,80 @@ test("static-check tools return monolithic text payloads with minimal execution
|
|
|
1551
1604
|
}
|
|
1552
1605
|
});
|
|
1553
1606
|
|
|
1607
|
+
/**
|
|
1608
|
+
* @brief Verifies debug tool wrapper commands mirror tool content and reject when disabled.
|
|
1609
|
+
* @details Enables debug tool wrapper commands for one fixture project, compares each debug slash-command editor payload against the matching tool `content[0].text`, then disables the flag and verifies the already-registered wrapper rejects further execution. Runtime is dominated by tool execution plus temporary git fixture setup. Side effects are limited to temporary filesystem mutation, editor-text capture, and process cwd changes.
|
|
1610
|
+
* @return {Promise<void>} Promise resolved when the verification completes.
|
|
1611
|
+
* @throws {AssertionError} Throws when a debug wrapper output diverges from the wrapped tool text or disabled execution is accepted.
|
|
1612
|
+
* @satisfies TST-116
|
|
1613
|
+
*/
|
|
1614
|
+
test("debug tool wrapper commands mirror tool content and reject when disabled", async () => {
|
|
1615
|
+
const { projectBase } = initFixtureRepo({
|
|
1616
|
+
fixtures: [],
|
|
1617
|
+
staticCheck: {
|
|
1618
|
+
TypeScript: createStaticCheckLanguageConfig([{ module: "Dummy" }], "enable"),
|
|
1619
|
+
},
|
|
1620
|
+
});
|
|
1621
|
+
fs.writeFileSync(path.join(projectBase, "src", "check.ts"), "export const VALUE = 1;\n", "utf8");
|
|
1622
|
+
const add = spawnSync("git", ["add", "."], { cwd: projectBase, encoding: "utf8" });
|
|
1623
|
+
assert.equal(add.status, 0, add.stderr);
|
|
1624
|
+
const commit = spawnSync("git", ["commit", "-m", "add debug tool inputs"], { cwd: projectBase, encoding: "utf8" });
|
|
1625
|
+
assert.equal(commit.status, 0, commit.stderr);
|
|
1626
|
+
|
|
1627
|
+
const previousCwd = process.cwd();
|
|
1628
|
+
try {
|
|
1629
|
+
writeProjectConfigOverrides(projectBase, {
|
|
1630
|
+
DEBUG_TOOL_COMMANDS_ENABLED: "enable",
|
|
1631
|
+
});
|
|
1632
|
+
|
|
1633
|
+
process.chdir(projectBase);
|
|
1634
|
+
const pi = createFakePi();
|
|
1635
|
+
piUsereqExtension(pi);
|
|
1636
|
+
|
|
1637
|
+
const compressToolResult = await executeRegisteredTool(pi, "compress", projectBase, {}) as {
|
|
1638
|
+
content?: Array<{ type: string; text?: string }>;
|
|
1639
|
+
};
|
|
1640
|
+
const referencesToolResult = await executeRegisteredTool(pi, "references", projectBase, {}) as {
|
|
1641
|
+
content?: Array<{ type: string; text?: string }>;
|
|
1642
|
+
};
|
|
1643
|
+
const staticCheckToolResult = await executeRegisteredTool(pi, "static-check", projectBase) as {
|
|
1644
|
+
content?: Array<{ type: string; text?: string }>;
|
|
1645
|
+
};
|
|
1646
|
+
const tokensToolResult = await executeRegisteredTool(pi, "tokens", projectBase, {}) as {
|
|
1647
|
+
content?: Array<{ type: string; text?: string }>;
|
|
1648
|
+
};
|
|
1649
|
+
|
|
1650
|
+
const compressCtx = createFakeCtx(projectBase);
|
|
1651
|
+
await pi.commands.get("debug-compress")!.handler("", compressCtx);
|
|
1652
|
+
assert.equal(compressCtx.__state.editorText, compressToolResult.content?.[0]?.text ?? "");
|
|
1653
|
+
|
|
1654
|
+
const referencesCtx = createFakeCtx(projectBase);
|
|
1655
|
+
await pi.commands.get("debug-references")!.handler("", referencesCtx);
|
|
1656
|
+
assert.equal(referencesCtx.__state.editorText, referencesToolResult.content?.[0]?.text ?? "");
|
|
1657
|
+
|
|
1658
|
+
const staticCheckCtx = createFakeCtx(projectBase);
|
|
1659
|
+
await pi.commands.get("debug-static-check")!.handler("", staticCheckCtx);
|
|
1660
|
+
assert.equal(staticCheckCtx.__state.editorText, staticCheckToolResult.content?.[0]?.text ?? "");
|
|
1661
|
+
|
|
1662
|
+
const tokensCtx = createFakeCtx(projectBase);
|
|
1663
|
+
await pi.commands.get("debug-tokens")!.handler("", tokensCtx);
|
|
1664
|
+
assert.equal(tokensCtx.__state.editorText, tokensToolResult.content?.[0]?.text ?? "");
|
|
1665
|
+
|
|
1666
|
+
writeProjectConfigOverrides(projectBase, {
|
|
1667
|
+
DEBUG_TOOL_COMMANDS_ENABLED: "disable",
|
|
1668
|
+
});
|
|
1669
|
+
const disabledCtx = createFakeCtx(projectBase);
|
|
1670
|
+
await assert.rejects(
|
|
1671
|
+
async () => pi.commands.get("debug-tokens")!.handler("", disabledCtx),
|
|
1672
|
+
/disabled by Debug > Enable debug commands for tools/u,
|
|
1673
|
+
);
|
|
1674
|
+
assert.equal(disabledCtx.__state.editorText, "");
|
|
1675
|
+
} finally {
|
|
1676
|
+
process.chdir(previousCwd);
|
|
1677
|
+
fs.rmSync(projectBase, { recursive: true, force: true });
|
|
1678
|
+
}
|
|
1679
|
+
});
|
|
1680
|
+
|
|
1554
1681
|
test("configuration menu saves updated docs-dir in project config", async () => {
|
|
1555
1682
|
const cwd = createTempDir("pi-usereq-menu-");
|
|
1556
1683
|
fs.mkdirSync(path.dirname(getProjectConfigPath(cwd)), { recursive: true });
|
|
@@ -1821,6 +1948,7 @@ test("debug menu dims locked rows and persists debug settings with focus-preserv
|
|
|
1821
1948
|
"Debug",
|
|
1822
1949
|
"Debug",
|
|
1823
1950
|
"Log file",
|
|
1951
|
+
"Enable debug commands for tools",
|
|
1824
1952
|
"Log on status",
|
|
1825
1953
|
"any",
|
|
1826
1954
|
"Status changes",
|
|
@@ -1839,24 +1967,31 @@ test("debug menu dims locked rows and persists debug settings with focus-preserv
|
|
|
1839
1967
|
const persistedConfig = readProjectConfigJson(cwd);
|
|
1840
1968
|
const debugCalls = ctx.__state.selectCalls.filter((call) => call.title === "Debug");
|
|
1841
1969
|
assert.match(renderedInitialDebugMenu, /<dim>Log file/);
|
|
1970
|
+
assert.match(renderedInitialDebugMenu, /<dim>Enable debug commands for tools/);
|
|
1842
1971
|
assert.match(renderedInitialDebugMenu, /<dim>Log on status/);
|
|
1843
1972
|
assert.match(renderedInitialDebugMenu, /<dim>Status changes/);
|
|
1844
1973
|
assert.match(renderedInitialDebugMenu, /<dim>Workflow events/);
|
|
1845
1974
|
assert.match(renderedInitialDebugMenu, /<dim>compress/);
|
|
1846
1975
|
assert.equal(persistedConfig.DEBUG_ENABLED, "enable");
|
|
1847
1976
|
assert.equal(persistedConfig.DEBUG_LOG_FILE, "logs/debug-output.json");
|
|
1977
|
+
assert.equal(persistedConfig.DEBUG_TOOL_COMMANDS_ENABLED, "enable");
|
|
1848
1978
|
assert.equal(persistedConfig.DEBUG_STATUS_CHANGES, "enable");
|
|
1849
1979
|
assert.equal(persistedConfig.DEBUG_WORKFLOW_EVENTS, "enable");
|
|
1850
1980
|
assert.equal(persistedConfig.DEBUG_LOG_ON_STATUS, "any");
|
|
1981
|
+
assert.ok(pi.commands.get("debug-compress"));
|
|
1982
|
+
assert.ok(pi.commands.get("debug-references"));
|
|
1983
|
+
assert.ok(pi.commands.get("debug-static-check"));
|
|
1984
|
+
assert.ok(pi.commands.get("debug-tokens"));
|
|
1851
1985
|
assert.deepEqual(persistedConfig.DEBUG_ENABLED_TOOLS, ["files-tokens"]);
|
|
1852
1986
|
assert.deepEqual(persistedConfig.DEBUG_ENABLED_PROMPTS, ["req-analyze"]);
|
|
1853
1987
|
assert.equal(debugCalls[1]?.selectedChoiceId, "debug-enabled");
|
|
1854
1988
|
assert.equal(debugCalls[2]?.selectedChoiceId, "debug-log-file");
|
|
1855
|
-
assert.equal(debugCalls[3]?.selectedChoiceId, "debug-
|
|
1856
|
-
assert.equal(debugCalls[4]?.selectedChoiceId, "debug-status
|
|
1857
|
-
assert.equal(debugCalls[5]?.selectedChoiceId, "debug-
|
|
1858
|
-
assert.equal(debugCalls[6]?.selectedChoiceId, "debug-
|
|
1859
|
-
assert.equal(debugCalls[7]?.selectedChoiceId, "debug-
|
|
1989
|
+
assert.equal(debugCalls[3]?.selectedChoiceId, "debug-tool-commands-enabled");
|
|
1990
|
+
assert.equal(debugCalls[4]?.selectedChoiceId, "debug-log-on-status");
|
|
1991
|
+
assert.equal(debugCalls[5]?.selectedChoiceId, "debug-status-changes");
|
|
1992
|
+
assert.equal(debugCalls[6]?.selectedChoiceId, "debug-workflow-events");
|
|
1993
|
+
assert.equal(debugCalls[7]?.selectedChoiceId, "debug-tool:files-tokens");
|
|
1994
|
+
assert.equal(debugCalls[8]?.selectedChoiceId, "debug-prompt:req-analyze");
|
|
1860
1995
|
|
|
1861
1996
|
const resetCtx = createFakeCtx(cwd, {
|
|
1862
1997
|
selects: ["Debug", "Reset defaults", "Approve reset", "Save and close"],
|
|
@@ -1865,6 +2000,7 @@ test("debug menu dims locked rows and persists debug settings with focus-preserv
|
|
|
1865
2000
|
const resetConfig = readProjectConfigJson(cwd);
|
|
1866
2001
|
assert.equal(resetConfig.DEBUG_ENABLED, "disable");
|
|
1867
2002
|
assert.equal(resetConfig.DEBUG_LOG_FILE, DEFAULT_DEBUG_LOG_FILE);
|
|
2003
|
+
assert.equal(resetConfig.DEBUG_TOOL_COMMANDS_ENABLED, DEFAULT_DEBUG_TOOL_COMMANDS_ENABLED);
|
|
1868
2004
|
assert.equal(resetConfig.DEBUG_STATUS_CHANGES, "disable");
|
|
1869
2005
|
assert.equal(resetConfig.DEBUG_WORKFLOW_EVENTS, DEFAULT_DEBUG_WORKFLOW_EVENTS);
|
|
1870
2006
|
assert.equal(resetConfig.DEBUG_LOG_ON_STATUS, DEFAULT_DEBUG_LOG_ON_STATUS);
|
|
@@ -1888,6 +2024,7 @@ test("debug menu rows derive from canonical tool and prompt inventories", async
|
|
|
1888
2024
|
assert.deepEqual(ctx.__state.selectCalls[1]?.items ?? [], [
|
|
1889
2025
|
"Debug",
|
|
1890
2026
|
"Log file",
|
|
2027
|
+
"Enable debug commands for tools",
|
|
1891
2028
|
"Log on status",
|
|
1892
2029
|
"Status changes",
|
|
1893
2030
|
"Workflow events",
|
|
@@ -5386,6 +5523,7 @@ test("default configuration applies the documented static-check, debug, notify,
|
|
|
5386
5523
|
assert.deepEqual(config["static-check"].Ruby, createStaticCheckLanguageConfig([], "disable"));
|
|
5387
5524
|
assert.equal(config.DEBUG_ENABLED, "disable");
|
|
5388
5525
|
assert.equal(config.DEBUG_LOG_FILE, DEFAULT_DEBUG_LOG_FILE);
|
|
5526
|
+
assert.equal(config.DEBUG_TOOL_COMMANDS_ENABLED, DEFAULT_DEBUG_TOOL_COMMANDS_ENABLED);
|
|
5389
5527
|
assert.equal(config.DEBUG_WORKFLOW_EVENTS, DEFAULT_DEBUG_WORKFLOW_EVENTS);
|
|
5390
5528
|
assert.equal(config.DEBUG_LOG_ON_STATUS, DEFAULT_DEBUG_LOG_ON_STATUS);
|
|
5391
5529
|
assert.deepEqual(config.DEBUG_ENABLED_TOOLS, []);
|
|
@@ -5409,6 +5547,7 @@ test("default configuration applies the documented static-check, debug, notify,
|
|
|
5409
5547
|
const globalConfig = readGlobalConfigJson();
|
|
5410
5548
|
assert.equal((localConfig["static-check"] as Record<string, any>).Python.enabled, "enable");
|
|
5411
5549
|
assert.equal((localConfig["static-check"] as Record<string, any>).Ruby.enabled, "disable");
|
|
5550
|
+
assert.equal(localConfig.DEBUG_TOOL_COMMANDS_ENABLED, DEFAULT_DEBUG_TOOL_COMMANDS_ENABLED);
|
|
5412
5551
|
assert.deepEqual((globalConfig["static-check"] as Record<string, any>).Python.checkers, [
|
|
5413
5552
|
{ module: "Command", cmd: "pyright", params: ["--outputjson"] },
|
|
5414
5553
|
{ module: "Command", cmd: "ruff", params: ["check"] },
|
|
@@ -94,11 +94,12 @@ test(
|
|
|
94
94
|
);
|
|
95
95
|
|
|
96
96
|
test(
|
|
97
|
-
"release workflow
|
|
97
|
+
"release workflow pins Node.js 24.15.0, publishes to npm, and creates the GitHub release",
|
|
98
98
|
() => {
|
|
99
99
|
const workflow = readReleaseWorkflow();
|
|
100
100
|
|
|
101
|
-
assert.
|
|
101
|
+
assert.match(workflow, /NODE_VERSION:\s+'24\.15\.0'/);
|
|
102
|
+
assert.ok(workflow.includes("uses: actions/setup-node@v5"));
|
|
102
103
|
assert.ok(workflow.includes("registry-url: 'https://registry.npmjs.org'"));
|
|
103
104
|
assert.ok(workflow.includes("run: npm ci"));
|
|
104
105
|
assert.ok(workflow.includes("run: npm pkg delete private"));
|