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
package/src/core/static-check.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @file
|
|
3
3
|
* @brief Defines static-check language mappings and checker dispatch implementations.
|
|
4
|
-
* @details Parses static-check
|
|
4
|
+
* @details Parses Command-only user static-check specifications, preserves debug `Dummy` config handling, resolves file targets, and runs modular dummy or command-based analyzers. Runtime is linear in file count plus external tool cost. Side effects include filesystem reads, PATH probing, process spawning, and console output.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import fs from "node:fs";
|
|
@@ -73,10 +73,16 @@ export const STATIC_CHECK_EXT_TO_LANG: Record<string, string> = {
|
|
|
73
73
|
};
|
|
74
74
|
|
|
75
75
|
/**
|
|
76
|
-
* @brief Lists the
|
|
77
|
-
* @details The tuple constrains configuration
|
|
76
|
+
* @brief Lists the user-configurable static-check module identifiers.
|
|
77
|
+
* @details The tuple constrains guided configuration and `--enable-static-check` parsing to the supported user-facing checker implementations. Access complexity is O(1).
|
|
78
78
|
*/
|
|
79
|
-
export const STATIC_CHECK_MODULES = ["
|
|
79
|
+
export const STATIC_CHECK_MODULES = ["Command"] as const;
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* @brief Lists the persisted or debug-capable static-check module identifiers.
|
|
83
|
+
* @details The tuple augments user-configurable modules with debug-only `Dummy` support used by existing config payloads and the standalone test driver. Access complexity is O(1).
|
|
84
|
+
*/
|
|
85
|
+
const STATIC_CHECK_PERSISTED_MODULES = ["Dummy", "Command"] as const;
|
|
80
86
|
|
|
81
87
|
/**
|
|
82
88
|
* @brief Describes supported extensions for one canonical static-check language.
|
|
@@ -88,13 +94,10 @@ export interface StaticCheckLanguageSupport {
|
|
|
88
94
|
}
|
|
89
95
|
|
|
90
96
|
/**
|
|
91
|
-
* @brief Maps case-insensitive module names to canonical module identifiers.
|
|
92
|
-
* @details Enables permissive user input while keeping downstream dispatch logic deterministic. Lookup complexity is O(1).
|
|
97
|
+
* @brief Maps case-insensitive user module names to canonical static-check module identifiers.
|
|
98
|
+
* @details Enables permissive Command-only user input while keeping downstream dispatch logic deterministic. Lookup complexity is O(1).
|
|
93
99
|
*/
|
|
94
100
|
const CANONICAL_MODULES: Record<string, (typeof STATIC_CHECK_MODULES)[number]> = {
|
|
95
|
-
dummy: "Dummy",
|
|
96
|
-
pylance: "Pylance",
|
|
97
|
-
ruff: "Ruff",
|
|
98
101
|
command: "Command",
|
|
99
102
|
};
|
|
100
103
|
|
|
@@ -126,14 +129,23 @@ export function getSupportedStaticCheckLanguageSupport(): StaticCheckLanguageSup
|
|
|
126
129
|
}
|
|
127
130
|
|
|
128
131
|
/**
|
|
129
|
-
* @brief Formats the
|
|
130
|
-
* @details Joins `STATIC_CHECK_MODULES` with commas for direct insertion into error strings. Time complexity is O(n). No side effects occur.
|
|
131
|
-
* @return {string} Comma-delimited module names.
|
|
132
|
+
* @brief Formats the user-configurable module list for diagnostics.
|
|
133
|
+
* @details Joins `STATIC_CHECK_MODULES` with commas for direct insertion into user-facing error strings. Time complexity is O(n). No side effects occur.
|
|
134
|
+
* @return {string} Comma-delimited user-configurable module names.
|
|
132
135
|
*/
|
|
133
136
|
function formatStaticCheckModules(): string {
|
|
134
137
|
return STATIC_CHECK_MODULES.join(", ");
|
|
135
138
|
}
|
|
136
139
|
|
|
140
|
+
/**
|
|
141
|
+
* @brief Formats the persisted or debug-capable module list for dispatch diagnostics.
|
|
142
|
+
* @details Joins `STATIC_CHECK_PERSISTED_MODULES` with commas for error strings emitted while executing existing config entries or debug-driver requests. Time complexity is O(n). No side effects occur.
|
|
143
|
+
* @return {string} Comma-delimited persisted module names.
|
|
144
|
+
*/
|
|
145
|
+
function formatDispatchStaticCheckModules(): string {
|
|
146
|
+
return STATIC_CHECK_PERSISTED_MODULES.join(", ");
|
|
147
|
+
}
|
|
148
|
+
|
|
137
149
|
/**
|
|
138
150
|
* @brief Splits a comma-delimited static-check specification while honoring quotes.
|
|
139
151
|
* @details Performs a single pass over the right-hand side of `LANG=...`, preserving commas inside quoted segments. Runtime is O(n). No side effects occur.
|
|
@@ -165,11 +177,11 @@ function splitCsvLikeTokens(specRhs: string): string[] {
|
|
|
165
177
|
}
|
|
166
178
|
|
|
167
179
|
/**
|
|
168
|
-
* @brief Parses one `LANG=
|
|
169
|
-
* @details Validates the language alias, canonicalizes the module name, enforces
|
|
180
|
+
* @brief Parses one `LANG=Command,CMD[,PARAM...]` static-check specification.
|
|
181
|
+
* @details Validates the language alias, canonicalizes the Command module name, enforces the required executable argument, and returns a config entry ready for persistence. Runtime is O(n) in specification length. No external state is mutated.
|
|
170
182
|
* @param[in] spec {string} Raw static-check specification string.
|
|
171
183
|
* @return {[string, StaticCheckEntry]} Tuple of canonical language name and normalized checker configuration.
|
|
172
|
-
* @throws {ReqError} Throws for missing separators, unknown languages,
|
|
184
|
+
* @throws {ReqError} Throws for missing separators, unknown languages, non-Command modules, or missing required command arguments.
|
|
173
185
|
*/
|
|
174
186
|
export function parseEnableStaticCheck(spec: string): [string, StaticCheckEntry] {
|
|
175
187
|
if (!spec.includes("=")) {
|
|
@@ -290,8 +302,8 @@ function resolveFiles(inputs: string[]): string[] {
|
|
|
290
302
|
}
|
|
291
303
|
|
|
292
304
|
/**
|
|
293
|
-
* @brief Provides the base implementation for file-oriented static checks.
|
|
294
|
-
* @details Resolves input files once, emits standardized headers, and defines overridable `checkFile`
|
|
305
|
+
* @brief Provides the shared and debug-capable base implementation for file-oriented static checks.
|
|
306
|
+
* @details Resolves input files once, emits standardized headers, implements the debug `Dummy` checker behavior, and defines overridable `checkFile` plus `emitLine` hooks used by concrete analyzers. Runtime is O(f) plus subclass checker cost. Side effects include console output.
|
|
295
307
|
*/
|
|
296
308
|
export class StaticCheckBase {
|
|
297
309
|
static LABEL = "Dummy";
|
|
@@ -370,146 +382,7 @@ export class StaticCheckBase {
|
|
|
370
382
|
}
|
|
371
383
|
|
|
372
384
|
/**
|
|
373
|
-
* @brief
|
|
374
|
-
* @details Checks the project virtual environment first, then `PI_USEREQ_PYTHON`, then `python3`, then `python`, and finally falls back to the literal `python3` string. Runtime is O(c) in candidate count. Side effects are filesystem reads and PATH probing.
|
|
375
|
-
* @param[in] projectBase {string | undefined} Optional project root used to probe `.venv/bin/python`.
|
|
376
|
-
* @return {string} Executable path or command name.
|
|
377
|
-
*/
|
|
378
|
-
function detectPythonExecutable(projectBase?: string): string {
|
|
379
|
-
const candidates = [
|
|
380
|
-
projectBase ? path.join(projectBase, ".venv", "bin", "python") : undefined,
|
|
381
|
-
process.env.PI_USEREQ_PYTHON,
|
|
382
|
-
"python3",
|
|
383
|
-
"python",
|
|
384
|
-
].filter((value): value is string => !!value);
|
|
385
|
-
|
|
386
|
-
for (const candidate of candidates) {
|
|
387
|
-
if (candidate.includes(path.sep)) {
|
|
388
|
-
if (fs.existsSync(candidate) && fs.statSync(candidate).isFile()) {
|
|
389
|
-
return candidate;
|
|
390
|
-
}
|
|
391
|
-
continue;
|
|
392
|
-
}
|
|
393
|
-
if (findExecutable(candidate)) {
|
|
394
|
-
return candidate;
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
return "python3";
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
/**
|
|
401
|
-
* @brief Runs Pyright/Pylance checks through the selected Python interpreter.
|
|
402
|
-
* @details Invokes `python -m pyright` for each resolved file and emits standardized OK/FAIL records. Runtime is dominated by external checker execution. Side effects include process spawning and console output.
|
|
403
|
-
*/
|
|
404
|
-
export class StaticCheckPylance extends StaticCheckBase {
|
|
405
|
-
static override LABEL = "Pylance";
|
|
406
|
-
private projectBase?: string;
|
|
407
|
-
|
|
408
|
-
/**
|
|
409
|
-
* @brief Initializes a Pylance checker instance.
|
|
410
|
-
* @details Delegates file resolution to the base class, stores the optional project base, and overrides the human-readable label. Runtime is O(f) in resolved input count. Mutates instance fields only.
|
|
411
|
-
* @param[in] inputs {string[]} Raw file inputs.
|
|
412
|
-
* @param[in] extraArgs {string[] | undefined} Extra Pyright arguments.
|
|
413
|
-
* @param[in] failOnly {boolean} When `true`, suppress successful-file output.
|
|
414
|
-
* @param[in] projectBase {string | undefined} Optional project root used for interpreter resolution and process cwd.
|
|
415
|
-
*/
|
|
416
|
-
constructor(inputs: string[], extraArgs?: string[], failOnly = false, projectBase?: string) {
|
|
417
|
-
super(inputs, extraArgs, failOnly);
|
|
418
|
-
this.projectBase = projectBase;
|
|
419
|
-
this.label = "Pylance";
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
/**
|
|
423
|
-
* @brief Runs Pyright for one file.
|
|
424
|
-
* @details Resolves the Python interpreter, executes `python -m pyright`, and emits standardized OK/FAIL output with captured evidence on failure. Runtime is dominated by external tool execution. Side effects include process spawning and console output.
|
|
425
|
-
* @param[in] filePath {string} File to analyze.
|
|
426
|
-
* @return {number} `0` on success; `1` on execution or analysis failure.
|
|
427
|
-
*/
|
|
428
|
-
protected override checkFile(filePath: string): number {
|
|
429
|
-
const pythonExec = detectPythonExecutable(this.projectBase);
|
|
430
|
-
const command = [pythonExec, "-m", "pyright", "--pythonpath", pythonExec, filePath, ...this.extraArgs];
|
|
431
|
-
const result = spawnSync(command[0]!, command.slice(1), {
|
|
432
|
-
cwd: this.projectBase,
|
|
433
|
-
encoding: "utf8",
|
|
434
|
-
});
|
|
435
|
-
if (result.error) {
|
|
436
|
-
this.emitLine(this.headerLine(filePath));
|
|
437
|
-
this.emitLine("Result: FAIL");
|
|
438
|
-
this.emitLine("Evidence:");
|
|
439
|
-
this.emitLine(" pyright module not available via sys.executable");
|
|
440
|
-
return 1;
|
|
441
|
-
}
|
|
442
|
-
if (result.status === 0) {
|
|
443
|
-
if (!this.failOnly) {
|
|
444
|
-
this.emitLine(this.headerLine(filePath));
|
|
445
|
-
this.emitLine("Result: OK");
|
|
446
|
-
}
|
|
447
|
-
return 0;
|
|
448
|
-
}
|
|
449
|
-
this.emitLine(this.headerLine(filePath));
|
|
450
|
-
this.emitLine("Result: FAIL");
|
|
451
|
-
this.emitLine("Evidence:");
|
|
452
|
-
this.emitLine(`${result.stdout ?? ""}${result.stderr ?? ""}`.trimEnd());
|
|
453
|
-
return 1;
|
|
454
|
-
}
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
/**
|
|
458
|
-
* @brief Runs Ruff checks through the selected Python interpreter.
|
|
459
|
-
* @details Invokes `python -m ruff check` for each resolved file and emits standardized OK/FAIL records. Runtime is dominated by external checker execution. Side effects include process spawning and console output.
|
|
460
|
-
*/
|
|
461
|
-
export class StaticCheckRuff extends StaticCheckBase {
|
|
462
|
-
static override LABEL = "Ruff";
|
|
463
|
-
private projectBase?: string;
|
|
464
|
-
|
|
465
|
-
/**
|
|
466
|
-
* @brief Initializes a Ruff checker instance.
|
|
467
|
-
* @details Delegates file resolution to the base class, stores the optional project base, and overrides the human-readable label. Runtime is O(f) in resolved input count. Mutates instance fields only.
|
|
468
|
-
* @param[in] inputs {string[]} Raw file inputs.
|
|
469
|
-
* @param[in] extraArgs {string[] | undefined} Extra Ruff arguments.
|
|
470
|
-
* @param[in] failOnly {boolean} When `true`, suppress successful-file output.
|
|
471
|
-
* @param[in] projectBase {string | undefined} Optional project root used for interpreter resolution and process cwd.
|
|
472
|
-
*/
|
|
473
|
-
constructor(inputs: string[], extraArgs?: string[], failOnly = false, projectBase?: string) {
|
|
474
|
-
super(inputs, extraArgs, failOnly);
|
|
475
|
-
this.label = "Ruff";
|
|
476
|
-
this.projectBase = projectBase;
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
/**
|
|
480
|
-
* @brief Runs Ruff for one file.
|
|
481
|
-
* @details Resolves the Python interpreter, executes `python -m ruff check`, and emits standardized OK/FAIL output with captured evidence on failure. Runtime is dominated by external tool execution. Side effects include process spawning and console output.
|
|
482
|
-
* @param[in] filePath {string} File to analyze.
|
|
483
|
-
* @return {number} `0` on success; `1` on execution or analysis failure.
|
|
484
|
-
*/
|
|
485
|
-
protected override checkFile(filePath: string): number {
|
|
486
|
-
const pythonExec = detectPythonExecutable(this.projectBase);
|
|
487
|
-
const command = [pythonExec, "-m", "ruff", "check", filePath, ...this.extraArgs];
|
|
488
|
-
const result = spawnSync(command[0]!, command.slice(1), { encoding: "utf8", cwd: this.projectBase });
|
|
489
|
-
if (result.error) {
|
|
490
|
-
this.emitLine(this.headerLine(filePath));
|
|
491
|
-
this.emitLine("Result: FAIL");
|
|
492
|
-
this.emitLine("Evidence:");
|
|
493
|
-
this.emitLine(" ruff module not available via sys.executable");
|
|
494
|
-
return 1;
|
|
495
|
-
}
|
|
496
|
-
if (result.status === 0) {
|
|
497
|
-
if (!this.failOnly) {
|
|
498
|
-
this.emitLine(this.headerLine(filePath));
|
|
499
|
-
this.emitLine("Result: OK");
|
|
500
|
-
}
|
|
501
|
-
return 0;
|
|
502
|
-
}
|
|
503
|
-
this.emitLine(this.headerLine(filePath));
|
|
504
|
-
this.emitLine("Result: FAIL");
|
|
505
|
-
this.emitLine("Evidence:");
|
|
506
|
-
this.emitLine(`${result.stdout ?? ""}${result.stderr ?? ""}`.trimEnd());
|
|
507
|
-
return 1;
|
|
508
|
-
}
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
/**
|
|
512
|
-
* @brief Runs an arbitrary external command as a static checker.
|
|
385
|
+
* @brief Runs the user-facing external-command static checker.
|
|
513
386
|
* @details Validates command availability on PATH during construction, then invokes the command with configured extra arguments plus one target file at a time. Runtime is dominated by external command execution. Side effects include PATH probing, process spawning, and console output.
|
|
514
387
|
*/
|
|
515
388
|
export class StaticCheckCommand extends StaticCheckBase {
|
|
@@ -602,7 +475,7 @@ function findExecutable(cmd: string): string | undefined {
|
|
|
602
475
|
|
|
603
476
|
/**
|
|
604
477
|
* @brief Dispatches one configured static checker for a single file.
|
|
605
|
-
* @details Selects the
|
|
478
|
+
* @details Selects the debug `Dummy` or user-facing `Command` implementation by module name, normalizes parameter arrays, and runs exactly one checker instance against the target file. Runtime is dominated by the selected checker. Side effects include console output and possible process spawning.
|
|
606
479
|
* @param[in] filePath {string} Absolute or relative file path to check.
|
|
607
480
|
* @param[in] langConfig {StaticCheckEntry} Normalized static-check configuration entry.
|
|
608
481
|
* @param[in] options {{ failOnly?: boolean; projectBase?: string }} Optional execution controls.
|
|
@@ -618,14 +491,9 @@ export function dispatchStaticCheckForFile(
|
|
|
618
491
|
const params = Array.isArray(langConfig.params) ? langConfig.params.map(String) : [];
|
|
619
492
|
const cmd = typeof langConfig.cmd === "string" ? langConfig.cmd : undefined;
|
|
620
493
|
const failOnly = options.failOnly ?? false;
|
|
621
|
-
const projectBase = options.projectBase;
|
|
622
494
|
switch (moduleName.toLowerCase()) {
|
|
623
495
|
case "dummy":
|
|
624
496
|
return new StaticCheckBase([filePath], params, failOnly).run();
|
|
625
|
-
case "pylance":
|
|
626
|
-
return new StaticCheckPylance([filePath], params, failOnly, projectBase).run();
|
|
627
|
-
case "ruff":
|
|
628
|
-
return new StaticCheckRuff([filePath], params, failOnly, projectBase).run();
|
|
629
497
|
case "command":
|
|
630
498
|
if (!cmd) {
|
|
631
499
|
throw new ReqError(`Error: Command module requires 'cmd' in static-check config for '${filePath}'.`, 1);
|
|
@@ -633,7 +501,7 @@ export function dispatchStaticCheckForFile(
|
|
|
633
501
|
return new StaticCheckCommand(cmd, [filePath], params, failOnly).run();
|
|
634
502
|
default:
|
|
635
503
|
throw new ReqError(
|
|
636
|
-
`Error: unknown static-check module '${moduleName}'. Valid modules: ${
|
|
504
|
+
`Error: unknown static-check module '${moduleName}'. Valid modules: ${formatDispatchStaticCheckModules()}`,
|
|
637
505
|
1,
|
|
638
506
|
);
|
|
639
507
|
}
|
|
@@ -641,23 +509,19 @@ export function dispatchStaticCheckForFile(
|
|
|
641
509
|
|
|
642
510
|
/**
|
|
643
511
|
* @brief Runs the standalone static-check test driver.
|
|
644
|
-
* @details Dispatches
|
|
512
|
+
* @details Dispatches debug `dummy` or user-facing `command` subcommands without consulting project configuration. Runtime is O(n) in argument count plus checker cost. Side effects include console output and external process spawning.
|
|
645
513
|
* @param[in] argv {string[]} Raw static-check subcommand arguments.
|
|
646
514
|
* @return {number} Checker exit status where `0` means success.
|
|
647
515
|
* @throws {ReqError} Throws when no subcommand is provided, the subcommand is unknown, or required arguments are missing.
|
|
648
516
|
*/
|
|
649
517
|
export function runStaticCheck(argv: string[]): number {
|
|
650
518
|
if (argv.length === 0) {
|
|
651
|
-
throw new ReqError("Error: --test-static-check requires a subcommand: dummy,
|
|
519
|
+
throw new ReqError("Error: --test-static-check requires a subcommand: dummy, command.", 1);
|
|
652
520
|
}
|
|
653
521
|
const [subcommand, ...rest] = argv;
|
|
654
522
|
switch (subcommand) {
|
|
655
523
|
case "dummy":
|
|
656
524
|
return new StaticCheckBase(rest).run();
|
|
657
|
-
case "pylance":
|
|
658
|
-
return new StaticCheckPylance(rest).run();
|
|
659
|
-
case "ruff":
|
|
660
|
-
return new StaticCheckRuff(rest, undefined, false, process.cwd()).run();
|
|
661
525
|
case "command": {
|
|
662
526
|
if (rest.length === 0) {
|
|
663
527
|
throw new ReqError("Error: --test-static-check command requires a <cmd> argument.", 1);
|
|
@@ -667,7 +531,7 @@ export function runStaticCheck(argv: string[]): number {
|
|
|
667
531
|
}
|
|
668
532
|
default:
|
|
669
533
|
throw new ReqError(
|
|
670
|
-
`Error: unknown --test-static-check subcommand '${subcommand}'. Valid subcommands: dummy,
|
|
534
|
+
`Error: unknown --test-static-check subcommand '${subcommand}'. Valid subcommands: dummy, command.`,
|
|
671
535
|
1,
|
|
672
536
|
);
|
|
673
537
|
}
|
|
@@ -175,13 +175,11 @@ export interface TokenToolGuidanceSection {
|
|
|
175
175
|
|
|
176
176
|
/**
|
|
177
177
|
* @brief Describes the full agent-oriented token payload.
|
|
178
|
-
* @details
|
|
178
|
+
* @details Exposes only aggregate numeric totals plus per-file metrics, omitting request echoes and derived guidance that can be inferred from tool registration or recomputed by the caller. The interface is compile-time only and introduces no runtime cost.
|
|
179
179
|
*/
|
|
180
180
|
export interface TokenToolPayload {
|
|
181
|
-
request: TokenToolRequestSection;
|
|
182
181
|
summary: TokenToolSummarySection;
|
|
183
182
|
files: TokenToolFileEntry[];
|
|
184
|
-
guidance: TokenToolGuidanceSection;
|
|
185
183
|
}
|
|
186
184
|
|
|
187
185
|
/**
|
|
@@ -359,41 +357,6 @@ function roundRatio(numerator: number, denominator: number): number {
|
|
|
359
357
|
return Number((numerator / denominator).toFixed(6));
|
|
360
358
|
}
|
|
361
359
|
|
|
362
|
-
/**
|
|
363
|
-
* @brief Orders canonical file paths by one numeric metric while removing duplicates.
|
|
364
|
-
* @details Filters to counted file entries, sorts by the supplied metric direction, breaks ties by canonical path, and preserves only the first occurrence of each path. Runtime is O(n log n). No external state is mutated.
|
|
365
|
-
* @param[in] files {TokenToolFileEntry[]} Token payload file entries.
|
|
366
|
-
* @param[in] metric {(entry: TokenToolFileEntry) => number} Numeric metric selector.
|
|
367
|
-
* @param[in] direction {"asc" | "desc"} Sort direction.
|
|
368
|
-
* @return {string[]} Unique canonical paths ordered by the requested metric.
|
|
369
|
-
*/
|
|
370
|
-
function orderPathsByMetric(
|
|
371
|
-
files: TokenToolFileEntry[],
|
|
372
|
-
metric: (entry: TokenToolFileEntry) => number,
|
|
373
|
-
direction: "asc" | "desc",
|
|
374
|
-
): string[] {
|
|
375
|
-
const sorted = files
|
|
376
|
-
.filter((entry) => entry.status === "counted")
|
|
377
|
-
.sort((left, right) => {
|
|
378
|
-
const leftMetric = metric(left);
|
|
379
|
-
const rightMetric = metric(right);
|
|
380
|
-
if (leftMetric === rightMetric) {
|
|
381
|
-
return left.canonical_path.localeCompare(right.canonical_path);
|
|
382
|
-
}
|
|
383
|
-
return direction === "desc" ? rightMetric - leftMetric : leftMetric - rightMetric;
|
|
384
|
-
});
|
|
385
|
-
const seen = new Set<string>();
|
|
386
|
-
const orderedPaths: string[] = [];
|
|
387
|
-
for (const entry of sorted) {
|
|
388
|
-
if (seen.has(entry.canonical_path)) {
|
|
389
|
-
continue;
|
|
390
|
-
}
|
|
391
|
-
seen.add(entry.canonical_path);
|
|
392
|
-
orderedPaths.push(entry.canonical_path);
|
|
393
|
-
}
|
|
394
|
-
return orderedPaths;
|
|
395
|
-
}
|
|
396
|
-
|
|
397
360
|
/**
|
|
398
361
|
* @brief Probes one requested path before token counting.
|
|
399
362
|
* @details Resolves whether the target exists and is a regular file while capturing a stable skip reason for missing or non-file inputs. Runtime is dominated by one filesystem stat. Side effects are limited to filesystem reads.
|
|
@@ -496,9 +459,9 @@ export function countFilesMetrics(filePaths: string[], encodingName = TOKEN_COUN
|
|
|
496
459
|
|
|
497
460
|
/**
|
|
498
461
|
* @brief Builds the agent-oriented JSON payload for token-centric tools.
|
|
499
|
-
* @details Validates requested paths against the filesystem, counts token metrics for processable files, preserves caller order in the file table,
|
|
462
|
+
* @details Validates requested paths against the filesystem, counts token metrics for processable files, preserves caller order in the file table, and emits direct-access file facts such as sizes, headings, and optional Doxygen file fields while omitting request echoes and derived guidance. Runtime is O(F + S). Side effects are limited to filesystem reads.
|
|
500
463
|
* @param[in] options {BuildTokenToolPayloadOptions} Payload-construction options.
|
|
501
|
-
* @return {TokenToolPayload} Structured token payload ordered as
|
|
464
|
+
* @return {TokenToolPayload} Structured token payload ordered as summary then files.
|
|
502
465
|
* @satisfies REQ-010, REQ-017, REQ-069, REQ-070, REQ-071, REQ-073, REQ-074, REQ-075
|
|
503
466
|
*/
|
|
504
467
|
export function buildTokenToolPayload(options: BuildTokenToolPayloadOptions): TokenToolPayload {
|
|
@@ -596,71 +559,7 @@ export function buildTokenToolPayload(options: BuildTokenToolPayloadOptions): To
|
|
|
596
559
|
const countedFileCount = filesWithShares.filter((entry) => entry.status === "counted").length;
|
|
597
560
|
const errorFileCount = filesWithShares.filter((entry) => entry.status === "error").length;
|
|
598
561
|
const skippedFileCount = filesWithShares.filter((entry) => entry.status === "skipped").length;
|
|
599
|
-
const countedPathsByTokenCountDesc = orderPathsByMetric(filesWithShares, (entry) => entry.token_count, "desc");
|
|
600
|
-
const countedPathsByTokenCountAsc = orderPathsByMetric(filesWithShares, (entry) => entry.token_count, "asc");
|
|
601
|
-
const countedPathsByLineCountDesc = orderPathsByMetric(filesWithShares, (entry) => entry.line_count, "desc");
|
|
602
|
-
const dominantTokenFileEntry = filesWithShares
|
|
603
|
-
.filter((entry) => entry.status === "counted")
|
|
604
|
-
.sort((left, right) => {
|
|
605
|
-
if (left.token_count === right.token_count) {
|
|
606
|
-
return left.canonical_path.localeCompare(right.canonical_path);
|
|
607
|
-
}
|
|
608
|
-
return right.token_count - left.token_count;
|
|
609
|
-
})[0];
|
|
610
|
-
const skippedInputs = filesWithShares
|
|
611
|
-
.filter((entry) => entry.status === "skipped" && entry.error_message)
|
|
612
|
-
.map((entry) => ({
|
|
613
|
-
input_path: entry.input_path,
|
|
614
|
-
canonical_path: entry.canonical_path,
|
|
615
|
-
reason: entry.error_message!,
|
|
616
|
-
}));
|
|
617
|
-
const errorInputs = filesWithShares
|
|
618
|
-
.filter((entry) => entry.status === "error" && entry.error_message)
|
|
619
|
-
.map((entry) => ({
|
|
620
|
-
input_path: entry.input_path,
|
|
621
|
-
canonical_path: entry.canonical_path,
|
|
622
|
-
reason: entry.error_message!,
|
|
623
|
-
}));
|
|
624
|
-
const derivedRecommendations: TokenToolRecommendation[] = countedPathsByTokenCountDesc.length > 0
|
|
625
|
-
? [
|
|
626
|
-
{
|
|
627
|
-
kind: "prioritize_high_token_paths",
|
|
628
|
-
basis_metric_name: "token_count",
|
|
629
|
-
ordered_paths: countedPathsByTokenCountDesc,
|
|
630
|
-
},
|
|
631
|
-
{
|
|
632
|
-
kind: "defer_low_token_paths",
|
|
633
|
-
basis_metric_name: "token_count",
|
|
634
|
-
ordered_paths: countedPathsByTokenCountAsc,
|
|
635
|
-
},
|
|
636
|
-
]
|
|
637
|
-
: [];
|
|
638
|
-
const actionableNextSteps: TokenToolNextStepHint[] = countedPathsByTokenCountDesc.length > 0
|
|
639
|
-
? [
|
|
640
|
-
{
|
|
641
|
-
kind: "read_top_token_paths_first",
|
|
642
|
-
ordered_paths: countedPathsByTokenCountDesc.slice(0, 3),
|
|
643
|
-
goal: "minimize context-truncation risk during initial review",
|
|
644
|
-
},
|
|
645
|
-
{
|
|
646
|
-
kind: "reserve_low_token_paths_for_follow_up",
|
|
647
|
-
ordered_paths: countedPathsByTokenCountAsc.slice(0, 3),
|
|
648
|
-
goal: "defer lower-cost files until high-cost files have been reviewed",
|
|
649
|
-
},
|
|
650
|
-
]
|
|
651
|
-
: [];
|
|
652
562
|
return {
|
|
653
|
-
request: {
|
|
654
|
-
tool_name: options.toolName,
|
|
655
|
-
scope: options.scope,
|
|
656
|
-
encoding_name: encodingName,
|
|
657
|
-
base_dir_path: baseDir.split(path.sep).join("/"),
|
|
658
|
-
requested_file_count: options.requestedPaths.length,
|
|
659
|
-
requested_input_paths: options.requestedPaths,
|
|
660
|
-
requested_canonical_paths: requestedEntries.map((entry) => entry.canonicalPath),
|
|
661
|
-
docs_dir_path: options.docsDir,
|
|
662
|
-
canonical_doc_names: options.canonicalDocNames,
|
|
663
|
-
},
|
|
664
563
|
summary: {
|
|
665
564
|
processable_file_count: countedFileCount + errorFileCount,
|
|
666
565
|
counted_file_count: countedFileCount,
|
|
@@ -676,23 +575,6 @@ export function buildTokenToolPayload(options: BuildTokenToolPayloadOptions): To
|
|
|
676
575
|
average_line_count_per_counted_file: countedFileCount === 0 ? 0 : Number((totalLineCount / countedFileCount).toFixed(6)),
|
|
677
576
|
},
|
|
678
577
|
files: filesWithShares,
|
|
679
|
-
guidance: {
|
|
680
|
-
source_observations: {
|
|
681
|
-
counted_paths_by_token_count_desc: countedPathsByTokenCountDesc,
|
|
682
|
-
counted_paths_by_line_count_desc: countedPathsByLineCountDesc,
|
|
683
|
-
skipped_inputs: skippedInputs,
|
|
684
|
-
error_inputs: errorInputs,
|
|
685
|
-
dominant_token_file: dominantTokenFileEntry
|
|
686
|
-
? {
|
|
687
|
-
canonical_path: dominantTokenFileEntry.canonical_path,
|
|
688
|
-
token_count: dominantTokenFileEntry.token_count,
|
|
689
|
-
token_share: dominantTokenFileEntry.token_share,
|
|
690
|
-
}
|
|
691
|
-
: undefined,
|
|
692
|
-
},
|
|
693
|
-
derived_recommendations: derivedRecommendations,
|
|
694
|
-
actionable_next_steps: actionableNextSteps,
|
|
695
|
-
},
|
|
696
578
|
};
|
|
697
579
|
}
|
|
698
580
|
|