pi-usereq 0.4.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.gitignore +3 -0
- package/CHANGELOG.md +41 -0
- package/README.md +1 -1
- package/package.json +1 -1
- package/{req → pi-usereq}/docs/REFERENCES.md +499 -491
- package/{req → pi-usereq}/docs/REQUIREMENTS.md +84 -62
- package/{req → pi-usereq}/docs/WORKFLOW.md +87 -102
- package/src/core/agent-tool-json.ts +42 -193
- package/src/core/compress-payload.ts +7 -15
- package/src/core/config.ts +39 -12
- package/src/core/extension-status.ts +94 -58
- package/src/core/find-payload.ts +21 -44
- package/src/core/pi-notify.ts +198 -11
- package/src/core/reference-payload.ts +6 -14
- package/src/core/runtime-project-paths.ts +3 -32
- package/src/core/settings-menu.ts +9 -4
- package/src/core/static-check.ts +35 -171
- package/src/core/token-counter.ts +3 -121
- package/src/index.ts +339 -180
- package/tests/attended-results-scenarios.ts +3 -13
- package/tests/cli-command-option-parity.test.ts +21 -12
- package/tests/debug-extension-harness.test.ts +20 -22
- package/tests/extension-registration.test.ts +394 -157
- package/tests/oracle-project.test.ts +8 -3
- package/tests/oracle-standalone.test.ts +7 -13
- package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_c.c.json +0 -5
- package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_cpp.cpp.json +0 -5
- package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_csharp.cs.json +0 -5
- package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_elixir.ex.json +0 -5
- package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_go.go.json +0 -5
- package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_haskell.hs.json +0 -5
- package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_java.java.json +0 -5
- package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_javascript.js.json +0 -5
- package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_kotlin.kt.json +0 -5
- package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_lua.lua.json +0 -5
- package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_perl.pl.json +0 -5
- package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_php.php.json +0 -5
- package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_python.py.json +0 -5
- package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_ruby.rb.json +0 -5
- package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_rust.rs.json +0 -5
- package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_scala.scala.json +0 -5
- package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_shell.sh.json +0 -5
- package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_swift.swift.json +0 -5
- package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_typescript.ts.json +0 -5
- package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_zig.zig.json +0 -5
- package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_c.c.json +0 -5
- package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_cpp.cpp.json +0 -5
- package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_csharp.cs.json +0 -5
- package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_elixir.ex.json +0 -5
- package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_go.go.json +0 -5
- package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_haskell.hs.json +0 -5
- package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_java.java.json +0 -5
- package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_javascript.js.json +0 -5
- package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_kotlin.kt.json +0 -5
- package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_lua.lua.json +0 -5
- package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_perl.pl.json +0 -5
- package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_php.php.json +0 -5
- package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_python.py.json +0 -5
- package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_ruby.rb.json +0 -5
- package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_rust.rs.json +0 -5
- package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_scala.scala.json +0 -5
- package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_shell.sh.json +0 -5
- package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_swift.swift.json +0 -5
- package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_typescript.ts.json +0 -5
- package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_zig.zig.json +0 -5
|
@@ -4,7 +4,13 @@ import { spawnSync } from "node:child_process";
|
|
|
4
4
|
import fs from "node:fs";
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import piUsereqExtension from "../src/index.ts";
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
DEFAULT_DOCS_DIR,
|
|
9
|
+
getDefaultConfig,
|
|
10
|
+
getProjectConfigPath,
|
|
11
|
+
} from "../src/core/config.js";
|
|
12
|
+
import { formatRuntimePathForDisplay } from "../src/core/path-context.js";
|
|
13
|
+
import { setPiNotifyHttpsRequestForTests } from "../src/core/pi-notify.js";
|
|
8
14
|
import {
|
|
9
15
|
PI_USEREQ_DEFAULT_ENABLED_TOOL_NAMES,
|
|
10
16
|
PI_USEREQ_EMBEDDED_TOOL_NAMES,
|
|
@@ -284,9 +290,9 @@ function formatFakeThemeBackgroundFromForeground(color: string, text: string): s
|
|
|
284
290
|
|
|
285
291
|
/**
|
|
286
292
|
* @brief Builds the expected fake context-bar payload for assertions.
|
|
287
|
-
* @details Resolves the empty-state
|
|
288
|
-
* non-positive percent, resolves the
|
|
289
|
-
* above 90, and otherwise reconstructs the
|
|
293
|
+
* @details Resolves the empty-state `◀ CLEAR ▶ ` overlay for unavailable or
|
|
294
|
+
* non-positive percent, resolves the centered ` ◀ FULL ▶ ` overlay for percent
|
|
295
|
+
* above 90, and otherwise reconstructs the 10-cell bar. Runtime is O(1). No
|
|
290
296
|
* external state is mutated.
|
|
291
297
|
* @param[in] options {{ filledCells: number; percent?: number | null }} Expected context-bar facts.
|
|
292
298
|
* @return {string} Encoded context-bar string.
|
|
@@ -298,16 +304,16 @@ function buildExpectedFakeContextBar(options: {
|
|
|
298
304
|
if (options.percent === undefined || options.percent === null || options.percent <= 0) {
|
|
299
305
|
return formatFakeThemeBackgroundFromForeground(
|
|
300
306
|
"accent",
|
|
301
|
-
formatFakeThemeForeground("warning", "CLEAR"),
|
|
307
|
+
formatFakeThemeForeground("warning", "◀ CLEAR ▶ "),
|
|
302
308
|
);
|
|
303
309
|
}
|
|
304
310
|
if (options.percent > 90) {
|
|
305
311
|
return formatFakeThemeBackgroundFromForeground(
|
|
306
312
|
"warning",
|
|
307
|
-
formatFakeThemeForeground("error", "FULL
|
|
313
|
+
formatFakeThemeForeground("error", " ◀ FULL ▶ "),
|
|
308
314
|
);
|
|
309
315
|
}
|
|
310
|
-
return Array.from({ length:
|
|
316
|
+
return Array.from({ length: 10 }, (_value, index) =>
|
|
311
317
|
formatFakeThemeBackgroundFromForeground(
|
|
312
318
|
"accent",
|
|
313
319
|
formatFakeThemeForeground(index < options.filledCells ? "warning" : "dim", "▓"),
|
|
@@ -317,23 +323,21 @@ function buildExpectedFakeContextBar(options: {
|
|
|
317
323
|
|
|
318
324
|
/**
|
|
319
325
|
* @brief Builds the expected fake pi-usereq status-bar string for assertions.
|
|
320
|
-
* @details Reconstructs the field order, separators, context bar, and
|
|
321
|
-
* @param[in] options {{
|
|
326
|
+
* @details Reconstructs the field order, separators, context bar, consolidated elapsed field, and Pushover enable field emitted by the extension using deterministic fake theme markers. Runtime is O(1). No external state is mutated.
|
|
327
|
+
* @param[in] options {{ basePath: string; contextFilledCells: number; contextPercent?: number | null; et: string; beep?: string; sound?: string; pushover?: string }} Expected status facts.
|
|
322
328
|
* @return {string} Encoded status-bar string.
|
|
323
329
|
*/
|
|
324
330
|
function buildExpectedFakeStatusText(options: {
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
srcDir: string[];
|
|
330
|
-
toolCount: number;
|
|
331
|
+
basePath: string;
|
|
332
|
+
docsDir?: string;
|
|
333
|
+
testsDir?: string;
|
|
334
|
+
srcDir?: string[];
|
|
331
335
|
contextFilledCells: number;
|
|
332
336
|
contextPercent?: number | null;
|
|
333
|
-
|
|
334
|
-
last: string;
|
|
337
|
+
et: string;
|
|
335
338
|
beep?: string;
|
|
336
339
|
sound?: string;
|
|
340
|
+
pushover?: string;
|
|
337
341
|
}): string {
|
|
338
342
|
const buildField = (fieldName: string, value: string): string =>
|
|
339
343
|
`${formatFakeThemeForeground("accent", `${fieldName}:`)}${formatFakeThemeForeground("warning", value)}`;
|
|
@@ -342,20 +346,27 @@ function buildExpectedFakeStatusText(options: {
|
|
|
342
346
|
percent: options.contextPercent,
|
|
343
347
|
});
|
|
344
348
|
return [
|
|
345
|
-
buildField("
|
|
346
|
-
buildField("base", options.basePath ?? "."),
|
|
347
|
-
buildField("docs", options.docsDir),
|
|
348
|
-
buildField("tests", options.testsDir),
|
|
349
|
-
buildField("src", options.srcDir.join(",")),
|
|
350
|
-
buildField("tools", String(options.toolCount)),
|
|
349
|
+
buildField("base", options.basePath),
|
|
351
350
|
`${formatFakeThemeForeground("accent", "context:")}${contextBar}`,
|
|
352
|
-
buildField("elapsed", options.
|
|
353
|
-
buildField("
|
|
354
|
-
buildField("beep", options.beep ?? "none"),
|
|
351
|
+
buildField("elapsed", options.et),
|
|
352
|
+
buildField("beep", options.beep ?? "end,esc,err"),
|
|
355
353
|
buildField("sound", options.sound ?? "none"),
|
|
354
|
+
buildField("pushover", options.pushover ?? "off"),
|
|
356
355
|
].join(formatFakeThemeForeground("dim", " • "));
|
|
357
356
|
}
|
|
358
357
|
|
|
358
|
+
/**
|
|
359
|
+
* @brief Formats one expected absolute base path for fake status assertions.
|
|
360
|
+
* @details Resolves the supplied cwd and normalizes path separators to `/` so
|
|
361
|
+
* fake status comparisons stay stable across operating systems. Runtime is O(p)
|
|
362
|
+
* in path length. No external state is mutated.
|
|
363
|
+
* @param[in] cwd {string} Runtime working directory.
|
|
364
|
+
* @return {string} Slash-normalized absolute base path.
|
|
365
|
+
*/
|
|
366
|
+
function buildExpectedFakeBasePath(cwd: string): string {
|
|
367
|
+
return path.resolve(cwd).split(path.sep).join("/");
|
|
368
|
+
}
|
|
369
|
+
|
|
359
370
|
/**
|
|
360
371
|
* @brief Creates a fake extension command context with scripted UI interactions.
|
|
361
372
|
* @details Materializes deterministic `select`, shared settings-menu `custom`, `input`, `notify`, `setStatus`, `setEditorText`, `waitForIdle`, `newSession`, `sessionManager`, `getContextUsage`, theme-color behaviors, and captured custom-menu render output backed by in-memory state so menu-oriented and prompt-command tests can assert side effects without a real UI. Optional `onNewSession` and theme overrides let tests emulate runtime replacement and strict token validation. Runtime is O(1) plus queued interaction count and delegated new-session setup cost. Side effects are limited to in-memory state mutation.
|
|
@@ -595,13 +606,13 @@ test("token tools register agent-oriented descriptions and schema details", () =
|
|
|
595
606
|
assert.ok(filesTokens, "missing files-tokens tool");
|
|
596
607
|
assert.ok(tokens, "missing tokens tool");
|
|
597
608
|
|
|
598
|
-
assert.match(filesTokens.description ?? "", /
|
|
609
|
+
assert.match(filesTokens.description ?? "", /token-optimized JSON payload/);
|
|
599
610
|
assert.ok(filesTokens.promptGuidelines?.some((line) => line.includes("Output contract:")));
|
|
600
|
-
assert.match(String((filesTokens.parameters as { description?: string } | undefined)?.description ?? ""), /
|
|
611
|
+
assert.match(String((filesTokens.parameters as { description?: string } | undefined)?.description ?? ""), /token metrics/);
|
|
601
612
|
|
|
602
613
|
assert.match(tokens.description ?? "", /canonical docs/);
|
|
603
|
-
assert.ok(tokens.promptGuidelines?.some((line) => line.includes("
|
|
604
|
-
assert.match(String((tokens.parameters as { description?: string } | undefined)?.description ?? ""), /
|
|
614
|
+
assert.ok(tokens.promptGuidelines?.some((line) => line.includes("registration metadata")));
|
|
615
|
+
assert.match(String((tokens.parameters as { description?: string } | undefined)?.description ?? ""), /token-optimized JSON shape/);
|
|
605
616
|
});
|
|
606
617
|
|
|
607
618
|
test("reference tools register agent-oriented descriptions and schema details", () => {
|
|
@@ -613,7 +624,7 @@ test("reference tools register agent-oriented descriptions and schema details",
|
|
|
613
624
|
assert.ok(filesReferences, "missing files-references tool");
|
|
614
625
|
assert.ok(references, "missing references tool");
|
|
615
626
|
|
|
616
|
-
assert.match(filesReferences.description ?? "", /
|
|
627
|
+
assert.match(filesReferences.description ?? "", /summary, repository, files, and execution sections/);
|
|
617
628
|
assert.ok(filesReferences.promptGuidelines?.some((line) => line.includes("Numeric contract:")));
|
|
618
629
|
assert.ok(filesReferences.promptGuidelines?.some((line) => line.includes("Behavior contract:")));
|
|
619
630
|
assert.match(String((filesReferences.parameters as { description?: string } | undefined)?.description ?? ""), /structured Doxygen fields/);
|
|
@@ -633,11 +644,11 @@ test("find tools register agent-oriented descriptions and schema details", () =>
|
|
|
633
644
|
assert.ok(filesFind, "missing files-find tool");
|
|
634
645
|
assert.ok(find, "missing find tool");
|
|
635
646
|
|
|
636
|
-
assert.match(filesFind.description ?? "", /
|
|
647
|
+
assert.match(filesFind.description ?? "", /token-optimized JSON payload/);
|
|
637
648
|
assert.ok(filesFind.promptGuidelines?.some((line) => line.includes("Regex rule:")));
|
|
638
649
|
assert.ok(filesFind.promptGuidelines?.some((line) => line.includes("Supported tags [Typescript]:")));
|
|
639
650
|
assert.ok(filesFind.promptGuidelines?.some((line) => line.includes("Supported tags [Python]:")));
|
|
640
|
-
assert.match(String((filesFind.parameters as { description?: string } | undefined)?.description ?? ""), /
|
|
651
|
+
assert.match(String((filesFind.parameters as { description?: string } | undefined)?.description ?? ""), /Static supported-tag matrices are documented in tool registration metadata/);
|
|
641
652
|
|
|
642
653
|
assert.match(find.description ?? "", /configured project source directories/);
|
|
643
654
|
assert.ok(find.promptGuidelines?.some((line) => line.includes("source_directory_paths")));
|
|
@@ -654,7 +665,7 @@ test("compression tools register agent-oriented descriptions and schema details"
|
|
|
654
665
|
assert.ok(filesCompress, "missing files-compress tool");
|
|
655
666
|
assert.ok(compress, "missing compress tool");
|
|
656
667
|
|
|
657
|
-
assert.match(filesCompress.description ?? "", /
|
|
668
|
+
assert.match(filesCompress.description ?? "", /summary, repository, files, and execution sections/);
|
|
658
669
|
assert.ok(filesCompress.promptGuidelines?.some((line) => line.includes("Line-number behavior:")));
|
|
659
670
|
assert.ok(filesCompress.promptGuidelines?.some((line) => line.includes("Behavior contract:")));
|
|
660
671
|
assert.match(String((filesCompress.parameters as { description?: string } | undefined)?.description ?? ""), /structured compressed lines/);
|
|
@@ -665,7 +676,7 @@ test("compression tools register agent-oriented descriptions and schema details"
|
|
|
665
676
|
assert.match(String((compress.parameters as { description?: string } | undefined)?.description ?? ""), /structured compressed lines/);
|
|
666
677
|
});
|
|
667
678
|
|
|
668
|
-
test("files-tokens returns structured source facts
|
|
679
|
+
test("files-tokens returns token-optimized structured source facts", async () => {
|
|
669
680
|
const { projectBase } = initFixtureRepo({
|
|
670
681
|
fixtures: [],
|
|
671
682
|
docs: {
|
|
@@ -682,10 +693,6 @@ test("files-tokens returns structured source facts and separated guidance", asyn
|
|
|
682
693
|
}) as {
|
|
683
694
|
content?: Array<{ type: string; text?: string }>;
|
|
684
695
|
details?: {
|
|
685
|
-
request: {
|
|
686
|
-
base_dir_path: string;
|
|
687
|
-
requested_input_paths: string[];
|
|
688
|
-
};
|
|
689
696
|
summary: {
|
|
690
697
|
counted_file_count: number;
|
|
691
698
|
skipped_file_count: number;
|
|
@@ -698,14 +705,8 @@ test("files-tokens returns structured source facts and separated guidance", asyn
|
|
|
698
705
|
line_count: number;
|
|
699
706
|
byte_count: number;
|
|
700
707
|
primary_heading_text?: string;
|
|
708
|
+
error_message?: string;
|
|
701
709
|
}>;
|
|
702
|
-
guidance: {
|
|
703
|
-
source_observations: {
|
|
704
|
-
skipped_inputs: Array<{ input_path: string; canonical_path: string; reason: string }>;
|
|
705
|
-
};
|
|
706
|
-
derived_recommendations: Array<{ kind: string; ordered_paths: string[] }>;
|
|
707
|
-
actionable_next_steps: Array<{ kind: string; ordered_paths: string[] }>;
|
|
708
|
-
};
|
|
709
710
|
execution: {
|
|
710
711
|
stderr: string;
|
|
711
712
|
};
|
|
@@ -714,8 +715,6 @@ test("files-tokens returns structured source facts and separated guidance", asyn
|
|
|
714
715
|
|
|
715
716
|
const payload = result.details!;
|
|
716
717
|
assert.deepEqual(JSON.parse(result.content?.[0]?.text ?? "{}"), JSON.parse(JSON.stringify(payload)));
|
|
717
|
-
assert.equal(payload.request.base_dir_path, projectBase);
|
|
718
|
-
assert.deepEqual(payload.request.requested_input_paths, [`${DEFAULT_DOCS_DIR}/REQUIREMENTS.md`, `${DEFAULT_DOCS_DIR}/MISSING.md`]);
|
|
719
718
|
assert.equal(payload.summary.counted_file_count, 1);
|
|
720
719
|
assert.equal(payload.summary.skipped_file_count, 1);
|
|
721
720
|
assert.equal(payload.files[0]?.canonical_path, `${DEFAULT_DOCS_DIR}/REQUIREMENTS.md`);
|
|
@@ -723,11 +722,8 @@ test("files-tokens returns structured source facts and separated guidance", asyn
|
|
|
723
722
|
assert.equal(payload.files[0]?.end_line_number, payload.files[0]?.line_count);
|
|
724
723
|
assert.ok((payload.files[0]?.byte_count ?? 0) > 0);
|
|
725
724
|
assert.equal(payload.files[0]?.primary_heading_text, "Requirements");
|
|
726
|
-
assert.
|
|
727
|
-
|
|
728
|
-
]);
|
|
729
|
-
assert.equal(payload.guidance.derived_recommendations[0]?.kind, "prioritize_high_token_paths");
|
|
730
|
-
assert.equal(payload.guidance.actionable_next_steps[0]?.kind, "read_top_token_paths_first");
|
|
725
|
+
assert.equal(payload.files[1]?.canonical_path, `${DEFAULT_DOCS_DIR}/MISSING.md`);
|
|
726
|
+
assert.equal(payload.files[1]?.error_message, "not found");
|
|
731
727
|
assert.match(payload.execution.stderr, new RegExp(`skipped: ${DEFAULT_DOCS_DIR.replace("/", "\\/")}/MISSING\\.md: not found`));
|
|
732
728
|
} finally {
|
|
733
729
|
fs.rmSync(projectBase, { recursive: true, force: true });
|
|
@@ -760,9 +756,6 @@ export function buildSample(input: string): string {
|
|
|
760
756
|
}) as {
|
|
761
757
|
content?: Array<{ type: string; text?: string }>;
|
|
762
758
|
details?: {
|
|
763
|
-
request: {
|
|
764
|
-
requested_input_paths: string[];
|
|
765
|
-
};
|
|
766
759
|
summary: {
|
|
767
760
|
analyzed_file_count: number;
|
|
768
761
|
skipped_file_count: number;
|
|
@@ -797,7 +790,6 @@ export function buildSample(input: string): string {
|
|
|
797
790
|
|
|
798
791
|
const payload = result.details!;
|
|
799
792
|
assert.deepEqual(JSON.parse(result.content?.[0]?.text ?? "{}"), JSON.parse(JSON.stringify(payload)));
|
|
800
|
-
assert.deepEqual(payload.request.requested_input_paths, ["src/sample.ts", "src/missing.ts"]);
|
|
801
793
|
assert.equal(payload.summary.analyzed_file_count, 1);
|
|
802
794
|
assert.equal(payload.summary.skipped_file_count, 1);
|
|
803
795
|
assert.deepEqual(payload.repository.file_canonical_paths, ["src/sample.ts"]);
|
|
@@ -901,10 +893,6 @@ export function buildSample(input: string): string {
|
|
|
901
893
|
}) as {
|
|
902
894
|
content?: Array<{ type: string; text?: string }>;
|
|
903
895
|
details?: {
|
|
904
|
-
request: {
|
|
905
|
-
line_number_mode: string;
|
|
906
|
-
requested_input_paths: string[];
|
|
907
|
-
};
|
|
908
896
|
summary: {
|
|
909
897
|
compressed_file_count: number;
|
|
910
898
|
skipped_file_count: number;
|
|
@@ -939,8 +927,6 @@ export function buildSample(input: string): string {
|
|
|
939
927
|
|
|
940
928
|
const payload = result.details!;
|
|
941
929
|
assert.deepEqual(JSON.parse(result.content?.[0]?.text ?? "{}"), JSON.parse(JSON.stringify(payload)));
|
|
942
|
-
assert.equal(payload.request.line_number_mode, "enabled");
|
|
943
|
-
assert.deepEqual(payload.request.requested_input_paths, ["src/sample.ts", "src/missing.ts"]);
|
|
944
930
|
assert.equal(payload.summary.compressed_file_count, 1);
|
|
945
931
|
assert.equal(payload.summary.skipped_file_count, 1);
|
|
946
932
|
assert.equal(payload.summary.total_symbol_count, 1);
|
|
@@ -988,10 +974,6 @@ test("compress returns a structured repository-scoped compression payload", asyn
|
|
|
988
974
|
}) as {
|
|
989
975
|
content?: Array<{ type: string; text?: string }>;
|
|
990
976
|
details?: {
|
|
991
|
-
request: {
|
|
992
|
-
scope: string;
|
|
993
|
-
line_number_mode: string;
|
|
994
|
-
};
|
|
995
977
|
summary: {
|
|
996
978
|
compressed_file_count: number;
|
|
997
979
|
total_compressed_line_count: number;
|
|
@@ -1015,8 +997,6 @@ test("compress returns a structured repository-scoped compression payload", asyn
|
|
|
1015
997
|
|
|
1016
998
|
const payload = result.details!;
|
|
1017
999
|
assert.deepEqual(JSON.parse(result.content?.[0]?.text ?? "{}"), JSON.parse(JSON.stringify(payload)));
|
|
1018
|
-
assert.equal(payload.request.scope, "configured-source-directories");
|
|
1019
|
-
assert.equal(payload.request.line_number_mode, "disabled");
|
|
1020
1000
|
assert.equal(payload.summary.compressed_file_count, 2);
|
|
1021
1001
|
assert.ok((payload.summary.total_compressed_line_count ?? 0) >= 2);
|
|
1022
1002
|
assert.deepEqual(payload.repository.source_directory_paths, ["src"]);
|
|
@@ -1060,12 +1040,6 @@ export function buildSample(input: string): string {
|
|
|
1060
1040
|
}) as {
|
|
1061
1041
|
content?: Array<{ type: string; text?: string }>;
|
|
1062
1042
|
details?: {
|
|
1063
|
-
request: {
|
|
1064
|
-
tag_filter_values: string[];
|
|
1065
|
-
tag_filter_status: string;
|
|
1066
|
-
line_number_mode: string;
|
|
1067
|
-
requested_input_paths: string[];
|
|
1068
|
-
};
|
|
1069
1043
|
summary: {
|
|
1070
1044
|
search_status: string;
|
|
1071
1045
|
matched_file_count: number;
|
|
@@ -1073,7 +1047,7 @@ export function buildSample(input: string): string {
|
|
|
1073
1047
|
total_match_count: number;
|
|
1074
1048
|
};
|
|
1075
1049
|
repository: {
|
|
1076
|
-
|
|
1050
|
+
file_canonical_paths: string[];
|
|
1077
1051
|
};
|
|
1078
1052
|
files: Array<{
|
|
1079
1053
|
canonical_path: string;
|
|
@@ -1106,15 +1080,11 @@ export function buildSample(input: string): string {
|
|
|
1106
1080
|
|
|
1107
1081
|
const payload = result.details!;
|
|
1108
1082
|
assert.deepEqual(JSON.parse(result.content?.[0]?.text ?? "{}"), JSON.parse(JSON.stringify(payload)));
|
|
1109
|
-
assert.deepEqual(payload.request.tag_filter_values, ["FUNCTION"]);
|
|
1110
|
-
assert.equal(payload.request.tag_filter_status, "valid");
|
|
1111
|
-
assert.equal(payload.request.line_number_mode, "enabled");
|
|
1112
|
-
assert.deepEqual(payload.request.requested_input_paths, ["src/sample.ts", "src/missing.ts"]);
|
|
1113
1083
|
assert.equal(payload.summary.search_status, "matched");
|
|
1114
1084
|
assert.equal(payload.summary.matched_file_count, 1);
|
|
1115
1085
|
assert.equal(payload.summary.skipped_file_count, 1);
|
|
1116
1086
|
assert.equal(payload.summary.total_match_count, 1);
|
|
1117
|
-
assert.
|
|
1087
|
+
assert.deepEqual(payload.repository.file_canonical_paths, ["src/sample.ts", "src/missing.ts"]);
|
|
1118
1088
|
assert.deepEqual(payload.files.map((file) => file.status), ["matched", "skipped"]);
|
|
1119
1089
|
assert.equal(payload.files[0]?.canonical_path, "src/sample.ts");
|
|
1120
1090
|
assert.equal(payload.files[0]?.match_count, 1);
|
|
@@ -1171,11 +1141,6 @@ export function buildAlpha(): number {
|
|
|
1171
1141
|
}) as {
|
|
1172
1142
|
content?: Array<{ type: string; text?: string }>;
|
|
1173
1143
|
details?: {
|
|
1174
|
-
request: {
|
|
1175
|
-
scope: string;
|
|
1176
|
-
regex_status: string;
|
|
1177
|
-
requested_file_count: number;
|
|
1178
|
-
};
|
|
1179
1144
|
summary: {
|
|
1180
1145
|
search_status: string;
|
|
1181
1146
|
matched_file_count: number;
|
|
@@ -1185,7 +1150,6 @@ export function buildAlpha(): number {
|
|
|
1185
1150
|
repository: {
|
|
1186
1151
|
source_directory_paths: string[];
|
|
1187
1152
|
file_canonical_paths: string[];
|
|
1188
|
-
supported_tags_by_language: Record<string, string[]>;
|
|
1189
1153
|
};
|
|
1190
1154
|
files: Array<{
|
|
1191
1155
|
canonical_path: string;
|
|
@@ -1205,16 +1169,12 @@ export function buildAlpha(): number {
|
|
|
1205
1169
|
|
|
1206
1170
|
const payload = result.details!;
|
|
1207
1171
|
assert.deepEqual(JSON.parse(result.content?.[0]?.text ?? "{}"), JSON.parse(JSON.stringify(payload)));
|
|
1208
|
-
assert.equal(payload.request.scope, "configured-source-directories");
|
|
1209
|
-
assert.equal(payload.request.regex_status, "valid");
|
|
1210
|
-
assert.equal(payload.request.requested_file_count, 2);
|
|
1211
1172
|
assert.equal(payload.summary.search_status, "matched");
|
|
1212
1173
|
assert.equal(payload.summary.matched_file_count, 1);
|
|
1213
1174
|
assert.equal(payload.summary.no_match_file_count, 1);
|
|
1214
1175
|
assert.equal(payload.summary.total_match_count, 1);
|
|
1215
1176
|
assert.deepEqual(payload.repository.source_directory_paths, ["src"]);
|
|
1216
1177
|
assert.deepEqual(payload.repository.file_canonical_paths, ["src/alpha.ts", "src/nested/beta.ts"]);
|
|
1217
|
-
assert.ok(payload.repository.supported_tags_by_language.typescript?.includes("FUNCTION"));
|
|
1218
1178
|
assert.deepEqual(payload.files.map((file) => file.status), ["matched", "no_match"]);
|
|
1219
1179
|
assert.equal(payload.files[0]?.matches[0]?.symbol_name, "buildAlpha");
|
|
1220
1180
|
assert.match(payload.files[0]?.matches[0]?.code_lines[0]?.display_text ?? "", /^export function buildAlpha/);
|
|
@@ -1246,31 +1206,28 @@ test("path, static-check, git, docs, and worktree tools return structured JSON p
|
|
|
1246
1206
|
content?: Array<{ text?: string }>;
|
|
1247
1207
|
details?: {
|
|
1248
1208
|
result: { path_value: string; path_present: boolean };
|
|
1249
|
-
runtime_paths: { git_path: string };
|
|
1250
1209
|
execution: { code: number };
|
|
1251
1210
|
};
|
|
1252
1211
|
};
|
|
1253
1212
|
assert.deepEqual(JSON.parse(gitPathResult.content?.[0]?.text ?? "{}"), JSON.parse(JSON.stringify(gitPathResult.details)));
|
|
1254
|
-
assert.equal(gitPathResult.details?.result.path_value,
|
|
1213
|
+
assert.equal(gitPathResult.details?.result.path_value, projectBase);
|
|
1255
1214
|
assert.equal(gitPathResult.details?.result.path_present, true);
|
|
1256
1215
|
assert.equal(gitPathResult.details?.execution.code, 0);
|
|
1257
1216
|
|
|
1258
1217
|
const basePathResult = await executeRegisteredTool(pi, "get-base-path", projectBase) as {
|
|
1259
1218
|
details?: {
|
|
1260
1219
|
result: { path_value: string };
|
|
1261
|
-
runtime_paths: { base_path: string };
|
|
1262
1220
|
execution: { code: number };
|
|
1263
1221
|
};
|
|
1264
1222
|
};
|
|
1265
|
-
assert.equal(basePathResult.details?.result.path_value,
|
|
1223
|
+
assert.equal(basePathResult.details?.result.path_value, projectBase);
|
|
1266
1224
|
assert.equal(basePathResult.details?.execution.code, 0);
|
|
1267
1225
|
|
|
1268
1226
|
const gitCheckResult = await executeRegisteredTool(pi, "git-check", projectBase) as {
|
|
1269
|
-
details?: { result: {
|
|
1227
|
+
details?: { result: { git_path_present: boolean; status: string }; execution: { code: number } };
|
|
1270
1228
|
};
|
|
1229
|
+
assert.equal(gitCheckResult.details?.result.git_path_present, true);
|
|
1271
1230
|
assert.equal(gitCheckResult.details?.result.status, "clean");
|
|
1272
|
-
assert.equal(gitCheckResult.details?.result.worktree_status, "clean");
|
|
1273
|
-
assert.equal(gitCheckResult.details?.result.head_status, "valid");
|
|
1274
1231
|
assert.equal(gitCheckResult.details?.execution.code, 0);
|
|
1275
1232
|
|
|
1276
1233
|
const docsCheckResult = await executeRegisteredTool(pi, "docs-check", projectBase) as {
|
|
@@ -1306,43 +1263,34 @@ test("path, static-check, git, docs, and worktree tools return structured JSON p
|
|
|
1306
1263
|
|
|
1307
1264
|
const staticCheckResult = await executeRegisteredTool(pi, "static-check", projectBase) as {
|
|
1308
1265
|
details?: {
|
|
1309
|
-
request: { selection_directory_paths: string[]; excluded_directory_paths: string[] };
|
|
1310
1266
|
summary: { selected_file_count: number };
|
|
1311
1267
|
files: Array<{ canonical_path: string; status: string }>;
|
|
1312
1268
|
execution: { code: number };
|
|
1313
1269
|
};
|
|
1314
1270
|
};
|
|
1315
|
-
assert.deepEqual(staticCheckResult.details?.request.selection_directory_paths, ["src", "tests"]);
|
|
1316
|
-
assert.ok(staticCheckResult.details?.request.excluded_directory_paths.includes("tests/fixtures"));
|
|
1317
1271
|
assert.equal(staticCheckResult.details?.summary.selected_file_count, 1);
|
|
1318
1272
|
assert.equal(staticCheckResult.details?.files[0]?.canonical_path, "src/check.ts");
|
|
1319
1273
|
assert.equal(staticCheckResult.details?.files[0]?.status, "selected");
|
|
1320
1274
|
assert.equal(staticCheckResult.details?.execution.code, 0);
|
|
1321
1275
|
|
|
1322
1276
|
const gitWtNameResult = await executeRegisteredTool(pi, "git-wt-name", projectBase) as {
|
|
1323
|
-
details?: { result: { worktree_name?: string
|
|
1277
|
+
details?: { result: { worktree_name?: string }; execution: { code: number } };
|
|
1324
1278
|
};
|
|
1325
1279
|
const wtName = gitWtNameResult.details?.result.worktree_name ?? "";
|
|
1326
|
-
assert.equal(gitWtNameResult.details?.result.status, "generated");
|
|
1327
|
-
assert.equal(gitWtNameResult.details?.result.format_text, "useReq-<project>-<sanitized-branch>-<YYYYMMDDHHMMSS>");
|
|
1328
1280
|
assert.match(wtName, /^useReq-/);
|
|
1329
1281
|
assert.equal(gitWtNameResult.details?.execution.code, 0);
|
|
1330
1282
|
|
|
1331
1283
|
const gitWtCreateResult = await executeRegisteredTool(pi, "git-wt-create", projectBase, { wtName }) as {
|
|
1332
|
-
details?: { result: {
|
|
1284
|
+
details?: { result: { worktree_name: string; worktree_path: string }; execution: { code: number } };
|
|
1333
1285
|
};
|
|
1334
|
-
assert.equal(gitWtCreateResult.details?.result.status, "created");
|
|
1335
1286
|
assert.equal(gitWtCreateResult.details?.result.worktree_name, wtName);
|
|
1336
|
-
assert.equal(gitWtCreateResult.details?.result.branch_name, wtName);
|
|
1337
1287
|
assert.match(gitWtCreateResult.details?.result.worktree_path ?? "", new RegExp(`${wtName}$`));
|
|
1338
1288
|
assert.equal(gitWtCreateResult.details?.execution.code, 0);
|
|
1339
1289
|
|
|
1340
1290
|
const gitWtDeleteResult = await executeRegisteredTool(pi, "git-wt-delete", projectBase, { wtName }) as {
|
|
1341
|
-
details?: { result: {
|
|
1291
|
+
details?: { result: { worktree_name: string; worktree_path: string }; execution: { code: number } };
|
|
1342
1292
|
};
|
|
1343
|
-
assert.equal(gitWtDeleteResult.details?.result.status, "deleted");
|
|
1344
1293
|
assert.equal(gitWtDeleteResult.details?.result.worktree_name, wtName);
|
|
1345
|
-
assert.equal(gitWtDeleteResult.details?.result.branch_name, wtName);
|
|
1346
1294
|
assert.match(gitWtDeleteResult.details?.result.worktree_path ?? "", new RegExp(`${wtName}$`));
|
|
1347
1295
|
assert.equal(gitWtDeleteResult.details?.execution.code, 0);
|
|
1348
1296
|
} finally {
|
|
@@ -1383,7 +1331,9 @@ test("configuration menu omits prompt-delivery controls and persisted config omi
|
|
|
1383
1331
|
});
|
|
1384
1332
|
|
|
1385
1333
|
test("configuration menus expose show-config ordering and omit overview or notification-reference rows", async () => {
|
|
1386
|
-
const cwd =
|
|
1334
|
+
const cwd = fs.mkdtempSync(
|
|
1335
|
+
path.join(process.env.HOME ?? process.cwd(), "pi-usereq-menu-structure-"),
|
|
1336
|
+
);
|
|
1387
1337
|
fs.mkdirSync(path.dirname(getProjectConfigPath(cwd)), { recursive: true });
|
|
1388
1338
|
const pi = createFakePi();
|
|
1389
1339
|
piUsereqExtension(pi);
|
|
@@ -1393,6 +1343,7 @@ test("configuration menus expose show-config ordering and omit overview or notif
|
|
|
1393
1343
|
|
|
1394
1344
|
await command!.handler("", ctx);
|
|
1395
1345
|
|
|
1346
|
+
const renderedMenu = (ctx.__state.customRenderLines[0] ?? []).join("\n");
|
|
1396
1347
|
assert.deepEqual(ctx.__state.selectCalls[0]?.items ?? [], [
|
|
1397
1348
|
"docs-dir",
|
|
1398
1349
|
"tests-dir",
|
|
@@ -1400,10 +1351,11 @@ test("configuration menus expose show-config ordering and omit overview or notif
|
|
|
1400
1351
|
"static-check",
|
|
1401
1352
|
"startup tools",
|
|
1402
1353
|
"notifications",
|
|
1403
|
-
"Reset defaults",
|
|
1404
1354
|
"show-config",
|
|
1355
|
+
"Reset defaults",
|
|
1405
1356
|
"Save and close",
|
|
1406
1357
|
]);
|
|
1358
|
+
assert.ok(renderedMenu.includes(formatRuntimePathForDisplay(getProjectConfigPath(cwd))));
|
|
1407
1359
|
assert.deepEqual(ctx.__state.selectCalls[2]?.items ?? [], [
|
|
1408
1360
|
"Toggle beep on success",
|
|
1409
1361
|
"Toggle beep on escape",
|
|
@@ -1413,6 +1365,7 @@ test("configuration menus expose show-config ordering and omit overview or notif
|
|
|
1413
1365
|
"Notify command (low vol.)",
|
|
1414
1366
|
"Notify command (mid vol.)",
|
|
1415
1367
|
"Notify command (high vol.)",
|
|
1368
|
+
"Pushover notifications",
|
|
1416
1369
|
"Back",
|
|
1417
1370
|
]);
|
|
1418
1371
|
});
|
|
@@ -1493,13 +1446,12 @@ test("session_start applies configured pi-usereq startup tools", async () => {
|
|
|
1493
1446
|
assert.equal(
|
|
1494
1447
|
ctx.__state.statuses.get("pi-usereq"),
|
|
1495
1448
|
buildExpectedFakeStatusText({
|
|
1449
|
+
basePath: buildExpectedFakeBasePath(cwd),
|
|
1496
1450
|
docsDir: DEFAULT_DOCS_DIR,
|
|
1497
1451
|
testsDir: "tests",
|
|
1498
1452
|
srcDir: ["src", "foobar"],
|
|
1499
|
-
toolCount: 2,
|
|
1500
1453
|
contextFilledCells: 0,
|
|
1501
|
-
|
|
1502
|
-
last: "N/A",
|
|
1454
|
+
et: " ⏱︎ --:-- ⚑ --:-- ⌛︎ --:--",
|
|
1503
1455
|
}),
|
|
1504
1456
|
);
|
|
1505
1457
|
});
|
|
@@ -1518,14 +1470,13 @@ test("session_start renders the CLEAR overlay when context usage is empty", asyn
|
|
|
1518
1470
|
assert.equal(
|
|
1519
1471
|
ctx.__state.statuses.get("pi-usereq"),
|
|
1520
1472
|
buildExpectedFakeStatusText({
|
|
1473
|
+
basePath: buildExpectedFakeBasePath(cwd),
|
|
1521
1474
|
docsDir: DEFAULT_DOCS_DIR,
|
|
1522
1475
|
testsDir: "tests",
|
|
1523
1476
|
srcDir: ["src"],
|
|
1524
|
-
toolCount: PI_USEREQ_DEFAULT_ENABLED_TOOL_NAMES.length,
|
|
1525
1477
|
contextFilledCells: 0,
|
|
1526
1478
|
contextPercent: 0,
|
|
1527
|
-
|
|
1528
|
-
last: "N/A",
|
|
1479
|
+
et: " ⏱︎ --:-- ⚑ --:-- ⌛︎ --:--",
|
|
1529
1480
|
}),
|
|
1530
1481
|
);
|
|
1531
1482
|
});
|
|
@@ -1586,14 +1537,13 @@ test("context hook refreshes context usage and rounds progress cells upward", as
|
|
|
1586
1537
|
assert.equal(
|
|
1587
1538
|
ctx.__state.statuses.get("pi-usereq"),
|
|
1588
1539
|
buildExpectedFakeStatusText({
|
|
1540
|
+
basePath: buildExpectedFakeBasePath(cwd),
|
|
1589
1541
|
docsDir: DEFAULT_DOCS_DIR,
|
|
1590
1542
|
testsDir: "tests",
|
|
1591
1543
|
srcDir: ["src"],
|
|
1592
|
-
|
|
1593
|
-
contextFilledCells: 1,
|
|
1544
|
+
contextFilledCells: 2,
|
|
1594
1545
|
contextPercent: 19.1,
|
|
1595
|
-
|
|
1596
|
-
last: "N/A",
|
|
1546
|
+
et: " ⏱︎ --:-- ⚑ --:-- ⌛︎ --:--",
|
|
1597
1547
|
}),
|
|
1598
1548
|
);
|
|
1599
1549
|
});
|
|
@@ -1614,14 +1564,13 @@ test("context hook renders the FULL! overlay when usage exceeds ninety percent",
|
|
|
1614
1564
|
assert.equal(
|
|
1615
1565
|
ctx.__state.statuses.get("pi-usereq"),
|
|
1616
1566
|
buildExpectedFakeStatusText({
|
|
1567
|
+
basePath: buildExpectedFakeBasePath(cwd),
|
|
1617
1568
|
docsDir: DEFAULT_DOCS_DIR,
|
|
1618
1569
|
testsDir: "tests",
|
|
1619
1570
|
srcDir: ["src"],
|
|
1620
|
-
|
|
1621
|
-
contextFilledCells: 5,
|
|
1571
|
+
contextFilledCells: 10,
|
|
1622
1572
|
contextPercent: 91,
|
|
1623
|
-
|
|
1624
|
-
last: "N/A",
|
|
1573
|
+
et: " ⏱︎ --:-- ⚑ --:-- ⌛︎ --:--",
|
|
1625
1574
|
}),
|
|
1626
1575
|
);
|
|
1627
1576
|
});
|
|
@@ -1657,19 +1606,18 @@ test("status FULL overlay uses only CLI-supported theme tokens", async () => {
|
|
|
1657
1606
|
assert.equal(
|
|
1658
1607
|
ctx.__state.statuses.get("pi-usereq"),
|
|
1659
1608
|
buildExpectedFakeStatusText({
|
|
1609
|
+
basePath: buildExpectedFakeBasePath(cwd),
|
|
1660
1610
|
docsDir: DEFAULT_DOCS_DIR,
|
|
1661
1611
|
testsDir: "tests",
|
|
1662
1612
|
srcDir: ["src"],
|
|
1663
|
-
|
|
1664
|
-
contextFilledCells: 5,
|
|
1613
|
+
contextFilledCells: 10,
|
|
1665
1614
|
contextPercent: 91,
|
|
1666
|
-
|
|
1667
|
-
last: "N/A",
|
|
1615
|
+
et: " ⏱︎ --:-- ⚑ --:-- ⌛︎ --:--",
|
|
1668
1616
|
}),
|
|
1669
1617
|
);
|
|
1670
1618
|
});
|
|
1671
1619
|
|
|
1672
|
-
test("agent timing status updates
|
|
1620
|
+
test("agent timing status updates et while preserving accumulated completed runtime", async () => {
|
|
1673
1621
|
const cwd = createTempDir("pi-usereq-timing-status-");
|
|
1674
1622
|
fs.mkdirSync(path.dirname(getProjectConfigPath(cwd)), { recursive: true });
|
|
1675
1623
|
|
|
@@ -1698,13 +1646,12 @@ test("agent timing status updates elapsed and last while preserving aborted last
|
|
|
1698
1646
|
assert.equal(
|
|
1699
1647
|
ctx.__state.statuses.get("pi-usereq"),
|
|
1700
1648
|
buildExpectedFakeStatusText({
|
|
1649
|
+
basePath: buildExpectedFakeBasePath(cwd),
|
|
1701
1650
|
docsDir: DEFAULT_DOCS_DIR,
|
|
1702
1651
|
testsDir: "tests",
|
|
1703
1652
|
srcDir: ["src"],
|
|
1704
|
-
toolCount: PI_USEREQ_DEFAULT_ENABLED_TOOL_NAMES.length,
|
|
1705
1653
|
contextFilledCells: 0,
|
|
1706
|
-
|
|
1707
|
-
last: "N/A",
|
|
1654
|
+
et: " ⏱︎ 0:00 ⚑ --:-- ⌛︎ --:--",
|
|
1708
1655
|
}),
|
|
1709
1656
|
);
|
|
1710
1657
|
|
|
@@ -1713,13 +1660,12 @@ test("agent timing status updates elapsed and last while preserving aborted last
|
|
|
1713
1660
|
assert.equal(
|
|
1714
1661
|
ctx.__state.statuses.get("pi-usereq"),
|
|
1715
1662
|
buildExpectedFakeStatusText({
|
|
1663
|
+
basePath: buildExpectedFakeBasePath(cwd),
|
|
1716
1664
|
docsDir: DEFAULT_DOCS_DIR,
|
|
1717
1665
|
testsDir: "tests",
|
|
1718
1666
|
srcDir: ["src"],
|
|
1719
|
-
toolCount: PI_USEREQ_DEFAULT_ENABLED_TOOL_NAMES.length,
|
|
1720
1667
|
contextFilledCells: 0,
|
|
1721
|
-
|
|
1722
|
-
last: "N/A",
|
|
1668
|
+
et: " ⏱︎ 1:01 ⚑ --:-- ⌛︎ --:--",
|
|
1723
1669
|
}),
|
|
1724
1670
|
);
|
|
1725
1671
|
|
|
@@ -1735,13 +1681,12 @@ test("agent timing status updates elapsed and last while preserving aborted last
|
|
|
1735
1681
|
assert.equal(
|
|
1736
1682
|
ctx.__state.statuses.get("pi-usereq"),
|
|
1737
1683
|
buildExpectedFakeStatusText({
|
|
1684
|
+
basePath: buildExpectedFakeBasePath(cwd),
|
|
1738
1685
|
docsDir: DEFAULT_DOCS_DIR,
|
|
1739
1686
|
testsDir: "tests",
|
|
1740
1687
|
srcDir: ["src"],
|
|
1741
|
-
toolCount: PI_USEREQ_DEFAULT_ENABLED_TOOL_NAMES.length,
|
|
1742
1688
|
contextFilledCells: 0,
|
|
1743
|
-
|
|
1744
|
-
last: "1:01",
|
|
1689
|
+
et: " ⏱︎ --:-- ⚑ 1:01 ⌛︎ 1:01",
|
|
1745
1690
|
}),
|
|
1746
1691
|
);
|
|
1747
1692
|
|
|
@@ -1760,13 +1705,12 @@ test("agent timing status updates elapsed and last while preserving aborted last
|
|
|
1760
1705
|
assert.equal(
|
|
1761
1706
|
ctx.__state.statuses.get("pi-usereq"),
|
|
1762
1707
|
buildExpectedFakeStatusText({
|
|
1708
|
+
basePath: buildExpectedFakeBasePath(cwd),
|
|
1763
1709
|
docsDir: DEFAULT_DOCS_DIR,
|
|
1764
1710
|
testsDir: "tests",
|
|
1765
1711
|
srcDir: ["src"],
|
|
1766
|
-
toolCount: PI_USEREQ_DEFAULT_ENABLED_TOOL_NAMES.length,
|
|
1767
1712
|
contextFilledCells: 0,
|
|
1768
|
-
|
|
1769
|
-
last: "1:01",
|
|
1713
|
+
et: " ⏱︎ --:-- ⚑ 1:01 ⌛︎ 1:01",
|
|
1770
1714
|
}),
|
|
1771
1715
|
);
|
|
1772
1716
|
assert.ok(clearedHandles.length >= 2);
|
|
@@ -1794,6 +1738,14 @@ test("session_start enables default custom tools and default embedded tools only
|
|
|
1794
1738
|
assert.equal(activeTools.has("ls"), false);
|
|
1795
1739
|
});
|
|
1796
1740
|
|
|
1741
|
+
test("default configuration enables all pi-notify beep outcomes", () => {
|
|
1742
|
+
const config = getDefaultConfig(createTempDir("pi-usereq-default-notify-"));
|
|
1743
|
+
|
|
1744
|
+
assert.equal(config["notify-beep-on-end"], true);
|
|
1745
|
+
assert.equal(config["notify-beep-on-esc"], true);
|
|
1746
|
+
assert.equal(config["notify-beep-on-error"], true);
|
|
1747
|
+
});
|
|
1748
|
+
|
|
1797
1749
|
test("configuration menu can enable embedded builtin tools", async () => {
|
|
1798
1750
|
const cwd = createTempDir("pi-usereq-menu-embedded-");
|
|
1799
1751
|
fs.mkdirSync(path.dirname(getProjectConfigPath(cwd)), { recursive: true });
|
|
@@ -1866,9 +1818,9 @@ test("configuration menu can persist pi-notify settings", async () => {
|
|
|
1866
1818
|
await command!.handler("", ctx);
|
|
1867
1819
|
|
|
1868
1820
|
const config = JSON.parse(fs.readFileSync(getProjectConfigPath(cwd), "utf8"));
|
|
1869
|
-
assert.equal(config["notify-beep-on-end"],
|
|
1870
|
-
assert.equal(config["notify-beep-on-error"],
|
|
1871
|
-
assert.equal(config["notify-beep-on-esc"],
|
|
1821
|
+
assert.equal(config["notify-beep-on-end"], false);
|
|
1822
|
+
assert.equal(config["notify-beep-on-error"], false);
|
|
1823
|
+
assert.equal(config["notify-beep-on-esc"], true);
|
|
1872
1824
|
assert.equal(config["notify-sound"], "high");
|
|
1873
1825
|
assert.equal(config["notify-sound-toggle-shortcut"], "alt+shift+s");
|
|
1874
1826
|
assert.equal(config.PI_NOTIFY_SOUND_LOW_CMD, "echo low %%INSTALLATION_PATH%%");
|
|
@@ -1877,19 +1829,285 @@ test("configuration menu can persist pi-notify settings", async () => {
|
|
|
1877
1829
|
assert.equal(
|
|
1878
1830
|
ctx.__state.statuses.get("pi-usereq"),
|
|
1879
1831
|
buildExpectedFakeStatusText({
|
|
1832
|
+
basePath: buildExpectedFakeBasePath(cwd),
|
|
1880
1833
|
docsDir: DEFAULT_DOCS_DIR,
|
|
1881
1834
|
testsDir: "tests",
|
|
1882
1835
|
srcDir: ["src"],
|
|
1883
|
-
toolCount: pi.getActiveTools().length,
|
|
1884
1836
|
contextFilledCells: 0,
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
beep: "end,err",
|
|
1837
|
+
et: " ⏱︎ --:-- ⚑ --:-- ⌛︎ --:--",
|
|
1838
|
+
beep: "esc",
|
|
1888
1839
|
sound: "high",
|
|
1889
1840
|
}),
|
|
1890
1841
|
);
|
|
1891
1842
|
});
|
|
1892
1843
|
|
|
1844
|
+
test("configuration menu can persist pushover settings", async () => {
|
|
1845
|
+
const cwd = createTempDir("pi-usereq-menu-pushover-");
|
|
1846
|
+
fs.mkdirSync(path.dirname(getProjectConfigPath(cwd)), { recursive: true });
|
|
1847
|
+
const pi = createFakePi();
|
|
1848
|
+
piUsereqExtension(pi);
|
|
1849
|
+
const command = pi.commands.get("pi-usereq");
|
|
1850
|
+
assert.ok(command);
|
|
1851
|
+
const ctx = createFakeCtx(cwd, {
|
|
1852
|
+
selects: [
|
|
1853
|
+
"notifications",
|
|
1854
|
+
"Pushover notifications",
|
|
1855
|
+
"Notify on success",
|
|
1856
|
+
"User Key/Delivery Group Key",
|
|
1857
|
+
"Token/API Token Key",
|
|
1858
|
+
"Priority",
|
|
1859
|
+
"1=High Priority",
|
|
1860
|
+
"Back",
|
|
1861
|
+
"Back",
|
|
1862
|
+
"Save and close",
|
|
1863
|
+
],
|
|
1864
|
+
inputs: [
|
|
1865
|
+
"gzfjjvp1xxmhibqwzh9m7i1zwvf83j",
|
|
1866
|
+
"ah6bf5u2sj63mcvou6qamiabeoubbe",
|
|
1867
|
+
],
|
|
1868
|
+
});
|
|
1869
|
+
|
|
1870
|
+
await command!.handler("", ctx);
|
|
1871
|
+
|
|
1872
|
+
const config = JSON.parse(fs.readFileSync(getProjectConfigPath(cwd), "utf8"));
|
|
1873
|
+
assert.equal(config["notify-pushover-global-disable"], false);
|
|
1874
|
+
assert.equal(config["notify-pushover-on-success"], true);
|
|
1875
|
+
assert.equal(config["notify-pushover-user-key"], "gzfjjvp1xxmhibqwzh9m7i1zwvf83j");
|
|
1876
|
+
assert.equal(config["notify-pushover-api-token"], "ah6bf5u2sj63mcvou6qamiabeoubbe");
|
|
1877
|
+
assert.equal(config["notify-pushover-priority"], 1);
|
|
1878
|
+
assert.equal(
|
|
1879
|
+
ctx.__state.statuses.get("pi-usereq"),
|
|
1880
|
+
buildExpectedFakeStatusText({
|
|
1881
|
+
basePath: buildExpectedFakeBasePath(cwd),
|
|
1882
|
+
docsDir: DEFAULT_DOCS_DIR,
|
|
1883
|
+
testsDir: "tests",
|
|
1884
|
+
srcDir: ["src"],
|
|
1885
|
+
contextFilledCells: 0,
|
|
1886
|
+
et: " ⏱︎ --:-- ⚑ --:-- ⌛︎ --:--",
|
|
1887
|
+
pushover: "on",
|
|
1888
|
+
}),
|
|
1889
|
+
);
|
|
1890
|
+
});
|
|
1891
|
+
|
|
1892
|
+
test("successful req prompt completion sends pushover requests and suppresses invalid scenarios", async () => {
|
|
1893
|
+
const cwd = createTempDir("pi-usereq-pushover-run-");
|
|
1894
|
+
fs.mkdirSync(path.dirname(getProjectConfigPath(cwd)), { recursive: true });
|
|
1895
|
+
const configPath = getProjectConfigPath(cwd);
|
|
1896
|
+
const writeConfig = (overrides: Record<string, unknown>) => {
|
|
1897
|
+
const config = {
|
|
1898
|
+
...getDefaultConfig(cwd),
|
|
1899
|
+
...overrides,
|
|
1900
|
+
};
|
|
1901
|
+
fs.writeFileSync(configPath, `${JSON.stringify(config, null, 2)}\n`, "utf8");
|
|
1902
|
+
};
|
|
1903
|
+
const pi = createFakePi();
|
|
1904
|
+
piUsereqExtension(pi);
|
|
1905
|
+
const ctx = createFakeCtx(cwd);
|
|
1906
|
+
const originalDateNow = Date.now;
|
|
1907
|
+
const recordedRequests: Array<{ url: string; options: Record<string, unknown>; body: string }> = [];
|
|
1908
|
+
let nowMs = 0;
|
|
1909
|
+
setPiNotifyHttpsRequestForTests(((url: URL, options: Record<string, unknown>, callback?: (response: any) => void) => {
|
|
1910
|
+
const record = { url: url.toString(), options, body: "" };
|
|
1911
|
+
recordedRequests.push(record);
|
|
1912
|
+
const fakeResponse = {
|
|
1913
|
+
on(_eventName: string, _handler: (...args: any[]) => void) {
|
|
1914
|
+
return fakeResponse;
|
|
1915
|
+
},
|
|
1916
|
+
resume() {
|
|
1917
|
+
return undefined;
|
|
1918
|
+
},
|
|
1919
|
+
};
|
|
1920
|
+
callback?.(fakeResponse);
|
|
1921
|
+
const fakeRequest = {
|
|
1922
|
+
on(_eventName: string, _handler: (...args: any[]) => void) {
|
|
1923
|
+
return fakeRequest;
|
|
1924
|
+
},
|
|
1925
|
+
end(body?: string) {
|
|
1926
|
+
record.body = body ?? "";
|
|
1927
|
+
},
|
|
1928
|
+
};
|
|
1929
|
+
return fakeRequest as unknown as ReturnType<typeof import("node:https").request>;
|
|
1930
|
+
}) as typeof import("node:https").request);
|
|
1931
|
+
Date.now = () => nowMs;
|
|
1932
|
+
|
|
1933
|
+
try {
|
|
1934
|
+
writeConfig({
|
|
1935
|
+
"notify-pushover-on-success": true,
|
|
1936
|
+
"notify-pushover-user-key": "gzfjjvp1xxmhibqwzh9m7i1zwvf83j",
|
|
1937
|
+
"notify-pushover-api-token": "ah6bf5u2sj63mcvou6qamiabeoubbe",
|
|
1938
|
+
"notify-pushover-priority": 1,
|
|
1939
|
+
});
|
|
1940
|
+
await pi.emit("session_start", { reason: "startup" }, ctx);
|
|
1941
|
+
recordedRequests.length = 0;
|
|
1942
|
+
nowMs = 0;
|
|
1943
|
+
await pi.commands.get("req-analyze")!.handler("Inspect src/index.ts", ctx);
|
|
1944
|
+
await pi.emit("agent_start", {}, ctx);
|
|
1945
|
+
nowMs = 5_000;
|
|
1946
|
+
await pi.emit("agent_end", {
|
|
1947
|
+
messages: [
|
|
1948
|
+
{
|
|
1949
|
+
role: "assistant",
|
|
1950
|
+
stopReason: "stop",
|
|
1951
|
+
content: [],
|
|
1952
|
+
},
|
|
1953
|
+
],
|
|
1954
|
+
}, ctx);
|
|
1955
|
+
|
|
1956
|
+
assert.equal(recordedRequests.length, 1);
|
|
1957
|
+
assert.equal(recordedRequests[0]?.url, "https://api.pushover.net/1/messages.json");
|
|
1958
|
+
const successParams = new URLSearchParams(recordedRequests[0]?.body ?? "");
|
|
1959
|
+
assert.equal(successParams.get("token"), "ah6bf5u2sj63mcvou6qamiabeoubbe");
|
|
1960
|
+
assert.equal(successParams.get("user"), "gzfjjvp1xxmhibqwzh9m7i1zwvf83j");
|
|
1961
|
+
assert.equal(successParams.get("priority"), "1");
|
|
1962
|
+
assert.equal(successParams.get("message"), "Inspect src/index.ts");
|
|
1963
|
+
assert.equal(successParams.get("title"), `analyze @ ${buildExpectedFakeBasePath(cwd)} [0:05]`);
|
|
1964
|
+
|
|
1965
|
+
recordedRequests.length = 0;
|
|
1966
|
+
nowMs = 10_000;
|
|
1967
|
+
await pi.commands.get("req-change")!.handler("Adjust docs", ctx);
|
|
1968
|
+
await pi.emit("agent_start", {}, ctx);
|
|
1969
|
+
nowMs = 14_000;
|
|
1970
|
+
await pi.emit("agent_end", {
|
|
1971
|
+
messages: [
|
|
1972
|
+
{
|
|
1973
|
+
role: "assistant",
|
|
1974
|
+
stopReason: "aborted",
|
|
1975
|
+
content: [],
|
|
1976
|
+
},
|
|
1977
|
+
],
|
|
1978
|
+
}, ctx);
|
|
1979
|
+
assert.equal(recordedRequests.length, 0);
|
|
1980
|
+
|
|
1981
|
+
recordedRequests.length = 0;
|
|
1982
|
+
nowMs = 20_000;
|
|
1983
|
+
await pi.commands.get("req-create")!.handler("Create docs", ctx);
|
|
1984
|
+
await pi.emit("agent_start", {}, ctx);
|
|
1985
|
+
nowMs = 23_000;
|
|
1986
|
+
await pi.emit("agent_end", {
|
|
1987
|
+
messages: [
|
|
1988
|
+
{
|
|
1989
|
+
role: "assistant",
|
|
1990
|
+
stopReason: "error",
|
|
1991
|
+
content: [],
|
|
1992
|
+
},
|
|
1993
|
+
],
|
|
1994
|
+
}, ctx);
|
|
1995
|
+
assert.equal(recordedRequests.length, 0);
|
|
1996
|
+
|
|
1997
|
+
writeConfig({
|
|
1998
|
+
"notify-pushover-on-success": true,
|
|
1999
|
+
"notify-pushover-user-key": "gzfjjvp1xxmhibqwzh9m7i1zwvf83j",
|
|
2000
|
+
"notify-pushover-api-token": "",
|
|
2001
|
+
"notify-pushover-priority": 1,
|
|
2002
|
+
});
|
|
2003
|
+
await pi.emit("session_start", { reason: "startup" }, ctx);
|
|
2004
|
+
recordedRequests.length = 0;
|
|
2005
|
+
nowMs = 30_000;
|
|
2006
|
+
await pi.commands.get("req-write")!.handler("Write docs", ctx);
|
|
2007
|
+
await pi.emit("agent_start", {}, ctx);
|
|
2008
|
+
nowMs = 32_000;
|
|
2009
|
+
await pi.emit("agent_end", {
|
|
2010
|
+
messages: [
|
|
2011
|
+
{
|
|
2012
|
+
role: "assistant",
|
|
2013
|
+
stopReason: "stop",
|
|
2014
|
+
content: [],
|
|
2015
|
+
},
|
|
2016
|
+
],
|
|
2017
|
+
}, ctx);
|
|
2018
|
+
assert.equal(recordedRequests.length, 0);
|
|
2019
|
+
} finally {
|
|
2020
|
+
setPiNotifyHttpsRequestForTests(undefined);
|
|
2021
|
+
Date.now = originalDateNow;
|
|
2022
|
+
}
|
|
2023
|
+
});
|
|
2024
|
+
|
|
2025
|
+
test("pushover global disable suppresses delivery without clearing enabled state", async () => {
|
|
2026
|
+
const cwd = createTempDir("pi-usereq-pushover-disabled-");
|
|
2027
|
+
fs.mkdirSync(path.dirname(getProjectConfigPath(cwd)), { recursive: true });
|
|
2028
|
+
const pi = createFakePi();
|
|
2029
|
+
piUsereqExtension(pi);
|
|
2030
|
+
const command = pi.commands.get("pi-usereq");
|
|
2031
|
+
assert.ok(command);
|
|
2032
|
+
const ctx = createFakeCtx(cwd, {
|
|
2033
|
+
selects: [
|
|
2034
|
+
"notifications",
|
|
2035
|
+
"Pushover notifications",
|
|
2036
|
+
"Notify on success",
|
|
2037
|
+
"Global disable",
|
|
2038
|
+
"User Key/Delivery Group Key",
|
|
2039
|
+
"Token/API Token Key",
|
|
2040
|
+
"Back",
|
|
2041
|
+
"Back",
|
|
2042
|
+
"Save and close",
|
|
2043
|
+
],
|
|
2044
|
+
inputs: [
|
|
2045
|
+
"gzfjjvp1xxmhibqwzh9m7i1zwvf83j",
|
|
2046
|
+
"ah6bf5u2sj63mcvou6qamiabeoubbe",
|
|
2047
|
+
],
|
|
2048
|
+
});
|
|
2049
|
+
|
|
2050
|
+
await command!.handler("", ctx);
|
|
2051
|
+
|
|
2052
|
+
const config = JSON.parse(fs.readFileSync(getProjectConfigPath(cwd), "utf8"));
|
|
2053
|
+
assert.equal(config["notify-pushover-on-success"], true);
|
|
2054
|
+
assert.equal(config["notify-pushover-global-disable"], true);
|
|
2055
|
+
assert.equal(
|
|
2056
|
+
ctx.__state.statuses.get("pi-usereq"),
|
|
2057
|
+
buildExpectedFakeStatusText({
|
|
2058
|
+
basePath: buildExpectedFakeBasePath(cwd),
|
|
2059
|
+
docsDir: DEFAULT_DOCS_DIR,
|
|
2060
|
+
testsDir: "tests",
|
|
2061
|
+
srcDir: ["src"],
|
|
2062
|
+
contextFilledCells: 0,
|
|
2063
|
+
et: " ⏱︎ --:-- ⚑ --:-- ⌛︎ --:--",
|
|
2064
|
+
pushover: "on",
|
|
2065
|
+
}),
|
|
2066
|
+
);
|
|
2067
|
+
|
|
2068
|
+
const recordedRequests: string[] = [];
|
|
2069
|
+
setPiNotifyHttpsRequestForTests(((url: URL, options: Record<string, unknown>, callback?: (response: any) => void) => {
|
|
2070
|
+
void options;
|
|
2071
|
+
recordedRequests.push(url.toString());
|
|
2072
|
+
const fakeResponse = {
|
|
2073
|
+
on(_eventName: string, _handler: (...args: any[]) => void) {
|
|
2074
|
+
return fakeResponse;
|
|
2075
|
+
},
|
|
2076
|
+
resume() {
|
|
2077
|
+
return undefined;
|
|
2078
|
+
},
|
|
2079
|
+
};
|
|
2080
|
+
callback?.(fakeResponse);
|
|
2081
|
+
const fakeRequest = {
|
|
2082
|
+
on(_eventName: string, _handler: (...args: any[]) => void) {
|
|
2083
|
+
return fakeRequest;
|
|
2084
|
+
},
|
|
2085
|
+
end(_body?: string) {
|
|
2086
|
+
return undefined;
|
|
2087
|
+
},
|
|
2088
|
+
};
|
|
2089
|
+
return fakeRequest as unknown as ReturnType<typeof import("node:https").request>;
|
|
2090
|
+
}) as typeof import("node:https").request);
|
|
2091
|
+
|
|
2092
|
+
try {
|
|
2093
|
+
await pi.emit("session_start", { reason: "startup" }, ctx);
|
|
2094
|
+
await pi.commands.get("req-analyze")!.handler("Inspect src/index.ts", ctx);
|
|
2095
|
+
await pi.emit("agent_start", {}, ctx);
|
|
2096
|
+
await pi.emit("agent_end", {
|
|
2097
|
+
messages: [
|
|
2098
|
+
{
|
|
2099
|
+
role: "assistant",
|
|
2100
|
+
stopReason: "stop",
|
|
2101
|
+
content: [],
|
|
2102
|
+
},
|
|
2103
|
+
],
|
|
2104
|
+
}, ctx);
|
|
2105
|
+
assert.deepEqual(recordedRequests, []);
|
|
2106
|
+
} finally {
|
|
2107
|
+
setPiNotifyHttpsRequestForTests(undefined);
|
|
2108
|
+
}
|
|
2109
|
+
});
|
|
2110
|
+
|
|
1893
2111
|
test("sound toggle shortcut cycles persisted pi-notify sound levels", async () => {
|
|
1894
2112
|
const cwd = createTempDir("pi-usereq-shortcut-notify-");
|
|
1895
2113
|
fs.mkdirSync(path.dirname(getProjectConfigPath(cwd)), { recursive: true });
|
|
@@ -1914,13 +2132,12 @@ test("sound toggle shortcut cycles persisted pi-notify sound levels", async () =
|
|
|
1914
2132
|
assert.equal(
|
|
1915
2133
|
ctx.__state.statuses.get("pi-usereq"),
|
|
1916
2134
|
buildExpectedFakeStatusText({
|
|
2135
|
+
basePath: buildExpectedFakeBasePath(cwd),
|
|
1917
2136
|
docsDir: DEFAULT_DOCS_DIR,
|
|
1918
2137
|
testsDir: "tests",
|
|
1919
2138
|
srcDir: ["src"],
|
|
1920
|
-
toolCount: PI_USEREQ_DEFAULT_ENABLED_TOOL_NAMES.length,
|
|
1921
2139
|
contextFilledCells: 0,
|
|
1922
|
-
|
|
1923
|
-
last: "N/A",
|
|
2140
|
+
et: " ⏱︎ --:-- ⚑ --:-- ⌛︎ --:--",
|
|
1924
2141
|
sound: expectedSound,
|
|
1925
2142
|
}),
|
|
1926
2143
|
);
|
|
@@ -1939,11 +2156,11 @@ test("git-path tool derives the repository root at runtime", async () => {
|
|
|
1939
2156
|
piUsereqExtension(pi);
|
|
1940
2157
|
const result = await executeRegisteredTool(pi, "git-path", projectBase) as {
|
|
1941
2158
|
content?: Array<{ text?: string }>;
|
|
1942
|
-
details?: { result: { path_value: string }
|
|
2159
|
+
details?: { result: { path_value: string } };
|
|
1943
2160
|
};
|
|
1944
2161
|
|
|
1945
2162
|
assert.deepEqual(JSON.parse(result.content?.[0]?.text ?? "{}"), JSON.parse(JSON.stringify(result.details)));
|
|
1946
|
-
assert.equal(result.details?.result.path_value,
|
|
2163
|
+
assert.equal(result.details?.result.path_value, projectBase);
|
|
1947
2164
|
} finally {
|
|
1948
2165
|
fs.rmSync(projectBase, { recursive: true, force: true });
|
|
1949
2166
|
}
|
|
@@ -1979,6 +2196,26 @@ test("configuration menu can add static-check entries from raw specs", async ()
|
|
|
1979
2196
|
assert.deepEqual(config["static-check"].Python, [{ module: "Command", cmd: "true" }]);
|
|
1980
2197
|
});
|
|
1981
2198
|
|
|
2199
|
+
test("configuration menu hides removed static-check modules from user-facing actions", async () => {
|
|
2200
|
+
const cwd = createTempDir("pi-usereq-menu-sc-hidden-");
|
|
2201
|
+
fs.mkdirSync(path.dirname(getProjectConfigPath(cwd)), { recursive: true });
|
|
2202
|
+
const pi = createFakePi();
|
|
2203
|
+
piUsereqExtension(pi);
|
|
2204
|
+
const command = pi.commands.get("pi-usereq");
|
|
2205
|
+
assert.ok(command);
|
|
2206
|
+
const ctx = createFakeCtx(cwd, {
|
|
2207
|
+
selects: ["static-check"],
|
|
2208
|
+
});
|
|
2209
|
+
|
|
2210
|
+
await command!.handler("", ctx);
|
|
2211
|
+
|
|
2212
|
+
const renderedStaticCheckMenu = (ctx.__state.customRenderLines[1] ?? []).join("\n");
|
|
2213
|
+
assert.match(renderedStaticCheckMenu, /Add entry for supported language/);
|
|
2214
|
+
assert.doesNotMatch(renderedStaticCheckMenu, /Pylance/);
|
|
2215
|
+
assert.doesNotMatch(renderedStaticCheckMenu, /Ruff/);
|
|
2216
|
+
assert.doesNotMatch(renderedStaticCheckMenu, /Dummy/);
|
|
2217
|
+
});
|
|
2218
|
+
|
|
1982
2219
|
test("configuration menu can add guided static-check entries for explicit supported languages", async () => {
|
|
1983
2220
|
const cwd = createTempDir("pi-usereq-menu-sc-guided-");
|
|
1984
2221
|
fs.mkdirSync(path.dirname(getProjectConfigPath(cwd)), { recursive: true });
|
|
@@ -1994,13 +2231,13 @@ test("configuration menu can add guided static-check entries for explicit suppor
|
|
|
1994
2231
|
"static-check",
|
|
1995
2232
|
"Add entry for supported language",
|
|
1996
2233
|
"Python",
|
|
1997
|
-
"Ruff",
|
|
1998
2234
|
"Back",
|
|
1999
2235
|
"Save and close",
|
|
2000
2236
|
],
|
|
2237
|
+
inputs: ["true", ""],
|
|
2001
2238
|
}),
|
|
2002
2239
|
);
|
|
2003
2240
|
|
|
2004
2241
|
const config = JSON.parse(fs.readFileSync(getProjectConfigPath(cwd), "utf8"));
|
|
2005
|
-
assert.deepEqual(config["static-check"].Python, [{ module: "
|
|
2242
|
+
assert.deepEqual(config["static-check"].Python, [{ module: "Command", cmd: "true" }]);
|
|
2006
2243
|
});
|