@mrclrchtr/supi-code-intelligence 5.0.0 → 6.0.1

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.
Files changed (151) hide show
  1. package/README.md +4 -2
  2. package/node_modules/@mrclrchtr/supi-code-runtime/README.md +2 -0
  3. package/node_modules/@mrclrchtr/supi-code-runtime/package.json +1 -1
  4. package/node_modules/@mrclrchtr/supi-core/README.md +2 -0
  5. package/node_modules/@mrclrchtr/supi-core/package.json +2 -2
  6. package/node_modules/@mrclrchtr/supi-core/src/api.ts +1 -1
  7. package/node_modules/@mrclrchtr/supi-core/src/debug-registry.ts +0 -3
  8. package/node_modules/@mrclrchtr/supi-core/src/debug.ts +9 -0
  9. package/node_modules/@mrclrchtr/supi-core/src/index.ts +1 -1
  10. package/node_modules/@mrclrchtr/supi-lsp/README.md +36 -3
  11. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/README.md +2 -0
  12. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/package.json +1 -1
  13. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/README.md +2 -0
  14. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/package.json +2 -2
  15. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/api.ts +1 -1
  16. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/debug-registry.ts +0 -3
  17. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/debug.ts +9 -0
  18. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/index.ts +1 -1
  19. package/node_modules/@mrclrchtr/supi-lsp/package.json +3 -3
  20. package/node_modules/@mrclrchtr/supi-lsp/src/api.ts +12 -1
  21. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-cache.ts +67 -28
  22. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-collection.ts +63 -6
  23. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-evidence.ts +46 -5
  24. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-host.ts +1 -0
  25. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-publication.ts +214 -0
  26. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-refresh.ts +278 -79
  27. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-timing.ts +29 -8
  28. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostics.ts +101 -19
  29. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-document-state.ts +19 -1
  30. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-document-sync.ts +49 -3
  31. package/node_modules/@mrclrchtr/supi-lsp/src/client/client.ts +73 -15
  32. package/node_modules/@mrclrchtr/supi-lsp/src/client/transport.ts +1 -1
  33. package/node_modules/@mrclrchtr/supi-lsp/src/config/capabilities.ts +2 -2
  34. package/node_modules/@mrclrchtr/supi-lsp/src/config/config.ts +5 -5
  35. package/node_modules/@mrclrchtr/supi-lsp/src/config/server-config.ts +3 -0
  36. package/node_modules/@mrclrchtr/supi-lsp/src/config/tsconfig-extends.ts +68 -0
  37. package/node_modules/@mrclrchtr/supi-lsp/src/config/tsconfig-path.ts +8 -0
  38. package/node_modules/@mrclrchtr/supi-lsp/src/config/tsconfig-scope.ts +176 -24
  39. package/node_modules/@mrclrchtr/supi-lsp/src/diagnostics/evidence.ts +4 -0
  40. package/node_modules/@mrclrchtr/supi-lsp/src/diagnostics/workspace-sentinels.ts +46 -10
  41. package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-diagnostics.ts +4 -3
  42. package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-workspace-recovery.ts +65 -17
  43. package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager.ts +143 -21
  44. package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-diagnostic-surface.ts +2 -0
  45. package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-diagnostics.ts +43 -1
  46. package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-registry.ts +31 -0
  47. package/node_modules/@mrclrchtr/supi-tree-sitter/README.md +2 -0
  48. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/README.md +2 -0
  49. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/package.json +1 -1
  50. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/README.md +2 -0
  51. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/package.json +2 -2
  52. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/api.ts +1 -1
  53. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/debug-registry.ts +0 -3
  54. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/debug.ts +9 -0
  55. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/index.ts +1 -1
  56. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/debug/web-tree-sitter.wasm +0 -0
  57. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/debug/web-tree-sitter.wasm.map +4 -4
  58. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/package.json +1 -1
  59. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/web-tree-sitter.wasm +0 -0
  60. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/web-tree-sitter.wasm.map +4 -4
  61. package/node_modules/@mrclrchtr/supi-tree-sitter/package.json +3 -3
  62. package/package.json +5 -5
  63. package/src/analysis/health/diagnostics.ts +58 -126
  64. package/src/analysis/health/file-scope.ts +184 -0
  65. package/src/analysis/health/recovery.ts +8 -1
  66. package/src/analysis/readiness.ts +4 -0
  67. package/src/analysis/search/ast-scan.ts +4 -1
  68. package/src/analysis/search/pattern-analysis.ts +10 -3
  69. package/src/analysis/search/pattern.ts +6 -1
  70. package/src/config.ts +35 -1
  71. package/src/extension.ts +57 -14
  72. package/src/overview/overview-data.ts +9 -2
  73. package/src/overview/overview.ts +49 -30
  74. package/src/overview/types.ts +2 -0
  75. package/src/session/find-types.ts +2 -2
  76. package/src/session/find-workflow.ts +3 -2
  77. package/src/session/health-refresh.ts +76 -49
  78. package/src/session/health-types.ts +7 -1
  79. package/src/session/health-workflow.ts +35 -18
  80. package/src/session/input/workflows.ts +2 -2
  81. package/src/session/inspect-workflow.ts +2 -1
  82. package/src/session/orientation/collect.ts +2 -2
  83. package/src/session/orientation-types.ts +2 -0
  84. package/src/session/orientation-workflow.ts +8 -2
  85. package/src/session/refactor-workflow.ts +2 -1
  86. package/src/session/session.ts +25 -1
  87. package/src/substrate/lsp/maintenance.ts +105 -23
  88. package/src/substrate/lsp/recovery.ts +11 -5
  89. package/src/tool/code_find/execute.ts +23 -0
  90. package/src/tool/code_find/guidance.ts +8 -0
  91. package/src/tool/{find → code_find}/render.ts +1 -1
  92. package/src/tool/{result/find.ts → code_find/result.ts} +36 -11
  93. package/src/tool/code_find/spec.ts +37 -0
  94. package/src/tool/code_graph/execute.ts +24 -0
  95. package/src/tool/code_graph/guidance.ts +6 -0
  96. package/src/tool/{graph → code_graph}/markdown.ts +1 -1
  97. package/src/tool/{result/graph.ts → code_graph/result.ts} +69 -4
  98. package/src/tool/code_graph/spec.ts +39 -0
  99. package/src/tool/{health → code_health}/execute.ts +2 -18
  100. package/src/tool/code_health/guidance.ts +8 -0
  101. package/src/tool/{health → code_health}/markdown.ts +68 -18
  102. package/src/tool/{result/health.ts → code_health/result.ts} +29 -4
  103. package/src/tool/code_health/spec.ts +41 -0
  104. package/src/tool/code_inspect/execute.ts +20 -0
  105. package/src/tool/code_inspect/guidance.ts +6 -0
  106. package/src/tool/{inspect → code_inspect}/markdown.ts +1 -1
  107. package/src/tool/{result/inspect.ts → code_inspect/result.ts} +32 -3
  108. package/src/tool/code_inspect/spec.ts +25 -0
  109. package/src/tool/code_orientation/execute.ts +25 -0
  110. package/src/tool/code_orientation/guidance.ts +9 -0
  111. package/src/tool/{orientation → code_orientation}/markdown.ts +1 -1
  112. package/src/tool/{result/orientation.ts → code_orientation/result.ts} +67 -3
  113. package/src/tool/code_orientation/spec.ts +25 -0
  114. package/src/tool/code_refactor_apply/execute.ts +17 -0
  115. package/src/tool/code_refactor_apply/guidance.ts +5 -0
  116. package/src/tool/{refactor-apply/execute.ts → code_refactor_apply/result.ts} +5 -11
  117. package/src/tool/code_refactor_apply/spec.ts +26 -0
  118. package/src/tool/code_refactor_plan/execute.ts +27 -0
  119. package/src/tool/code_refactor_plan/guidance.ts +8 -0
  120. package/src/tool/{refactor-plan/execute.ts → code_refactor_plan/result.ts} +9 -22
  121. package/src/tool/code_refactor_plan/spec.ts +25 -0
  122. package/src/tool/code_resolve/execute.ts +19 -0
  123. package/src/tool/code_resolve/guidance.ts +8 -0
  124. package/src/tool/{resolve → code_resolve}/markdown.ts +1 -1
  125. package/src/tool/{result/resolve.ts → code_resolve/result.ts} +37 -24
  126. package/src/tool/code_resolve/spec.ts +25 -0
  127. package/src/tool/guidance.ts +71 -52
  128. package/src/tool/infra/truncate.ts +40 -0
  129. package/src/tool/{refactor-plan/markdown.ts → refactor-markdown.ts} +4 -7
  130. package/src/tool/register.ts +9 -97
  131. package/src/tool/result/refactor.ts +1 -4
  132. package/src/tool/schemas.ts +26 -157
  133. package/src/tool/specs.ts +40 -126
  134. package/src/tool/find/execute.ts +0 -43
  135. package/src/tool/graph/execute.ts +0 -82
  136. package/src/tool/inspect/execute.ts +0 -40
  137. package/src/tool/orientation/execute.ts +0 -80
  138. package/src/tool/resolve/execute.ts +0 -53
  139. /package/src/tool/{find → code_find}/ast-kinds.ts +0 -0
  140. /package/src/tool/{find → code_find}/markdown.ts +0 -0
  141. /package/src/tool/{find → code_find}/modes.ts +0 -0
  142. /package/src/tool/{find → code_find}/tui.ts +0 -0
  143. /package/src/tool/{graph → code_graph}/tui.ts +0 -0
  144. /package/src/tool/{health → code_health}/refresh-status.ts +0 -0
  145. /package/src/tool/{health → code_health}/semantic-state.ts +0 -0
  146. /package/src/tool/{health → code_health}/tui.ts +0 -0
  147. /package/src/tool/{inspect → code_inspect}/tui.ts +0 -0
  148. /package/src/tool/{orientation → code_orientation}/tui.ts +0 -0
  149. /package/src/tool/{refactor-apply → code_refactor_apply}/tui.ts +0 -0
  150. /package/src/tool/{refactor-plan → code_refactor_plan}/tui.ts +0 -0
  151. /package/src/tool/{resolve → code_resolve}/tui.ts +0 -0
