pi-usereq 0.5.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +48 -0
- package/README.md +1 -1
- package/package.json +1 -1
- package/pi-usereq/docs/REFERENCES.md +24470 -4590
- package/pi-usereq/docs/REQUIREMENTS.md +89 -68
- package/pi-usereq/docs/WORKFLOW.md +133 -106
- package/src/core/agent-tool-json.ts +42 -193
- package/src/core/compress-payload.ts +7 -15
- package/src/core/config.ts +84 -29
- package/src/core/extension-status.ts +49 -177
- package/src/core/find-payload.ts +21 -44
- package/src/core/pi-notify.ts +473 -313
- package/src/core/reference-payload.ts +6 -14
- package/src/core/settings-menu.ts +21 -1
- package/src/core/token-counter.ts +58 -124
- package/src/index.ts +798 -537
- package/src/resources/images/favicon.svg +21 -0
- package/src/resources/images/pi.dev.png +0 -0
- package/tests/debug-extension-harness.test.ts +17 -23
- package/tests/extension-registration.test.ts +632 -215
|
@@ -182,10 +182,9 @@ export interface ReferenceToolRepositorySection {
|
|
|
182
182
|
|
|
183
183
|
/**
|
|
184
184
|
* @brief Describes the full agent-oriented references payload.
|
|
185
|
-
* @details
|
|
185
|
+
* @details Exposes only aggregate analysis totals, repository structure, and per-file reference records, omitting request echoes that are already known to the caller or encoded in the tool registration. The interface is compile-time only and introduces no runtime cost.
|
|
186
186
|
*/
|
|
187
187
|
export interface ReferenceToolPayload {
|
|
188
|
-
request: ReferenceToolRequestSection;
|
|
189
188
|
summary: ReferenceToolSummarySection;
|
|
190
189
|
repository: ReferenceToolRepositorySection;
|
|
191
190
|
files: ReferenceToolFileEntry[];
|
|
@@ -663,9 +662,9 @@ function analyzeReferenceFile(
|
|
|
663
662
|
|
|
664
663
|
/**
|
|
665
664
|
* @brief Builds the full agent-oriented references payload.
|
|
666
|
-
* @details Validates requested paths against the filesystem, analyzes processable files in caller order, preserves skipped and failed inputs in structured file entries, computes aggregate numeric totals, and emits
|
|
665
|
+
* @details Validates requested paths against the filesystem, analyzes processable files in caller order, preserves skipped and failed inputs in structured file entries, computes aggregate numeric totals, and emits structured repository data without echoing request metadata already known to the caller. Runtime is O(F log F + S). Side effects are limited to filesystem reads and optional stderr logging.
|
|
667
666
|
* @param[in] options {BuildReferenceToolPayloadOptions} Payload-construction options.
|
|
668
|
-
* @return {ReferenceToolPayload} Structured references payload ordered as
|
|
667
|
+
* @return {ReferenceToolPayload} Structured references payload ordered as summary, repository, and files.
|
|
669
668
|
* @satisfies REQ-011, REQ-014, REQ-076, REQ-077, REQ-078, REQ-079
|
|
670
669
|
*/
|
|
671
670
|
export function buildReferenceToolPayload(options: BuildReferenceToolPayloadOptions): ReferenceToolPayload {
|
|
@@ -761,17 +760,10 @@ export function buildReferenceToolPayload(options: BuildReferenceToolPayloadOpti
|
|
|
761
760
|
const repositoryFileCanonicalPaths = [...new Set(analyzedFiles.map((file) => file.canonical_path))]
|
|
762
761
|
.sort((left, right) => left.localeCompare(right));
|
|
763
762
|
|
|
763
|
+
void toolName;
|
|
764
|
+
void scope;
|
|
765
|
+
void canonicalRequestedPaths;
|
|
764
766
|
return {
|
|
765
|
-
request: {
|
|
766
|
-
tool_name: toolName,
|
|
767
|
-
scope,
|
|
768
|
-
base_dir_path: absoluteBaseDir,
|
|
769
|
-
source_directory_count: sourceDirectoryPaths.length,
|
|
770
|
-
source_directory_paths: sourceDirectoryPaths.map((sourceDirectoryPath) => canonicalizeReferencePath(sourceDirectoryPath, absoluteBaseDir)),
|
|
771
|
-
requested_file_count: requestedPaths.length,
|
|
772
|
-
requested_input_paths: [...requestedPaths],
|
|
773
|
-
requested_canonical_paths: canonicalRequestedPaths,
|
|
774
|
-
},
|
|
775
767
|
summary: {
|
|
776
768
|
processable_file_count: processableFiles.length,
|
|
777
769
|
analyzed_file_count: analyzedFiles.length,
|
|
@@ -26,10 +26,21 @@ export interface PiUsereqSettingsMenuChoice {
|
|
|
26
26
|
export interface PiUsereqSettingsMenuBridge {
|
|
27
27
|
title: string;
|
|
28
28
|
choices: PiUsereqSettingsMenuChoice[];
|
|
29
|
+
selectedChoiceId?: string;
|
|
29
30
|
selectByLabel: (label: string) => boolean;
|
|
30
31
|
cancel: () => void;
|
|
31
32
|
}
|
|
32
33
|
|
|
34
|
+
/**
|
|
35
|
+
* @brief Describes optional behavior overrides for one settings-menu render.
|
|
36
|
+
* @details Carries the caller-selected initial focus row so menu re-renders can
|
|
37
|
+
* preserve selection after an in-place toggle or value edit. The interface is
|
|
38
|
+
* compile-time only and introduces no runtime cost.
|
|
39
|
+
*/
|
|
40
|
+
export interface PiUsereqSettingsMenuOptions {
|
|
41
|
+
initialSelectedId?: string;
|
|
42
|
+
}
|
|
43
|
+
|
|
33
44
|
/**
|
|
34
45
|
* @brief Represents a custom menu component augmented with the offline bridge.
|
|
35
46
|
* @details Extends the generic TUI `Component` contract with one optional bridge field consumed only by deterministic test and debug harness adapters. The interface is compile-time only and introduces no runtime cost.
|
|
@@ -177,13 +188,15 @@ function buildSettingItems(
|
|
|
177
188
|
* @param[in] ctx {ExtensionCommandContext} Active command context.
|
|
178
189
|
* @param[in] title {string} Menu title displayed in the heading and offline bridge.
|
|
179
190
|
* @param[in] choices {PiUsereqSettingsMenuChoice[]} Ordered menu-choice vector.
|
|
191
|
+
* @param[in] options {PiUsereqSettingsMenuOptions | undefined} Optional initial-focus override.
|
|
180
192
|
* @return {Promise<string | undefined>} Selected choice identifier or `undefined` when cancelled.
|
|
181
|
-
* @satisfies REQ-151, REQ-152, REQ-153, REQ-154, REQ-156
|
|
193
|
+
* @satisfies REQ-151, REQ-152, REQ-153, REQ-154, REQ-156, REQ-192
|
|
182
194
|
*/
|
|
183
195
|
export async function showPiUsereqSettingsMenu(
|
|
184
196
|
ctx: ExtensionCommandContext,
|
|
185
197
|
title: string,
|
|
186
198
|
choices: PiUsereqSettingsMenuChoice[],
|
|
199
|
+
options: PiUsereqSettingsMenuOptions = {},
|
|
187
200
|
): Promise<string | undefined> {
|
|
188
201
|
return ctx.ui.custom<string | undefined>((tui, theme, _keybindings, done) => {
|
|
189
202
|
const container = new Container();
|
|
@@ -199,6 +212,12 @@ export async function showPiUsereqSettingsMenu(
|
|
|
199
212
|
() => undefined,
|
|
200
213
|
() => done(undefined),
|
|
201
214
|
);
|
|
215
|
+
const initialSelectedIndex = options.initialSelectedId === undefined
|
|
216
|
+
? 0
|
|
217
|
+
: choices.findIndex((choice) => choice.id === options.initialSelectedId);
|
|
218
|
+
if (initialSelectedIndex >= 0) {
|
|
219
|
+
(settingsList as SettingsList & { selectedIndex: number }).selectedIndex = initialSelectedIndex;
|
|
220
|
+
}
|
|
202
221
|
container.addChild(titleText);
|
|
203
222
|
container.addChild(new Text("", 0, 0));
|
|
204
223
|
container.addChild(settingsList);
|
|
@@ -218,6 +237,7 @@ export async function showPiUsereqSettingsMenu(
|
|
|
218
237
|
__piUsereqSettingsMenu: {
|
|
219
238
|
title,
|
|
220
239
|
choices,
|
|
240
|
+
selectedChoiceId: initialSelectedIndex >= 0 ? choices[initialSelectedIndex]?.id : undefined,
|
|
221
241
|
selectByLabel(label: string): boolean {
|
|
222
242
|
const choice = choices.find(
|
|
223
243
|
(candidate) => candidate.label === label || candidate.id === label,
|
|
@@ -6,9 +6,10 @@
|
|
|
6
6
|
|
|
7
7
|
import fs from "node:fs";
|
|
8
8
|
import path from "node:path";
|
|
9
|
-
import {
|
|
9
|
+
import { createRequire } from "node:module";
|
|
10
10
|
import { detectLanguage as detectSourceLanguage } from "./compress.js";
|
|
11
11
|
import { parseDoxygenComment, type DoxygenFieldMap } from "./doxygen-parser.js";
|
|
12
|
+
import { ReqError } from "./errors.js";
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* @brief Declares the tokenizer encoding used by token-count workflows.
|
|
@@ -175,13 +176,11 @@ export interface TokenToolGuidanceSection {
|
|
|
175
176
|
|
|
176
177
|
/**
|
|
177
178
|
* @brief Describes the full agent-oriented token payload.
|
|
178
|
-
* @details
|
|
179
|
+
* @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
180
|
*/
|
|
180
181
|
export interface TokenToolPayload {
|
|
181
|
-
request: TokenToolRequestSection;
|
|
182
182
|
summary: TokenToolSummarySection;
|
|
183
183
|
files: TokenToolFileEntry[];
|
|
184
|
-
guidance: TokenToolGuidanceSection;
|
|
185
184
|
}
|
|
186
185
|
|
|
187
186
|
/**
|
|
@@ -198,6 +197,57 @@ export interface BuildTokenToolPayloadOptions {
|
|
|
198
197
|
encodingName?: string;
|
|
199
198
|
}
|
|
200
199
|
|
|
200
|
+
type TokenCounterEncoding = {
|
|
201
|
+
encode(content: string): ArrayLike<number>;
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
type JsTiktokenModule = {
|
|
205
|
+
getEncoding(encodingName: string): TokenCounterEncoding;
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
const require = createRequire(import.meta.url);
|
|
209
|
+
|
|
210
|
+
function defaultJsTiktokenModuleLoader(): JsTiktokenModule {
|
|
211
|
+
return require("js-tiktoken") as JsTiktokenModule;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
let jsTiktokenModuleLoader: () => JsTiktokenModule = defaultJsTiktokenModuleLoader;
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* @brief Overrides the `js-tiktoken` loader for tests.
|
|
218
|
+
* @details Enables deterministic dependency-failure tests without mutating repository dependencies on disk. Runtime is O(1). Side effects are limited to module-local test state.
|
|
219
|
+
* @param[in] loader {(() => JsTiktokenModule) | undefined} Replacement loader, or `undefined` to restore the default loader.
|
|
220
|
+
* @return {void} No return value.
|
|
221
|
+
*/
|
|
222
|
+
export function setJsTiktokenModuleLoaderForTests(loader?: () => JsTiktokenModule): void {
|
|
223
|
+
jsTiktokenModuleLoader = loader ?? defaultJsTiktokenModuleLoader;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* @brief Loads the `js-tiktoken` module on demand.
|
|
228
|
+
* @details Defers dependency resolution until token counting is requested so extension registration can succeed even when the optional runtime dependency has not yet been installed. Runtime is O(1) plus module resolution cost. Side effects are limited to Node module loading.
|
|
229
|
+
* @return {JsTiktokenModule} Loaded tokenizer module.
|
|
230
|
+
* @throws {ReqError} Throws when `js-tiktoken` is unavailable.
|
|
231
|
+
*/
|
|
232
|
+
function loadJsTiktokenModule(): JsTiktokenModule {
|
|
233
|
+
try {
|
|
234
|
+
const module = jsTiktokenModuleLoader();
|
|
235
|
+
if (!module || typeof module.getEncoding !== "function") {
|
|
236
|
+
throw new ReqError("Error: token-count dependency 'js-tiktoken' is unavailable. Run `npm ci` in the PI-useReq repository.", 1);
|
|
237
|
+
}
|
|
238
|
+
return module;
|
|
239
|
+
} catch (error) {
|
|
240
|
+
if (error instanceof ReqError) {
|
|
241
|
+
throw error;
|
|
242
|
+
}
|
|
243
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
244
|
+
if (/Cannot find (module|package) 'js-tiktoken'|ERR_MODULE_NOT_FOUND/.test(message)) {
|
|
245
|
+
throw new ReqError("Error: token-count dependency 'js-tiktoken' is not installed. Run `npm ci` in the PI-useReq repository.", 1);
|
|
246
|
+
}
|
|
247
|
+
throw error;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
201
251
|
/**
|
|
202
252
|
* @brief Encapsulates one tokenizer instance for repeated token counting.
|
|
203
253
|
* @details Caches a `js-tiktoken` encoding object so multiple documents can be counted without repeated encoding lookup. Counting cost is O(n) in content length. The class mutates only instance state during construction.
|
|
@@ -207,7 +257,7 @@ export class TokenCounter {
|
|
|
207
257
|
* @brief Stores the tokenizer implementation used for subsequent counts.
|
|
208
258
|
* @details The field holds the encoder returned by `getEncoding`. Access complexity is O(1). The value is initialized once per instance.
|
|
209
259
|
*/
|
|
210
|
-
private encoding;
|
|
260
|
+
private encoding: TokenCounterEncoding;
|
|
211
261
|
|
|
212
262
|
/**
|
|
213
263
|
* @brief Initializes a token counter for one encoding family.
|
|
@@ -216,7 +266,7 @@ export class TokenCounter {
|
|
|
216
266
|
* @return {TokenCounter} New token counter instance.
|
|
217
267
|
*/
|
|
218
268
|
constructor(encodingName = TOKEN_COUNTER_ENCODING) {
|
|
219
|
-
this.encoding = getEncoding(encodingName);
|
|
269
|
+
this.encoding = loadJsTiktokenModule().getEncoding(encodingName);
|
|
220
270
|
}
|
|
221
271
|
|
|
222
272
|
/**
|
|
@@ -359,41 +409,6 @@ function roundRatio(numerator: number, denominator: number): number {
|
|
|
359
409
|
return Number((numerator / denominator).toFixed(6));
|
|
360
410
|
}
|
|
361
411
|
|
|
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
412
|
/**
|
|
398
413
|
* @brief Probes one requested path before token counting.
|
|
399
414
|
* @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 +511,9 @@ export function countFilesMetrics(filePaths: string[], encodingName = TOKEN_COUN
|
|
|
496
511
|
|
|
497
512
|
/**
|
|
498
513
|
* @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,
|
|
514
|
+
* @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
515
|
* @param[in] options {BuildTokenToolPayloadOptions} Payload-construction options.
|
|
501
|
-
* @return {TokenToolPayload} Structured token payload ordered as
|
|
516
|
+
* @return {TokenToolPayload} Structured token payload ordered as summary then files.
|
|
502
517
|
* @satisfies REQ-010, REQ-017, REQ-069, REQ-070, REQ-071, REQ-073, REQ-074, REQ-075
|
|
503
518
|
*/
|
|
504
519
|
export function buildTokenToolPayload(options: BuildTokenToolPayloadOptions): TokenToolPayload {
|
|
@@ -596,71 +611,7 @@ export function buildTokenToolPayload(options: BuildTokenToolPayloadOptions): To
|
|
|
596
611
|
const countedFileCount = filesWithShares.filter((entry) => entry.status === "counted").length;
|
|
597
612
|
const errorFileCount = filesWithShares.filter((entry) => entry.status === "error").length;
|
|
598
613
|
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
614
|
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
615
|
summary: {
|
|
665
616
|
processable_file_count: countedFileCount + errorFileCount,
|
|
666
617
|
counted_file_count: countedFileCount,
|
|
@@ -676,23 +627,6 @@ export function buildTokenToolPayload(options: BuildTokenToolPayloadOptions): To
|
|
|
676
627
|
average_line_count_per_counted_file: countedFileCount === 0 ? 0 : Number((totalLineCount / countedFileCount).toFixed(6)),
|
|
677
628
|
},
|
|
678
629
|
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
630
|
};
|
|
697
631
|
}
|
|
698
632
|
|