pi-usereq 0.5.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.
@@ -156,10 +156,11 @@ export interface FindToolRequestSection {
156
156
 
157
157
  /**
158
158
  * @brief Describes the summary section of the find payload.
159
- * @details Exposes aggregate file, match, line, and Doxygen counts as numeric fields plus one stable search-status discriminator. The interface is compile-time only and introduces no runtime cost.
159
+ * @details Exposes aggregate file, match, line, and Doxygen counts as numeric fields plus one stable search-status discriminator and the normalized validation error when request parsing fails. The interface is compile-time only and introduces no runtime cost.
160
160
  */
161
161
  export interface FindToolSummarySection {
162
162
  search_status: FindSearchStatus;
163
+ validation_error_message?: string;
163
164
  processable_file_count: number;
164
165
  matched_file_count: number;
165
166
  no_match_file_count: number;
@@ -172,22 +173,20 @@ export interface FindToolSummarySection {
172
173
 
173
174
  /**
174
175
  * @brief Describes the repository section of the find payload.
175
- * @details Stores the base path, configured source-directory scope, canonical file list, and supported-tag matrix needed to specialize later searches without rereading tool descriptions. The interface is compile-time only and introduces no runtime cost.
176
+ * @details Stores the base path, configured source-directory scope, and canonical file list used during search while omitting the static supported-tag matrix because that data belongs in tool registration metadata. The interface is compile-time only and introduces no runtime cost.
176
177
  */
177
178
  export interface FindToolRepositorySection {
178
179
  root_directory_path: string;
179
180
  source_directory_paths: string[];
180
181
  file_count: number;
181
182
  file_canonical_paths: string[];
182
- supported_tags_by_language: Record<string, string[]>;
183
183
  }
184
184
 
185
185
  /**
186
186
  * @brief Describes the full agent-oriented find payload.
187
- * @details Orders the top-level sections as request, summary, repository, and files so execution metadata can be appended deterministically by the tool wrapper. The interface is compile-time only and introduces no runtime cost.
187
+ * @details Exposes only aggregate search totals, repository scope, and per-file match records, omitting request echoes and static supported-tag matrices that already belong in registration metadata. The interface is compile-time only and introduces no runtime cost.
188
188
  */
189
189
  export interface FindToolPayload {
190
- request: FindToolRequestSection;
191
190
  summary: FindToolSummarySection;
192
191
  repository: FindToolRepositorySection;
193
192
  files: FindToolFileEntry[];
@@ -262,19 +261,6 @@ function buildLineRange(startLineNumber: number, endLineNumber: number): FindLin
262
261
  };
263
262
  }
264
263
 
265
- /**
266
- * @brief Returns the supported-tag matrix ordered for deterministic JSON emission.
267
- * @details Sorts languages alphabetically and tag arrays lexicographically so downstream agents can reuse the matrix without reparsing human prose. Runtime is O(l * t log t). No side effects occur.
268
- * @return {Record<string, string[]>} Supported tags keyed by canonical language identifier.
269
- */
270
- function buildSupportedTagsByLanguage(): Record<string, string[]> {
271
- return Object.fromEntries(
272
- Object.entries(LANGUAGE_TAGS)
273
- .sort(([left], [right]) => left.localeCompare(right))
274
- .map(([language, tagSet]) => [language, [...tagSet].sort()]),
275
- );
276
- }
277
-
278
264
  /**
279
265
  * @brief Resolves one stable symbol name from an analyzed element.
280
266
  * @details Prefers explicit analyzer name metadata, then falls back to the derived signature or the first source line so every matched construct retains a direct-access identifier. Runtime is O(1). No side effects occur.
@@ -717,9 +703,9 @@ function analyzeFindFile(
717
703
 
718
704
  /**
719
705
  * @brief Builds the full agent-oriented find payload.
720
- * @details Validates request parameters, analyzes requested files in caller order when the request is valid, preserves skipped and no-match outcomes in structured file entries, computes aggregate numeric totals, and emits a structured supported-tag matrix. Runtime is O(F log F + S + M). Side effects are limited to filesystem reads and optional stderr logging.
706
+ * @details Validates request parameters, analyzes requested files in caller order when the request is valid, preserves skipped and no-match outcomes in structured file entries, computes aggregate numeric totals, and omits request echoes plus the static supported-tag matrix already encoded in registration metadata. Runtime is O(F log F + S + M). Side effects are limited to filesystem reads and optional stderr logging.
721
707
  * @param[in] options {BuildFindToolPayloadOptions} Payload-construction options.
722
- * @return {FindToolPayload} Structured find payload ordered as request, summary, repository, and files.
708
+ * @return {FindToolPayload} Structured find payload ordered as summary, repository, and files.
723
709
  * @satisfies REQ-089, REQ-090, REQ-091, REQ-092, REQ-093, REQ-094, REQ-096, REQ-098
724
710
  */
725
711
  export function buildFindToolPayload(options: BuildFindToolPayloadOptions): FindToolPayload {
@@ -835,28 +821,20 @@ export function buildFindToolPayload(options: BuildFindToolPayloadOptions): Find
835
821
  const processableFiles = files.filter((file) => file.status !== "skipped");
836
822
  const repositoryFileCanonicalPaths = files.map((file) => file.canonical_path);
837
823
 
824
+ void toolName;
825
+ void scope;
826
+ void lineNumberMode;
827
+ void tagFilter;
828
+ void pattern;
829
+ void canonicalRequestedPaths;
838
830
  return {
839
- request: {
840
- tool_name: toolName,
841
- scope,
842
- base_dir_path: absoluteBaseDir,
843
- line_number_mode: lineNumberMode,
844
- tag_filter_text: tagFilter,
845
- tag_filter_values: tagValidation.tagValues,
846
- tag_filter_status: tagValidation.status,
847
- tag_filter_error_message: tagValidation.errorMessage,
848
- name_regex_text: pattern,
849
- regex_engine: "javascript-regexp-search",
850
- regex_status: regexValidation.status,
851
- regex_error_message: regexValidation.errorMessage,
852
- source_directory_count: sourceDirectoryPaths.length,
853
- source_directory_paths: sourceDirectoryPaths.map((sourceDirectoryPath) => canonicalizeFindPath(sourceDirectoryPath, absoluteBaseDir)),
854
- requested_file_count: requestedPaths.length,
855
- requested_input_paths: [...requestedPaths],
856
- requested_canonical_paths: canonicalRequestedPaths,
857
- },
858
831
  summary: {
859
832
  search_status: searchStatus,
833
+ validation_error_message: tagValidation.status === "invalid"
834
+ ? tagValidation.errorMessage
835
+ : regexValidation.status === "invalid"
836
+ ? regexValidation.errorMessage
837
+ : undefined,
860
838
  processable_file_count: processableFiles.length,
861
839
  matched_file_count: matchedFiles.length,
862
840
  no_match_file_count: files.filter((file) => file.status === "no_match").length,
@@ -874,7 +852,6 @@ export function buildFindToolPayload(options: BuildFindToolPayloadOptions): Find
874
852
  source_directory_paths: sourceDirectoryPaths.map((sourceDirectoryPath) => canonicalizeFindPath(sourceDirectoryPath, absoluteBaseDir)),
875
853
  file_count: repositoryFileCanonicalPaths.length,
876
854
  file_canonical_paths: repositoryFileCanonicalPaths,
877
- supported_tags_by_language: buildSupportedTagsByLanguage(),
878
855
  },
879
856
  files,
880
857
  };
@@ -889,11 +866,11 @@ export function buildFindToolPayload(options: BuildFindToolPayloadOptions): Find
889
866
  */
890
867
  export function buildFindToolExecutionStderr(payload: FindToolPayload): string {
891
868
  const diagnostics: string[] = [];
892
- if (payload.request.tag_filter_status === "invalid") {
893
- diagnostics.push(`error: tag_filter: ${payload.request.tag_filter_error_message ?? "invalid tag filter"}`);
869
+ if (payload.summary.search_status === "invalid_tag_filter") {
870
+ diagnostics.push(`error: tag_filter: ${payload.summary.validation_error_message ?? "invalid tag filter"}`);
894
871
  }
895
- if (payload.request.regex_status === "invalid") {
896
- diagnostics.push(`error: name_regex: ${payload.request.regex_error_message ?? "invalid regex"}`);
872
+ if (payload.summary.search_status === "invalid_regex") {
873
+ diagnostics.push(`error: name_regex: ${payload.summary.validation_error_message ?? "invalid regex"}`);
897
874
  }
898
875
  payload.files.forEach((file) => {
899
876
  if (file.status === "skipped") {
@@ -182,10 +182,9 @@ export interface ReferenceToolRepositorySection {
182
182
 
183
183
  /**
184
184
  * @brief Describes the full agent-oriented references payload.
185
- * @details Orders the top-level sections as request, summary, repository, and files for deterministic downstream traversal. The interface is compile-time only and introduces no runtime cost.
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 a structured repository tree. Runtime is O(F log F + S). Side effects are limited to filesystem reads and optional stderr logging.
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 request, summary, repository, and files.
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,
@@ -175,13 +175,11 @@ export interface TokenToolGuidanceSection {
175
175
 
176
176
  /**
177
177
  * @brief Describes the full agent-oriented token payload.
178
- * @details Orders the top-level sections as request, summary, files, and guidance for deterministic downstream traversal. The interface is compile-time only and introduces no runtime cost.
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, separates raw observations from derived guidance, and emits direct-access file facts such as line ranges, sizes, headings, and optional Doxygen file fields. Runtime is O(F log F + S). Side effects are limited to filesystem reads.
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 request, summary, files, guidance.
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