@@ -10,16 +10,19 @@ import {
10
10
  unavailableCodeQuery,
11
11
  } from "@mrclrchtr/supi-code-runtime/api";
12
12
  import * as projectRoots from "@mrclrchtr/supi-core/project";
13
+ import * as ts from "typescript";
13
14
  import {
14
15
  LspClient,
15
16
  type LspClientLifecycleTransitionKind,
16
17
  RECOVERY_CLIENT_STARTUP_BOUND_MS,
17
18
  withTimeout,
18
19
  } from "../client/client.ts";
20
+ import type { ClientDiagnosticSnapshot } from "../client/client-document-state.ts";
19
21
 
20
22
  export { RECOVERY_CLIENT_STARTUP_BOUND_MS } from "../client/client.ts";
21
23
 
22
24
  import { getServerForFile } from "../config/config.ts";
25
+ import { getFileScopeDecision } from "../config/tsconfig-scope.ts";
23
26
  import type {
24
27
  DetectedProjectServer,
25
28
  Diagnostic,
@@ -42,6 +45,11 @@ import {
42
45
  summarizeDiagnosticEvidence,
43
46
  } from "../diagnostics/evidence.ts";
44
47
  import { raceRequestControl } from "../session/readiness.ts";
48
+ import type {
49
+ ScopeDecisionEntry,
50
+ ScopeDecisionSummary,
51
+ WorkspaceDiagnosticReport,
52
+ } from "../session/runtime-diagnostics.ts";
45
53
  import {
46
54
  displayRelativeFilePath,
47
55
  formatCoverageSummaryText,
@@ -89,6 +97,35 @@ import {
89
97
 
90
98
  type UnavailableReason = "missing-command" | "start-failed" | "runtime-error";
91
99
 
100
+ /** Maximum tracked-file entries retained in one scope-decision telemetry summary. */
101
+ const SCOPE_DECISION_MAX_ENTRIES = 24;
102
+
103
+ interface ScopeDecisionAccumulator {
104
+ entries: ScopeDecisionEntry[];
105
+ counts: ScopeDecisionSummary["counts"];
106
+ basisCounts: Record<string, number>;
107
+ }
108
+
109
+ function accumulateScopeDecision(
110
+ accumulator: ScopeDecisionAccumulator,
111
+ file: string,
112
+ decision: ReturnType<typeof getFileScopeDecision>,
113
+ ): void {
114
+ const statusKey =
115
+ decision.status === "no-config"
116
+ ? "noConfig"
117
+ : decision.status === "out-of-tree"
118
+ ? "outOfTree"
119
+ : decision.status;
120
+ accumulator.counts[statusKey]++;
121
+ if (decision.basis) {
122
+ accumulator.basisCounts[decision.basis] = (accumulator.basisCounts[decision.basis] ?? 0) + 1;
123
+ }
124
+ if (accumulator.entries.length < SCOPE_DECISION_MAX_ENTRIES) {
125
+ accumulator.entries.push({ file, status: decision.status, basis: decision.basis });
126
+ }
127
+ }
128
+
92
129
  type FileRoute = {
93
130
  serverName: string;
94
131
  serverConfig: ServerConfig;
@@ -639,6 +676,8 @@ export class LspManager {
639
676
  restartIfStillStale?: boolean;
640
677
  maxWaitMs?: number;
641
678
  quietMs?: number;
679
+ /** Evidence from a refresh the caller already completed; skips this pass's own refresh when no watched-file changes apply. */
680
+ initialEvidence?: DiagnosticEvidenceSummary;
642
681
  control?: CodeRequestControl;
643
682
  }): Promise<WorkspaceRecoveryResult> {
644
683
  return recoverWorkspaceDiagnosticsImpl(this, options);
@@ -767,6 +806,55 @@ export class LspManager {
767
806
  .filter((entry) => entry.openFiles.length > 0);
768
807
  return formatCoverageSummaryText(relevantEntries, maxServers, maxFiles);
769
808
  }
809
+ /**
810
+ * Capture one coherent diagnostic report from the current client snapshots.
811
+ *
812
+ * Summary and detailed projections share the same cache observation so a
813
+ * caller cannot combine entries from one generation with evidence from
814
+ * another.
815
+ */
816
+ getWorkspaceDiagnosticReport(maxSeverity: number = 2): WorkspaceDiagnosticReport {
817
+ this.pruneMissingFiles();
818
+ const snapshots = Array.from(this.clients.values()).map((client) =>
819
+ client.getDiagnosticSnapshot(),
820
+ );
821
+ const evidence = this.toWorkspaceDiagnosticEvidence(
822
+ snapshots.flatMap((snapshot) =>
823
+ snapshot.documents.map((document) => ({
824
+ file: relativeFilePathFromUri(document.uri, this.cwd),
825
+ status: document.status,
826
+ })),
827
+ ),
828
+ );
829
+ const current =
830
+ snapshots.every((snapshot) => snapshot.current) &&
831
+ evidence.documents.every((document) => document.status === "confirmed");
832
+ const fileDiags = new Map<string, { errors: number; warnings: number }>();
833
+ for (const snapshot of snapshots) {
834
+ for (const entry of snapshot.entries) {
835
+ collectDiagnosticSummaryCounts(fileDiags, entry, this.cwd, this.excludePatterns);
836
+ }
837
+ }
838
+
839
+ return {
840
+ summary: {
841
+ entries: Array.from(fileDiags.entries()).map(([file, counts]) => ({ file, ...counts })),
842
+ current,
843
+ evidence,
844
+ },
845
+ outstanding: {
846
+ entries: collectOutstandingDiagnosticsDetailed(
847
+ snapshots.map((snapshot) => snapshot.entries),
848
+ this.cwd,
849
+ this.excludePatterns,
850
+ maxSeverity,
851
+ ),
852
+ current,
853
+ evidence,
854
+ },
855
+ };
856
+ }
857
+
770
858
  /** Get a diagnostic summary across all servers and files. */
771
859
  getDiagnosticSummary(): DiagnosticSummary[] {
772
860
  return this.getDiagnosticSnapshot().entries;
@@ -811,15 +899,16 @@ export class LspManager {
811
899
  evidence: DiagnosticEvidenceSummary;
812
900
  } {
813
901
  this.pruneMissingFiles();
814
- const evidence = this.toWorkspaceDiagnosticEvidence(
815
- collectClientDiagnosticEvidenceDocuments(this.clients.values(), this.cwd),
902
+ const snapshots = Array.from(this.clients.values()).map((client) =>
903
+ client.getDiagnosticSnapshot(),
816
904
  );
817
- const clientCurrent = Array.from(this.clients.values()).every(
818
- (client) => client.getDiagnosticSnapshot().current,
905
+ const evidence = this.toWorkspaceDiagnosticEvidence(
906
+ collectClientDiagnosticEvidenceDocuments(snapshots, this.cwd),
819
907
  );
908
+ const clientCurrent = snapshots.every((snapshot) => snapshot.current);
820
909
  return {
821
910
  entries: collectOutstandingDiagnosticSummaryEntries(
822
- this.clients.values(),
911
+ snapshots,
823
912
  this.cwd,
824
913
  this.excludePatterns,
825
914
  maxSeverity,
@@ -854,29 +943,62 @@ export class LspManager {
854
943
  evidence: DiagnosticEvidenceSummary;
855
944
  } {
856
945
  this.pruneMissingFiles();
857
- const clients = Array.from(this.clients.values());
858
- let clientCurrent = true;
859
- const evidenceDocuments = clients.flatMap((client) => {
860
- const snapshot = client.getDiagnosticSnapshot();
861
- clientCurrent &&= snapshot.current;
862
- return snapshot.documents.map((document) => ({
946
+ const snapshots = Array.from(this.clients.values()).map((client) =>
947
+ client.getDiagnosticSnapshot(),
948
+ );
949
+ const evidenceDocuments = snapshots.flatMap((snapshot) =>
950
+ snapshot.documents.map((document) => ({
863
951
  file: relativeFilePathFromUri(document.uri, this.cwd),
864
952
  status: document.status,
865
- }));
866
- });
953
+ })),
954
+ );
867
955
  const evidence = this.toWorkspaceDiagnosticEvidence(evidenceDocuments);
868
956
  return {
869
957
  entries: collectOutstandingDiagnosticsDetailed(
870
- clients,
958
+ snapshots.map((snapshot) => snapshot.entries),
871
959
  this.cwd,
872
960
  this.excludePatterns,
873
961
  maxSeverity,
874
962
  ),
875
963
  current:
876
- clientCurrent && evidence.documents.every((document) => document.status === "confirmed"),
964
+ snapshots.every((snapshot) => snapshot.current) &&
965
+ evidence.documents.every((document) => document.status === "confirmed"),
877
966
  evidence,
878
967
  };
879
968
  }
969
+
970
+ /**
971
+ * Aggregate the tsconfig scope decision for every tracked file.
972
+ *
973
+ * The recovery telemetry surface uses this to report why tracked files are
974
+ * in or out of scope without re-deriving the scope logic. The returned
975
+ * entry list is bounded; counts are exact.
976
+ */
977
+ getScopeDecisionSummary(): ScopeDecisionSummary {
978
+ this.pruneMissingFiles();
979
+ const accumulator: ScopeDecisionAccumulator = {
980
+ entries: [],
981
+ counts: { included: 0, excluded: 0, noConfig: 0, outOfTree: 0 },
982
+ basisCounts: {},
983
+ };
984
+ let totalFiles = 0;
985
+
986
+ for (const client of this.clients.values()) {
987
+ for (const document of client.getDiagnosticSnapshot().documents) {
988
+ const file = relativeFilePathFromUri(document.uri, this.cwd);
989
+ totalFiles++;
990
+ accumulateScopeDecision(accumulator, file, getFileScopeDecision(file, this.cwd));
991
+ }
992
+ }
993
+
994
+ return {
995
+ caseSensitiveFileNames: ts.sys.useCaseSensitiveFileNames,
996
+ counts: accumulator.counts,
997
+ basisCounts: accumulator.basisCounts,
998
+ entries: accumulator.entries,
999
+ totalFiles,
1000
+ };
1001
+ }
880
1002
  private toWorkspaceDiagnosticEvidence(
881
1003
  documents: readonly DiagnosticEvidenceDocument[],
882
1004
  ): DiagnosticEvidenceSummary {
@@ -1089,11 +1211,11 @@ export class LspManager {
1089
1211
  }
1090
1212
 
1091
1213
  function collectClientDiagnosticEvidenceDocuments(
1092
- clients: Iterable<LspClient>,
1214
+ snapshots: Iterable<ClientDiagnosticSnapshot>,
1093
1215
  cwd: string,
1094
1216
  ): DiagnosticEvidenceDocument[] {
1095
- return Array.from(clients).flatMap((client) =>
1096
- client.getDiagnosticSnapshot().documents.map((document) => ({
1217
+ return Array.from(snapshots).flatMap((snapshot) =>
1218
+ snapshot.documents.map((document) => ({
1097
1219
  file: relativeFilePathFromUri(document.uri, cwd),
1098
1220
  status: document.status,
1099
1221
  })),
@@ -1101,14 +1223,14 @@ function collectClientDiagnosticEvidenceDocuments(
1101
1223
  }
1102
1224
 
1103
1225
  function collectOutstandingDiagnosticSummaryEntries(
1104
- clients: Iterable<LspClient>,
1226
+ snapshots: Iterable<ClientDiagnosticSnapshot>,
1105
1227
  cwd: string,
1106
1228
  excludePatterns: string[],
1107
1229
  maxSeverity: number,
1108
1230
  ): OutstandingDiagnosticSummaryEntry[] {
1109
1231
  const fileDiags = new Map<string, OutstandingDiagnosticSummaryEntry>();
1110
- for (const client of clients) {
1111
- for (const entry of client.getDiagnosticSnapshot().entries) {
1232
+ for (const snapshot of snapshots) {
1233
+ for (const entry of snapshot.entries) {
1112
1234
  const file = relativeFilePathFromUri(entry.uri, cwd);
1113
1235
  if (shouldIgnoreLspPath(file, cwd) || isExcludedByPattern(file, excludePatterns)) continue;
1114
1236
  const existing = fileDiags.get(file) ?? createOutstandingDiagnosticSummary(file);
@@ -35,6 +35,8 @@ export interface WorkspaceLspDiagnosticSurface {
35
35
  restartIfStillStale?: boolean;
36
36
  maxWaitMs?: number;
37
37
  quietMs?: number;
38
+ /** Evidence from a refresh the caller already completed; skips this pass's own refresh when no watched-file changes apply. */
39
+ initialEvidence?: DiagnosticEvidenceSummary;
38
40
  control?: CodeRequestControl;
39
41
  }): Promise<RecoverDiagnosticsResult>;
40
42
  }
@@ -21,6 +21,20 @@ export interface WorkspaceDiagnosticSummaryEntry {
21
21
  warnings: number;
22
22
  }
23
23
 
24
+ /**
25
+ * One coherent post-refresh observation of workspace diagnostics.
26
+ *
27
+ * Both projections are captured from the same client snapshots. The report is
28
+ * short-lived and must not be retained as the runtime's last refresh attempt.
29
+ */
30
+ export interface WorkspaceDiagnosticReport {
31
+ summary: WorkspaceDiagnosticSnapshot<WorkspaceDiagnosticSummaryEntry>;
32
+ outstanding: WorkspaceDiagnosticSnapshot<{
33
+ file: string;
34
+ diagnostics: Diagnostic[];
35
+ }>;
36
+ }
37
+
24
38
  /** Outstanding diagnostics grouped by file, including info and hint counts. */
25
39
  export interface OutstandingDiagnosticSummaryEntry {
26
40
  file: string;
@@ -36,8 +50,10 @@ export interface RecoverDiagnosticsResult {
36
50
  /** Active clients targeted by the best-effort refresh, not confirmed successful refreshes. */
37
51
  attemptedClients: number;
38
52
  restartedClients: number;
39
- /** Final document-level evidence from the last refresh in this recovery pass. */
53
+ /** Evidence collected by the refresh and recovery operations. */
40
54
  diagnosticEvidence: DiagnosticEvidenceSummary;
55
+ /** Final diagnostic report captured after all refresh and recovery work. */
56
+ diagnosticReport: WorkspaceDiagnosticReport;
41
57
  /** Failure from the first refresh, when no later pass replaced it. */
42
58
  refreshFailureReason?: string;
43
59
  /** Wall-clock duration of the whole recovery pass, for telemetry. */
@@ -48,3 +64,29 @@ export interface RecoverDiagnosticsResult {
48
64
  warning: string | null;
49
65
  };
50
66
  }
67
+
68
+ /** One tracked file's tsconfig scope decision, for debug telemetry. */
69
+ export interface ScopeDecisionEntry {
70
+ /** Workspace-relative file path. */
71
+ file: string;
72
+ status: "included" | "excluded" | "no-config" | "out-of-tree";
73
+ /** Decision mechanism; null when no decision applies. */
74
+ basis: string | null;
75
+ }
76
+
77
+ /** Aggregate tsconfig scope decisions for all tracked files, for debug telemetry. */
78
+ export interface ScopeDecisionSummary {
79
+ caseSensitiveFileNames: boolean;
80
+ counts: {
81
+ included: number;
82
+ excluded: number;
83
+ noConfig: number;
84
+ outOfTree: number;
85
+ };
86
+ /** Exact decision-basis counts; keys are {@link ScopeDecisionEntry.basis} values. */
87
+ basisCounts: Record<string, number>;
88
+ /** Bounded workspace-relative entries (oldest first), not the full set. */
89
+ entries: ScopeDecisionEntry[];
90
+ /** Total number of tracked files the summary was computed over. */
91
+ totalFiles: number;
92
+ }
@@ -45,6 +45,7 @@ export type {
45
45
  DiagnosticEvidenceSummary,
46
46
  OutstandingDiagnosticSummaryEntry,
47
47
  RecoverDiagnosticsResult,
48
+ WorkspaceDiagnosticReport,
48
49
  WorkspaceDiagnosticSnapshot,
49
50
  WorkspaceDiagnosticSummaryEntry,
50
51
  } from "./runtime-diagnostics.ts";
@@ -63,6 +64,33 @@ function unavailableFileQuery<T>(operation: string, file: string): CodeQueryResu
63
64
  return unavailableCodeQuery(`No routed LSP client could complete ${operation} for ${file}.`);
64
65
  }
65
66
 
67
+ /** Emit one aggregate tsconfig scope-decision event after a recovery pass. */
68
+ function recordScopeDecisionEvent(manager: LspManager): void {
69
+ try {
70
+ const summary = manager.getScopeDecisionSummary();
71
+ recordDebugEvent({
72
+ source: "lsp",
73
+ level: "debug",
74
+ category: "diagnostics.scope",
75
+ message: "LSP diagnostic tsconfig scope decisions",
76
+ cwd: truncateIdentity(manager.getCwd()),
77
+ data: {
78
+ caseSensitiveFileNames: summary.caseSensitiveFileNames,
79
+ counts: summary.counts,
80
+ basisCounts: summary.basisCounts,
81
+ totalFiles: summary.totalFiles,
82
+ entries: summary.entries.map((entry) => ({
83
+ file: truncateIdentity(entry.file),
84
+ status: entry.status,
85
+ ...(entry.basis ? { basis: entry.basis } : {}),
86
+ })),
87
+ },
88
+ });
89
+ } catch {
90
+ // Telemetry must never turn a completed recovery into a failure.
91
+ }
92
+ }
93
+
66
94
  class DefaultWorkspaceLspRuntime implements WorkspaceLspRuntime {
67
95
  constructor(private readonly manager: LspManager) {}
68
96
 
@@ -315,6 +343,8 @@ class DefaultWorkspaceLspRuntime implements WorkspaceLspRuntime {
315
343
  restartIfStillStale?: boolean;
316
344
  maxWaitMs?: number;
317
345
  quietMs?: number;
346
+ /** Evidence from a refresh the caller already completed; skips this pass's own refresh when no watched-file changes apply. */
347
+ initialEvidence?: DiagnosticEvidenceSummary;
318
348
  control?: CodeRequestControl;
319
349
  }): Promise<RecoverDiagnosticsResult> {
320
350
  const recoveryStartedAt = Date.now();
@@ -337,6 +367,7 @@ class DefaultWorkspaceLspRuntime implements WorkspaceLspRuntime {
337
367
  ...(result.restartReason ? { reason: result.restartReason } : {}),
338
368
  },
339
369
  });
370
+ recordScopeDecisionEvent(this.manager);
340
371
  return result;
341
372
  } catch (error) {
342
373
  // A cancelled pass has no result object, so telemetry records the
@@ -6,6 +6,8 @@
6
6
 
7
7
  # @mrclrchtr/supi-tree-sitter
8
8
 
9
+ [![GitHub stars](https://img.shields.io/github/stars/mrclrchtr/supi)](https://github.com/mrclrchtr/supi/stargazers)
10
+
9
11
  Tree-sitter structural code analysis library for the [pi coding agent](https://github.com/earendil-works/pi).
10
12
 
11
13
  This is a **library-only** package — it has no pi extension surface. Use `@mrclrchtr/supi-code-intelligence`
@@ -6,6 +6,8 @@
6
6
 
7
7
  # @mrclrchtr/supi-code-runtime
8
8
 
9
+ [![GitHub stars](https://img.shields.io/github/stars/mrclrchtr/supi)](https://github.com/mrclrchtr/supi/stargazers)
10
+
9
11
  Shared workspace context, capability contracts, and canonical types for the SuPi code-understanding stack.
10
12
 
11
13
  This is a **library-only package** — it has no pi extension surface, no user-facing tools, and no UI. It is not installed directly — `supi-lsp`, `supi-tree-sitter`, and `supi-code-intelligence` bundle it and use its shared abstractions to communicate capability availability.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mrclrchtr/supi-code-runtime",
3
- "version": "5.0.0",
3
+ "version": "6.0.1",
4
4
  "description": "Shared workspace context and capability contracts for code intelligence",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -6,6 +6,8 @@
6
6
 
7
7
  # @mrclrchtr/supi-core
8
8
 
9
+ [![GitHub stars](https://img.shields.io/github/stars/mrclrchtr/supi)](https://github.com/mrclrchtr/supi/stargazers)
10
+
9
11
  Shared infrastructure for SuPi extensions.
10
12
 
11
13
  This is a **pure library** — it does not register any pi commands or tools. The `/supi-settings` command is now available through `@mrclrchtr/supi-settings`.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mrclrchtr/supi-core",
3
- "version": "5.0.0",
3
+ "version": "6.0.1",
4
4
  "description": "Shared settings, configuration, reporting, and session infrastructure",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -53,7 +53,7 @@
53
53
  "./api": "./src/api.ts",
54
54
  "./config": "./src/config.ts",
55
55
  "./context": "./src/context.ts",
56
- "./debug": "./src/debug-registry.ts",
56
+ "./debug": "./src/debug.ts",
57
57
  "./evidence-badge": "./src/evidence-badge.ts",
58
58
  "./footer-registry": "./src/footer-registry.ts",
59
59
  "./llm": "./src/llm.ts",
@@ -11,7 +11,7 @@ export * from "./config.ts";
11
11
  // biome-ignore lint/performance/noReExportAll: intentional convenience barrel
12
12
  export * from "./context.ts";
13
13
  // biome-ignore lint/performance/noReExportAll: intentional convenience barrel
14
- export * from "./debug-registry.ts";
14
+ export * from "./debug.ts";
15
15
  // biome-ignore lint/performance/noReExportAll: intentional convenience barrel
16
16
  export * from "./evidence-badge.ts";
17
17
  // biome-ignore lint/performance/noReExportAll: intentional convenience barrel
@@ -4,9 +4,6 @@
4
4
  // supi-debug extension owns policy/configuration and exposes events through a
5
5
  // command/tool while this module stays dependency-free for producers.
6
6
 
7
- // biome-ignore lint/performance/noReExportAll: preserve the stable debug domain entry point
8
- export * from "./debug-timing.ts";
9
-
10
7
  export type DebugLevel = "debug" | "info" | "warning" | "error";
11
8
  export type DebugAgentAccess = "off" | "sanitized" | "raw";
12
9
  export interface DebugRegistryConfig {
@@ -0,0 +1,9 @@
1
+ // Debug domain entry for `@mrclrchtr/supi-core/debug`.
2
+ //
3
+ // Kept separate from debug-registry.ts so debug-timing.ts can import the
4
+ // registry without creating an import cycle through the barrel re-export.
5
+
6
+ // biome-ignore lint/performance/noReExportAll: preserve the stable debug domain entry point
7
+ export * from "./debug-registry.ts";
8
+ // biome-ignore lint/performance/noReExportAll: preserve the stable debug domain entry point
9
+ export * from "./debug-timing.ts";
@@ -11,7 +11,7 @@ export * from "./config.ts";
11
11
  // biome-ignore lint/performance/noReExportAll: intentional convenience barrel
12
12
  export * from "./context.ts";
13
13
  // biome-ignore lint/performance/noReExportAll: intentional convenience barrel
14
- export * from "./debug-registry.ts";
14
+ export * from "./debug.ts";
15
15
  // biome-ignore lint/performance/noReExportAll: intentional convenience barrel
16
16
  export * from "./footer-registry.ts";
17
17
  // biome-ignore lint/performance/noReExportAll: intentional convenience barrel