pi-usereq 0.12.0 → 0.32.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 +13 -6
- package/.gitignore +1 -2
- package/CHANGELOG.md +145 -0
- package/README.md +1 -1
- package/package.json +1 -1
- package/pi-usereq/docs/REFERENCES.md +629 -396
- package/pi-usereq/docs/REQUIREMENTS.md +70 -46
- package/pi-usereq/docs/WORKFLOW.md +83 -42
- package/src/core/config.ts +553 -180
- package/src/core/debug-runtime.ts +24 -0
- package/src/core/extension-status.ts +2 -2
- package/src/core/path-context.ts +19 -4
- package/src/index.ts +248 -44
- package/tests/attended-results-scenarios.ts +10 -4
- package/tests/cli-command-option-parity.test.ts +28 -10
- package/tests/debug-extension-harness.test.ts +7 -9
- package/tests/extension-registration.test.ts +314 -123
- package/tests/helpers.ts +29 -6
- 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
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
createStaticCheckLanguageConfig,
|
|
17
17
|
DEFAULT_DOCS_DIR,
|
|
18
18
|
getDefaultConfig,
|
|
19
|
+
getGlobalConfigPath,
|
|
19
20
|
getProjectConfigPath,
|
|
20
21
|
loadConfig,
|
|
21
22
|
} from "../src/core/config.js";
|
|
@@ -23,6 +24,7 @@ import {
|
|
|
23
24
|
DEBUG_PROMPT_NAMES,
|
|
24
25
|
DEFAULT_DEBUG_LOG_FILE,
|
|
25
26
|
DEFAULT_DEBUG_LOG_ON_STATUS,
|
|
27
|
+
DEFAULT_DEBUG_TOOL_COMMANDS_ENABLED,
|
|
26
28
|
DEFAULT_DEBUG_WORKFLOW_EVENTS,
|
|
27
29
|
} from "../src/core/debug-runtime.js";
|
|
28
30
|
import { PROMPT_COMMAND_NAMES } from "../src/core/prompt-command-catalog.js";
|
|
@@ -58,7 +60,13 @@ import {
|
|
|
58
60
|
} from "../src/core/extension-status.js";
|
|
59
61
|
import { showPiUsereqSettingsMenu } from "../src/core/settings-menu.js";
|
|
60
62
|
import { getSupportedStaticCheckLanguageSupport } from "../src/core/static-check.js";
|
|
61
|
-
import {
|
|
63
|
+
import {
|
|
64
|
+
createTempDir,
|
|
65
|
+
initFixtureRepo,
|
|
66
|
+
readGlobalConfigJson,
|
|
67
|
+
readProjectConfigJson,
|
|
68
|
+
saveFixtureConfigs,
|
|
69
|
+
} from "./helpers.js";
|
|
62
70
|
|
|
63
71
|
/**
|
|
64
72
|
* @brief Describes one fake extension command registration captured during tests.
|
|
@@ -91,17 +99,16 @@ type RegisteredTool = {
|
|
|
91
99
|
type RegisteredShortcut = { description?: string; handler: (ctx: any) => Promise<void> | void };
|
|
92
100
|
|
|
93
101
|
/**
|
|
94
|
-
* @brief Applies targeted
|
|
95
|
-
* @details Loads the
|
|
102
|
+
* @brief Applies targeted effective-config overrides inside one test project.
|
|
103
|
+
* @details Loads the merged local/global config, merges the supplied top-level overrides, rewrites the split Node config files plus the oracle mirror, and commits the repository-local config artifacts when the fixture is a git repo. Runtime is O(n) in config size. Side effects include filesystem reads, file overwrite, and optional git commits.
|
|
96
104
|
* @param[in] projectBase {string} Fixture project root.
|
|
97
105
|
* @param[in] overrides {Record<string, unknown>} Top-level config overrides.
|
|
98
106
|
* @return {void} No return value.
|
|
99
107
|
*/
|
|
100
108
|
function writeProjectConfigOverrides(projectBase: string, overrides: Record<string, unknown>): void {
|
|
101
|
-
const
|
|
102
|
-
const config = JSON.parse(fs.readFileSync(configPath, "utf8")) as Record<string, unknown>;
|
|
109
|
+
const config = loadConfig(projectBase);
|
|
103
110
|
Object.assign(config, overrides);
|
|
104
|
-
|
|
111
|
+
saveFixtureConfigs(projectBase, config);
|
|
105
112
|
if (fs.existsSync(path.join(projectBase, ".git"))) {
|
|
106
113
|
const gitEnv = {
|
|
107
114
|
...process.env,
|
|
@@ -519,13 +526,13 @@ function buildExpectedFakeBasePath(cwd: string): string {
|
|
|
519
526
|
}
|
|
520
527
|
|
|
521
528
|
/**
|
|
522
|
-
* @brief Formats one expected home-relative
|
|
523
|
-
* @details Resolves the supplied
|
|
524
|
-
* @param[in]
|
|
525
|
-
* @return {string} Home-relative or absolute
|
|
529
|
+
* @brief Formats one expected home-relative display path.
|
|
530
|
+
* @details Resolves the supplied path, emits `~` or `~/...` when the path is inside the current user home directory, and otherwise returns the slash-normalized absolute path. Runtime is O(p) in path length. No external state is mutated.
|
|
531
|
+
* @param[in] targetPath {string} Absolute or relative path candidate.
|
|
532
|
+
* @return {string} Home-relative or absolute display path.
|
|
526
533
|
*/
|
|
527
|
-
function
|
|
528
|
-
const resolvedPath = path.resolve(
|
|
534
|
+
function buildExpectedDisplayPath(targetPath: string): string {
|
|
535
|
+
const resolvedPath = path.resolve(targetPath);
|
|
529
536
|
const homePath = path.resolve(process.env.HOME ?? os.homedir());
|
|
530
537
|
if (resolvedPath === homePath) {
|
|
531
538
|
return "~";
|
|
@@ -538,15 +545,32 @@ function buildExpectedNotifyBasePath(cwd: string): string {
|
|
|
538
545
|
}
|
|
539
546
|
|
|
540
547
|
/**
|
|
541
|
-
* @brief Formats
|
|
542
|
-
* @details
|
|
548
|
+
* @brief Formats one expected home-relative base path for notify-template assertions.
|
|
549
|
+
* @details Delegates to the generic display-path formatter so notify-template assertions match the runtime `~`-relative path contract. Runtime is O(p) in path length. No external state is mutated.
|
|
543
550
|
* @param[in] cwd {string} Runtime working directory.
|
|
544
|
-
* @return {string}
|
|
551
|
+
* @return {string} Home-relative or absolute base path used by notification templates.
|
|
552
|
+
*/
|
|
553
|
+
function buildExpectedNotifyBasePath(cwd: string): string {
|
|
554
|
+
return buildExpectedDisplayPath(cwd);
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
/**
|
|
558
|
+
* @brief Formats the expected `Show local configuration` path value.
|
|
559
|
+
* @details Resolves the current local project config path and rewrites a leading home-directory prefix to `~` so top-level menu assertions match the documented display-only substitution without changing stored paths. Runtime is O(p) in path length. No external state is mutated.
|
|
560
|
+
* @param[in] cwd {string} Runtime working directory.
|
|
561
|
+
* @return {string} `~`-relative or absolute local config-path display string.
|
|
562
|
+
*/
|
|
563
|
+
function buildExpectedShowLocalConfigPath(cwd: string): string {
|
|
564
|
+
return buildExpectedDisplayPath(getProjectConfigPath(cwd));
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
/**
|
|
568
|
+
* @brief Formats the expected `Show global configuration` path value.
|
|
569
|
+
* @details Resolves the current global config path and rewrites a leading home-directory prefix to `~` so top-level menu assertions match the documented display-only substitution without changing stored paths. Runtime is O(p) in path length. No external state is mutated.
|
|
570
|
+
* @return {string} `~`-relative or absolute global config-path display string.
|
|
545
571
|
*/
|
|
546
|
-
function
|
|
547
|
-
return
|
|
548
|
-
? `${buildExpectedNotifyBasePath(cwd)}/.pi-usereq.json`
|
|
549
|
-
: getProjectConfigPath(cwd).split(path.sep).join("/");
|
|
572
|
+
function buildExpectedShowGlobalConfigPath(): string {
|
|
573
|
+
return buildExpectedDisplayPath(getGlobalConfigPath());
|
|
550
574
|
}
|
|
551
575
|
|
|
552
576
|
/**
|
|
@@ -815,6 +839,10 @@ test("extension registers prompt and config commands while exposing tool capabil
|
|
|
815
839
|
"tokens",
|
|
816
840
|
"files-static-check",
|
|
817
841
|
"static-check",
|
|
842
|
+
"debug-compress",
|
|
843
|
+
"debug-references",
|
|
844
|
+
"debug-static-check",
|
|
845
|
+
"debug-tokens",
|
|
818
846
|
"pi-usereq-show-config",
|
|
819
847
|
"test-static-check",
|
|
820
848
|
]) {
|
|
@@ -839,6 +867,54 @@ test("extension registers prompt and config commands while exposing tool capabil
|
|
|
839
867
|
}
|
|
840
868
|
});
|
|
841
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
|
+
|
|
842
918
|
/**
|
|
843
919
|
* @brief Verifies `req-references` registration is independent from bundled prompt inventory.
|
|
844
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.
|
|
@@ -1505,19 +1581,15 @@ test("static-check tools return monolithic text payloads with minimal execution
|
|
|
1505
1581
|
assert.equal(staticCheckResult.details?.execution.code, 0);
|
|
1506
1582
|
assert.deepEqual(staticCheckResult.details?.execution.stderr_lines, undefined);
|
|
1507
1583
|
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
},
|
|
1518
|
-
}, null, 2)}\n`,
|
|
1519
|
-
"utf8",
|
|
1520
|
-
);
|
|
1584
|
+
saveFixtureConfigs(projectBase, {
|
|
1585
|
+
...getDefaultConfig(projectBase),
|
|
1586
|
+
"docs-dir": DEFAULT_DOCS_DIR,
|
|
1587
|
+
"tests-dir": "tests",
|
|
1588
|
+
"src-dir": ["src"],
|
|
1589
|
+
"static-check": {
|
|
1590
|
+
TypeScript: createStaticCheckLanguageConfig([{ module: "Dummy" }], "disable"),
|
|
1591
|
+
},
|
|
1592
|
+
});
|
|
1521
1593
|
const disabledFilesStaticCheckResult = await executeRegisteredTool(pi, "files-static-check", projectBase, {
|
|
1522
1594
|
files: ["src/check.ts"],
|
|
1523
1595
|
}) as {
|
|
@@ -1532,6 +1604,80 @@ test("static-check tools return monolithic text payloads with minimal execution
|
|
|
1532
1604
|
}
|
|
1533
1605
|
});
|
|
1534
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
|
+
|
|
1535
1681
|
test("configuration menu saves updated docs-dir in project config", async () => {
|
|
1536
1682
|
const cwd = createTempDir("pi-usereq-menu-");
|
|
1537
1683
|
fs.mkdirSync(path.dirname(getProjectConfigPath(cwd)), { recursive: true });
|
|
@@ -1541,7 +1687,7 @@ test("configuration menu saves updated docs-dir in project config", async () =>
|
|
|
1541
1687
|
assert.ok(command);
|
|
1542
1688
|
|
|
1543
1689
|
await command!.handler("", createFakeCtx(cwd, { selects: ["docs-dir", "Save and close"], inputs: ["docs/custom"] }));
|
|
1544
|
-
const config =
|
|
1690
|
+
const config = readProjectConfigJson(cwd);
|
|
1545
1691
|
assert.equal(config["docs-dir"], "docs/custom");
|
|
1546
1692
|
});
|
|
1547
1693
|
|
|
@@ -1566,7 +1712,7 @@ test("configuration menu persists trailing-slash-free relative directory values"
|
|
|
1566
1712
|
|
|
1567
1713
|
await command!.handler("", ctx);
|
|
1568
1714
|
|
|
1569
|
-
const config =
|
|
1715
|
+
const config = readProjectConfigJson(cwd);
|
|
1570
1716
|
assert.equal(config["docs-dir"], "docs/custom");
|
|
1571
1717
|
assert.equal(config["tests-dir"], "tests/custom");
|
|
1572
1718
|
assert.deepEqual(config["src-dir"], ["src", "src/custom"]);
|
|
@@ -1586,12 +1732,14 @@ test("configuration menu forces worktree rows off and dim when auto git commit i
|
|
|
1586
1732
|
|
|
1587
1733
|
await command!.handler("", ctx);
|
|
1588
1734
|
|
|
1589
|
-
const
|
|
1735
|
+
const localConfig = readProjectConfigJson(cwd);
|
|
1736
|
+
const globalConfig = readGlobalConfigJson();
|
|
1590
1737
|
const defaultConfig = getDefaultConfig(cwd);
|
|
1591
1738
|
const renderedLockedMenu = (ctx.__state.customRenderLines[1] ?? []).join("\n");
|
|
1592
|
-
assert.equal(
|
|
1593
|
-
assert.equal(
|
|
1594
|
-
assert.equal(
|
|
1739
|
+
assert.equal(globalConfig.AUTO_GIT_COMMIT, "disable");
|
|
1740
|
+
assert.equal(globalConfig.GIT_WORKTREE_ENABLED, "disable");
|
|
1741
|
+
assert.equal(globalConfig.GIT_WORKTREE_PREFIX, defaultConfig.GIT_WORKTREE_PREFIX);
|
|
1742
|
+
assert.equal(Object.hasOwn(localConfig, "AUTO_GIT_COMMIT"), false);
|
|
1595
1743
|
assert.match(renderedLockedMenu, /<dim>Git worktree/);
|
|
1596
1744
|
assert.match(renderedLockedMenu, /<dim>Worktree prefix/);
|
|
1597
1745
|
});
|
|
@@ -1612,7 +1760,7 @@ test("configuration menu omits prompt-delivery controls and persisted config omi
|
|
|
1612
1760
|
assert.equal(fs.existsSync(getProjectConfigPath(cwd)), false);
|
|
1613
1761
|
});
|
|
1614
1762
|
|
|
1615
|
-
test("configuration menus expose
|
|
1763
|
+
test("configuration menus expose local/global config ordering and omit overview or notification-reference rows", async () => {
|
|
1616
1764
|
const cwd = fs.mkdtempSync(
|
|
1617
1765
|
path.join(process.env.HOME ?? process.cwd(), "pi-usereq-menu-structure-"),
|
|
1618
1766
|
);
|
|
@@ -1638,10 +1786,18 @@ test("configuration menus expose show-config ordering and omit overview or notif
|
|
|
1638
1786
|
"Enable tools",
|
|
1639
1787
|
"Notifications",
|
|
1640
1788
|
"Debug",
|
|
1641
|
-
"Show configuration",
|
|
1789
|
+
"Show local configuration",
|
|
1790
|
+
"Show global configuration",
|
|
1642
1791
|
"Reset defaults",
|
|
1643
1792
|
]);
|
|
1644
|
-
assert.
|
|
1793
|
+
assert.match(
|
|
1794
|
+
renderedMenu,
|
|
1795
|
+
new RegExp(`<dim>${buildExpectedShowLocalConfigPath(cwd).replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}</dim>`),
|
|
1796
|
+
);
|
|
1797
|
+
assert.match(
|
|
1798
|
+
renderedMenu,
|
|
1799
|
+
new RegExp(`<dim>${buildExpectedShowGlobalConfigPath().replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}</dim>`),
|
|
1800
|
+
);
|
|
1645
1801
|
assert.ok(renderedMenu.includes("notification:off • sound:none • pushover:off"));
|
|
1646
1802
|
assert.ok(renderedMenu.includes("disable"));
|
|
1647
1803
|
assert.doesNotMatch(renderedMenu, /beep:/);
|
|
@@ -1668,15 +1824,15 @@ test("configuration menus expose show-config ordering and omit overview or notif
|
|
|
1668
1824
|
]);
|
|
1669
1825
|
});
|
|
1670
1826
|
|
|
1671
|
-
test("show configuration saves pending config, closes the menu, and writes the persisted
|
|
1672
|
-
const cwd = createTempDir("pi-usereq-menu-show-config-");
|
|
1827
|
+
test("show local configuration saves pending config, closes the menu, and writes the persisted local config file text into the editor", async () => {
|
|
1828
|
+
const cwd = createTempDir("pi-usereq-menu-show-local-config-");
|
|
1673
1829
|
fs.mkdirSync(path.dirname(getProjectConfigPath(cwd)), { recursive: true });
|
|
1674
1830
|
const pi = createFakePi();
|
|
1675
1831
|
piUsereqExtension(pi);
|
|
1676
1832
|
const command = pi.commands.get("pi-usereq");
|
|
1677
1833
|
assert.ok(command);
|
|
1678
1834
|
const ctx = createFakeCtx(cwd, {
|
|
1679
|
-
selects: ["docs-dir", "show-config"],
|
|
1835
|
+
selects: ["docs-dir", "show-local-config"],
|
|
1680
1836
|
inputs: ["docs/custom"],
|
|
1681
1837
|
});
|
|
1682
1838
|
|
|
@@ -1691,6 +1847,28 @@ test("show configuration saves pending config, closes the menu, and writes the p
|
|
|
1691
1847
|
);
|
|
1692
1848
|
});
|
|
1693
1849
|
|
|
1850
|
+
test("show global configuration saves pending config, closes the menu, and writes the persisted global config file text into the editor", async () => {
|
|
1851
|
+
const cwd = createTempDir("pi-usereq-menu-show-global-config-");
|
|
1852
|
+
fs.mkdirSync(path.dirname(getProjectConfigPath(cwd)), { recursive: true });
|
|
1853
|
+
const pi = createFakePi();
|
|
1854
|
+
piUsereqExtension(pi);
|
|
1855
|
+
const command = pi.commands.get("pi-usereq");
|
|
1856
|
+
assert.ok(command);
|
|
1857
|
+
const ctx = createFakeCtx(cwd, {
|
|
1858
|
+
selects: ["Auto git commit", "show-global-config"],
|
|
1859
|
+
});
|
|
1860
|
+
|
|
1861
|
+
await command!.handler("", ctx);
|
|
1862
|
+
|
|
1863
|
+
const persistedText = fs.readFileSync(getGlobalConfigPath(), "utf8");
|
|
1864
|
+
assert.equal(ctx.__state.editorText, persistedText);
|
|
1865
|
+
assert.match(ctx.__state.editorText, /"AUTO_GIT_COMMIT": "disable"/);
|
|
1866
|
+
assert.equal(
|
|
1867
|
+
ctx.__state.selectCalls.filter((call) => call.title === "pi-usereq").length,
|
|
1868
|
+
2,
|
|
1869
|
+
);
|
|
1870
|
+
});
|
|
1871
|
+
|
|
1694
1872
|
test("descendant configuration menus end with Reset defaults only", async () => {
|
|
1695
1873
|
const cwd = createTempDir("pi-usereq-menu-terminal-rows-");
|
|
1696
1874
|
fs.mkdirSync(path.dirname(getProjectConfigPath(cwd)), { recursive: true });
|
|
@@ -1770,6 +1948,7 @@ test("debug menu dims locked rows and persists debug settings with focus-preserv
|
|
|
1770
1948
|
"Debug",
|
|
1771
1949
|
"Debug",
|
|
1772
1950
|
"Log file",
|
|
1951
|
+
"Enable debug commands for tools",
|
|
1773
1952
|
"Log on status",
|
|
1774
1953
|
"any",
|
|
1775
1954
|
"Status changes",
|
|
@@ -1785,35 +1964,43 @@ test("debug menu dims locked rows and persists debug settings with focus-preserv
|
|
|
1785
1964
|
await command!.handler("", ctx);
|
|
1786
1965
|
|
|
1787
1966
|
const renderedInitialDebugMenu = (ctx.__state.customRenderLines[1] ?? []).join("\n");
|
|
1788
|
-
const persistedConfig =
|
|
1967
|
+
const persistedConfig = readProjectConfigJson(cwd);
|
|
1789
1968
|
const debugCalls = ctx.__state.selectCalls.filter((call) => call.title === "Debug");
|
|
1790
1969
|
assert.match(renderedInitialDebugMenu, /<dim>Log file/);
|
|
1970
|
+
assert.match(renderedInitialDebugMenu, /<dim>Enable debug commands for tools/);
|
|
1791
1971
|
assert.match(renderedInitialDebugMenu, /<dim>Log on status/);
|
|
1792
1972
|
assert.match(renderedInitialDebugMenu, /<dim>Status changes/);
|
|
1793
1973
|
assert.match(renderedInitialDebugMenu, /<dim>Workflow events/);
|
|
1794
1974
|
assert.match(renderedInitialDebugMenu, /<dim>compress/);
|
|
1795
1975
|
assert.equal(persistedConfig.DEBUG_ENABLED, "enable");
|
|
1796
1976
|
assert.equal(persistedConfig.DEBUG_LOG_FILE, "logs/debug-output.json");
|
|
1977
|
+
assert.equal(persistedConfig.DEBUG_TOOL_COMMANDS_ENABLED, "enable");
|
|
1797
1978
|
assert.equal(persistedConfig.DEBUG_STATUS_CHANGES, "enable");
|
|
1798
1979
|
assert.equal(persistedConfig.DEBUG_WORKFLOW_EVENTS, "enable");
|
|
1799
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"));
|
|
1800
1985
|
assert.deepEqual(persistedConfig.DEBUG_ENABLED_TOOLS, ["files-tokens"]);
|
|
1801
1986
|
assert.deepEqual(persistedConfig.DEBUG_ENABLED_PROMPTS, ["req-analyze"]);
|
|
1802
1987
|
assert.equal(debugCalls[1]?.selectedChoiceId, "debug-enabled");
|
|
1803
1988
|
assert.equal(debugCalls[2]?.selectedChoiceId, "debug-log-file");
|
|
1804
|
-
assert.equal(debugCalls[3]?.selectedChoiceId, "debug-
|
|
1805
|
-
assert.equal(debugCalls[4]?.selectedChoiceId, "debug-status
|
|
1806
|
-
assert.equal(debugCalls[5]?.selectedChoiceId, "debug-
|
|
1807
|
-
assert.equal(debugCalls[6]?.selectedChoiceId, "debug-
|
|
1808
|
-
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");
|
|
1809
1995
|
|
|
1810
1996
|
const resetCtx = createFakeCtx(cwd, {
|
|
1811
1997
|
selects: ["Debug", "Reset defaults", "Approve reset", "Save and close"],
|
|
1812
1998
|
});
|
|
1813
1999
|
await command!.handler("", resetCtx);
|
|
1814
|
-
const resetConfig =
|
|
2000
|
+
const resetConfig = readProjectConfigJson(cwd);
|
|
1815
2001
|
assert.equal(resetConfig.DEBUG_ENABLED, "disable");
|
|
1816
2002
|
assert.equal(resetConfig.DEBUG_LOG_FILE, DEFAULT_DEBUG_LOG_FILE);
|
|
2003
|
+
assert.equal(resetConfig.DEBUG_TOOL_COMMANDS_ENABLED, DEFAULT_DEBUG_TOOL_COMMANDS_ENABLED);
|
|
1817
2004
|
assert.equal(resetConfig.DEBUG_STATUS_CHANGES, "disable");
|
|
1818
2005
|
assert.equal(resetConfig.DEBUG_WORKFLOW_EVENTS, DEFAULT_DEBUG_WORKFLOW_EVENTS);
|
|
1819
2006
|
assert.equal(resetConfig.DEBUG_LOG_ON_STATUS, DEFAULT_DEBUG_LOG_ON_STATUS);
|
|
@@ -1837,6 +2024,7 @@ test("debug menu rows derive from canonical tool and prompt inventories", async
|
|
|
1837
2024
|
assert.deepEqual(ctx.__state.selectCalls[1]?.items ?? [], [
|
|
1838
2025
|
"Debug",
|
|
1839
2026
|
"Log file",
|
|
2027
|
+
"Enable debug commands for tools",
|
|
1840
2028
|
"Log on status",
|
|
1841
2029
|
"Status changes",
|
|
1842
2030
|
"Workflow events",
|
|
@@ -1856,17 +2044,13 @@ test("debug menu rows derive from canonical tool and prompt inventories", async
|
|
|
1856
2044
|
test("tool debug logging honors global enablement and workflow-status filters", async () => {
|
|
1857
2045
|
const idleBase = createTempDir("pi-usereq-tool-debug-idle-");
|
|
1858
2046
|
fs.mkdirSync(path.dirname(getProjectConfigPath(idleBase)), { recursive: true });
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
DEBUG_ENABLED_TOOLS: ["bash"],
|
|
1867
|
-
}, null, 2)}\n`,
|
|
1868
|
-
"utf8",
|
|
1869
|
-
);
|
|
2047
|
+
saveFixtureConfigs(idleBase, {
|
|
2048
|
+
...getDefaultConfig(idleBase),
|
|
2049
|
+
DEBUG_ENABLED: "enable",
|
|
2050
|
+
DEBUG_LOG_FILE: "tool-debug.json",
|
|
2051
|
+
DEBUG_LOG_ON_STATUS: "any",
|
|
2052
|
+
DEBUG_ENABLED_TOOLS: ["bash"],
|
|
2053
|
+
});
|
|
1870
2054
|
const idlePi = createFakePi();
|
|
1871
2055
|
piUsereqExtension(idlePi);
|
|
1872
2056
|
const idleCtx = createFakeCtx(idleBase);
|
|
@@ -1927,17 +2111,13 @@ test("tool debug logging honors global enablement and workflow-status filters",
|
|
|
1927
2111
|
|
|
1928
2112
|
const disabledBase = createTempDir("pi-usereq-tool-debug-disabled-");
|
|
1929
2113
|
fs.mkdirSync(path.dirname(getProjectConfigPath(disabledBase)), { recursive: true });
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
DEBUG_ENABLED_TOOLS: ["bash"],
|
|
1938
|
-
}, null, 2)}\n`,
|
|
1939
|
-
"utf8",
|
|
1940
|
-
);
|
|
2114
|
+
saveFixtureConfigs(disabledBase, {
|
|
2115
|
+
...getDefaultConfig(disabledBase),
|
|
2116
|
+
DEBUG_ENABLED: "disable",
|
|
2117
|
+
DEBUG_LOG_FILE: "tool-debug.json",
|
|
2118
|
+
DEBUG_LOG_ON_STATUS: "any",
|
|
2119
|
+
DEBUG_ENABLED_TOOLS: ["bash"],
|
|
2120
|
+
});
|
|
1941
2121
|
const disabledPi = createFakePi();
|
|
1942
2122
|
piUsereqExtension(disabledPi);
|
|
1943
2123
|
const disabledCtx = createFakeCtx(disabledBase);
|
|
@@ -2181,11 +2361,12 @@ test("notifications reset defaults preserves non-notification settings", async (
|
|
|
2181
2361
|
|
|
2182
2362
|
await command!.handler("", ctx);
|
|
2183
2363
|
|
|
2184
|
-
const
|
|
2185
|
-
|
|
2186
|
-
assert.equal(
|
|
2187
|
-
assert.equal(
|
|
2188
|
-
assert.equal(
|
|
2364
|
+
const localConfig = readProjectConfigJson(cwd);
|
|
2365
|
+
const globalConfig = readGlobalConfigJson();
|
|
2366
|
+
assert.equal(localConfig["docs-dir"], "docs/custom");
|
|
2367
|
+
assert.equal(globalConfig["notify-enabled"], false);
|
|
2368
|
+
assert.equal(globalConfig["notify-sound"], "none");
|
|
2369
|
+
assert.equal(globalConfig["notify-pushover-enabled"], false);
|
|
2189
2370
|
});
|
|
2190
2371
|
|
|
2191
2372
|
test("top-level reset defaults restores all configuration values", async () => {
|
|
@@ -2210,11 +2391,12 @@ test("top-level reset defaults restores all configuration values", async () => {
|
|
|
2210
2391
|
|
|
2211
2392
|
await command!.handler("", ctx);
|
|
2212
2393
|
|
|
2213
|
-
const
|
|
2214
|
-
|
|
2215
|
-
assert.
|
|
2216
|
-
assert.
|
|
2217
|
-
assert.equal(
|
|
2394
|
+
const localConfig = readProjectConfigJson(cwd);
|
|
2395
|
+
const globalConfig = readGlobalConfigJson();
|
|
2396
|
+
assert.equal(localConfig["docs-dir"], DEFAULT_DOCS_DIR);
|
|
2397
|
+
assert.deepEqual(localConfig["src-dir"], ["src"]);
|
|
2398
|
+
assert.equal(globalConfig["notify-enabled"], false);
|
|
2399
|
+
assert.equal(globalConfig["notify-sound"], "none");
|
|
2218
2400
|
});
|
|
2219
2401
|
|
|
2220
2402
|
test("configuration menus reuse CLI settings theme semantics", async () => {
|
|
@@ -4795,17 +4977,11 @@ test("prompt commands remain functional when custom tool registrations are remov
|
|
|
4795
4977
|
test("session_start applies configured pi-usereq startup tools", async () => {
|
|
4796
4978
|
const cwd = createTempDir("pi-usereq-tools-startup-");
|
|
4797
4979
|
fs.mkdirSync(path.dirname(getProjectConfigPath(cwd)), { recursive: true });
|
|
4798
|
-
|
|
4799
|
-
|
|
4800
|
-
|
|
4801
|
-
|
|
4802
|
-
|
|
4803
|
-
"src-dir": ["src", "foobar"],
|
|
4804
|
-
"static-check": {},
|
|
4805
|
-
"enabled-tools": ["files-tokens", "static-check"],
|
|
4806
|
-
}, null, 2)}\n`,
|
|
4807
|
-
"utf8",
|
|
4808
|
-
);
|
|
4980
|
+
saveFixtureConfigs(cwd, {
|
|
4981
|
+
...getDefaultConfig(cwd),
|
|
4982
|
+
"src-dir": ["src", "foobar"],
|
|
4983
|
+
"enabled-tools": ["files-tokens", "static-check"],
|
|
4984
|
+
});
|
|
4809
4985
|
|
|
4810
4986
|
const pi = createFakePi();
|
|
4811
4987
|
piUsereqExtension(pi);
|
|
@@ -4915,7 +5091,7 @@ test("stale replacement contexts do not break workflow-state rendering", () => {
|
|
|
4915
5091
|
const cwd = createTempDir("pi-usereq-stale-render-");
|
|
4916
5092
|
fs.mkdirSync(path.dirname(getProjectConfigPath(cwd)), { recursive: true });
|
|
4917
5093
|
const controller = createPiUsereqStatusController();
|
|
4918
|
-
setPiUsereqStatusConfig(controller, getDefaultConfig());
|
|
5094
|
+
setPiUsereqStatusConfig(controller, getDefaultConfig(cwd));
|
|
4919
5095
|
const staleCtx = createFakeCtx(cwd);
|
|
4920
5096
|
Object.defineProperty(staleCtx, "ui", {
|
|
4921
5097
|
configurable: true,
|
|
@@ -5296,7 +5472,9 @@ test("session_start enables default custom tools and default embedded tools only
|
|
|
5296
5472
|
});
|
|
5297
5473
|
|
|
5298
5474
|
test("default configuration applies the documented static-check, debug, notify, sound, and pushover defaults", () => {
|
|
5299
|
-
const
|
|
5475
|
+
const cwd = createTempDir("pi-usereq-default-notify-");
|
|
5476
|
+
const config = getDefaultConfig(cwd);
|
|
5477
|
+
saveFixtureConfigs(cwd, config);
|
|
5300
5478
|
|
|
5301
5479
|
assert.equal(config.AUTO_GIT_COMMIT, "enable");
|
|
5302
5480
|
assert.deepEqual(Object.keys(config["static-check"]).length, 20);
|
|
@@ -5345,6 +5523,7 @@ test("default configuration applies the documented static-check, debug, notify,
|
|
|
5345
5523
|
assert.deepEqual(config["static-check"].Ruby, createStaticCheckLanguageConfig([], "disable"));
|
|
5346
5524
|
assert.equal(config.DEBUG_ENABLED, "disable");
|
|
5347
5525
|
assert.equal(config.DEBUG_LOG_FILE, DEFAULT_DEBUG_LOG_FILE);
|
|
5526
|
+
assert.equal(config.DEBUG_TOOL_COMMANDS_ENABLED, DEFAULT_DEBUG_TOOL_COMMANDS_ENABLED);
|
|
5348
5527
|
assert.equal(config.DEBUG_WORKFLOW_EVENTS, DEFAULT_DEBUG_WORKFLOW_EVENTS);
|
|
5349
5528
|
assert.equal(config.DEBUG_LOG_ON_STATUS, DEFAULT_DEBUG_LOG_ON_STATUS);
|
|
5350
5529
|
assert.deepEqual(config.DEBUG_ENABLED_TOOLS, []);
|
|
@@ -5363,6 +5542,22 @@ test("default configuration applies the documented static-check, debug, notify,
|
|
|
5363
5542
|
assert.equal(config["notify-pushover-on-failed"], false);
|
|
5364
5543
|
assert.equal(config.PI_NOTIFY_CMD, 'notify-send -i %%INSTALLATION_PATH%%/resources/images/pi.dev.png -a "PI-useReq" "%%PROMT%% @ %%BASE%% [%%TIME%%]" "%%RESULT%%"');
|
|
5365
5544
|
assert.equal(config["notify-pushover-text"], "%%RESULT%%\n%%ARGS%%");
|
|
5545
|
+
|
|
5546
|
+
const localConfig = readProjectConfigJson(cwd);
|
|
5547
|
+
const globalConfig = readGlobalConfigJson();
|
|
5548
|
+
assert.equal((localConfig["static-check"] as Record<string, any>).Python.enabled, "enable");
|
|
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);
|
|
5551
|
+
assert.deepEqual((globalConfig["static-check"] as Record<string, any>).Python.checkers, [
|
|
5552
|
+
{ module: "Command", cmd: "pyright", params: ["--outputjson"] },
|
|
5553
|
+
{ module: "Command", cmd: "ruff", params: ["check"] },
|
|
5554
|
+
]);
|
|
5555
|
+
assert.deepEqual((globalConfig["static-check"] as Record<string, any>).Ruby.checkers, []);
|
|
5556
|
+
assert.equal(globalConfig.AUTO_GIT_COMMIT, "enable");
|
|
5557
|
+
assert.equal(globalConfig.GIT_WORKTREE_ENABLED, "enable");
|
|
5558
|
+
assert.equal(globalConfig["notify-enabled"], false);
|
|
5559
|
+
assert.equal(Object.hasOwn(localConfig, "notify-enabled"), false);
|
|
5560
|
+
assert.equal(Object.hasOwn(localConfig, "enabled-tools"), false);
|
|
5366
5561
|
});
|
|
5367
5562
|
|
|
5368
5563
|
test("configuration menu orders tool toggles and can enable embedded builtin tools", async () => {
|
|
@@ -5378,7 +5573,7 @@ test("configuration menu orders tool toggles and can enable embedded builtin too
|
|
|
5378
5573
|
|
|
5379
5574
|
await command!.handler("", ctx);
|
|
5380
5575
|
|
|
5381
|
-
const config =
|
|
5576
|
+
const config = readGlobalConfigJson();
|
|
5382
5577
|
assert.deepEqual(ctx.__state.selectCalls[2]?.items ?? [], [
|
|
5383
5578
|
"compress",
|
|
5384
5579
|
"references",
|
|
@@ -5419,7 +5614,7 @@ test("configuration menu can disable configurable active tools", async () => {
|
|
|
5419
5614
|
}),
|
|
5420
5615
|
);
|
|
5421
5616
|
|
|
5422
|
-
const config =
|
|
5617
|
+
const config = readGlobalConfigJson();
|
|
5423
5618
|
assert.deepEqual(config["enabled-tools"], []);
|
|
5424
5619
|
assert.deepEqual(pi.getActiveTools().filter((toolName: string) => PI_USEREQ_STARTUP_TOOL_NAMES.includes(toolName as never)), []);
|
|
5425
5620
|
});
|
|
@@ -5463,7 +5658,7 @@ test("configuration menu can persist notify and boot-sound settings without chan
|
|
|
5463
5658
|
await pi.emit("session_start", { reason: "startup" }, ctx);
|
|
5464
5659
|
await command!.handler("", ctx);
|
|
5465
5660
|
|
|
5466
|
-
const config =
|
|
5661
|
+
const config = readGlobalConfigJson();
|
|
5467
5662
|
assert.equal(config["notify-enabled"], true);
|
|
5468
5663
|
assert.equal(config["notify-on-completed"], true);
|
|
5469
5664
|
assert.equal(config["notify-on-interrupted"], false);
|
|
@@ -5524,7 +5719,7 @@ test("configuration menu can persist pushover settings", async () => {
|
|
|
5524
5719
|
|
|
5525
5720
|
await command!.handler("", ctx);
|
|
5526
5721
|
|
|
5527
|
-
const config =
|
|
5722
|
+
const config = readGlobalConfigJson();
|
|
5528
5723
|
assert.equal(config["notify-pushover-enabled"], true);
|
|
5529
5724
|
assert.equal(config["notify-pushover-on-completed"], true);
|
|
5530
5725
|
assert.equal(config["notify-pushover-on-interrupted"], false);
|
|
@@ -5551,14 +5746,10 @@ test("configuration menu can persist pushover settings", async () => {
|
|
|
5551
5746
|
test("notifications menu escapes pushover text and locks pushover enablement until credentials exist", async () => {
|
|
5552
5747
|
const cwd = createTempDir("pi-usereq-menu-pushover-display-");
|
|
5553
5748
|
fs.mkdirSync(path.dirname(getProjectConfigPath(cwd)), { recursive: true });
|
|
5554
|
-
|
|
5555
|
-
|
|
5556
|
-
|
|
5557
|
-
|
|
5558
|
-
"notify-pushover-text": "first line\nsecond line",
|
|
5559
|
-
}, null, 2)}\n`,
|
|
5560
|
-
"utf8",
|
|
5561
|
-
);
|
|
5749
|
+
saveFixtureConfigs(cwd, {
|
|
5750
|
+
...getDefaultConfig(cwd),
|
|
5751
|
+
"notify-pushover-text": "first line\nsecond line",
|
|
5752
|
+
});
|
|
5562
5753
|
const pi = createFakePi();
|
|
5563
5754
|
piUsereqExtension(pi);
|
|
5564
5755
|
const command = pi.commands.get("pi-usereq");
|
|
@@ -5784,7 +5975,7 @@ test("pushover global enable controls status and suppresses delivery when disabl
|
|
|
5784
5975
|
await command!.handler("", ctx);
|
|
5785
5976
|
writeProjectConfigOverrides(cwd, { GIT_WORKTREE_ENABLED: "disable" });
|
|
5786
5977
|
|
|
5787
|
-
const config =
|
|
5978
|
+
const config = readGlobalConfigJson();
|
|
5788
5979
|
assert.equal(config["notify-pushover-enabled"], false);
|
|
5789
5980
|
assert.equal(config["notify-pushover-on-completed"], true);
|
|
5790
5981
|
assert.equal(
|
|
@@ -6038,8 +6229,9 @@ test("sound routing honors the selected sound state and per-event toggles", asyn
|
|
|
6038
6229
|
test("sound toggle shortcut cycles active runtime pi-notify sound levels without persisting boot config", async () => {
|
|
6039
6230
|
const cwd = createTempDir("pi-usereq-shortcut-notify-");
|
|
6040
6231
|
fs.mkdirSync(path.dirname(getProjectConfigPath(cwd)), { recursive: true });
|
|
6041
|
-
const
|
|
6042
|
-
|
|
6232
|
+
const persistedConfig = getDefaultConfig(cwd);
|
|
6233
|
+
saveFixtureConfigs(cwd, persistedConfig);
|
|
6234
|
+
const persistedGlobalConfigText = fs.readFileSync(getGlobalConfigPath(), "utf8");
|
|
6043
6235
|
const previousCwd = process.cwd();
|
|
6044
6236
|
const pi = createFakePi();
|
|
6045
6237
|
process.chdir(cwd);
|
|
@@ -6056,7 +6248,7 @@ test("sound toggle shortcut cycles active runtime pi-notify sound levels without
|
|
|
6056
6248
|
|
|
6057
6249
|
for (const expectedSound of ["low", "mid", "high", "none"]) {
|
|
6058
6250
|
await shortcut!.handler(ctx);
|
|
6059
|
-
assert.equal(fs.readFileSync(
|
|
6251
|
+
assert.equal(fs.readFileSync(getGlobalConfigPath(), "utf8"), persistedGlobalConfigText);
|
|
6060
6252
|
assert.equal(
|
|
6061
6253
|
ctx.__state.statuses.get("pi-usereq"),
|
|
6062
6254
|
buildExpectedFakeStatusText({
|
|
@@ -6077,11 +6269,7 @@ test("session_start loads the active runtime sound from persisted boot config",
|
|
|
6077
6269
|
fs.mkdirSync(path.dirname(getProjectConfigPath(cwd)), { recursive: true });
|
|
6078
6270
|
const persistedConfig = getDefaultConfig(cwd);
|
|
6079
6271
|
persistedConfig["notify-sound"] = "mid";
|
|
6080
|
-
|
|
6081
|
-
getProjectConfigPath(cwd),
|
|
6082
|
-
`${JSON.stringify(persistedConfig, null, 2)}\n`,
|
|
6083
|
-
"utf8",
|
|
6084
|
-
);
|
|
6272
|
+
saveFixtureConfigs(cwd, persistedConfig);
|
|
6085
6273
|
const previousCwd = process.cwd();
|
|
6086
6274
|
const pi = createFakePi();
|
|
6087
6275
|
process.chdir(cwd);
|
|
@@ -6180,9 +6368,10 @@ test("configuration menu can toggle per-language static-check enablement", async
|
|
|
6180
6368
|
}),
|
|
6181
6369
|
);
|
|
6182
6370
|
|
|
6183
|
-
const
|
|
6184
|
-
|
|
6185
|
-
assert.
|
|
6371
|
+
const localConfig = readProjectConfigJson(cwd);
|
|
6372
|
+
const globalConfig = readGlobalConfigJson();
|
|
6373
|
+
assert.equal((localConfig["static-check"] as Record<string, any>).Python.enabled, "disable");
|
|
6374
|
+
assert.deepEqual((globalConfig["static-check"] as Record<string, any>).Python.checkers, [
|
|
6186
6375
|
{ module: "Command", cmd: "pyright", params: ["--outputjson"] },
|
|
6187
6376
|
{ module: "Command", cmd: "ruff", params: ["check"] },
|
|
6188
6377
|
]);
|
|
@@ -6210,8 +6399,10 @@ test("configuration menu can add guided static-check entries for explicit suppor
|
|
|
6210
6399
|
}),
|
|
6211
6400
|
);
|
|
6212
6401
|
|
|
6213
|
-
const
|
|
6214
|
-
|
|
6402
|
+
const localConfig = readProjectConfigJson(cwd);
|
|
6403
|
+
const globalConfig = readGlobalConfigJson();
|
|
6404
|
+
assert.equal((localConfig["static-check"] as Record<string, any>).Ruby.enabled, "enable");
|
|
6405
|
+
assert.deepEqual((globalConfig["static-check"] as Record<string, any>).Ruby.checkers, [
|
|
6215
6406
|
{ module: "Command", cmd: "true" },
|
|
6216
|
-
]
|
|
6407
|
+
]);
|
|
6217
6408
|
});
|