@mrclrchtr/supi-code-intelligence 6.1.0 → 6.3.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.
Files changed (141) hide show
  1. package/README.md +3 -1
  2. package/node_modules/@mrclrchtr/supi-code-runtime/package.json +1 -1
  3. package/node_modules/@mrclrchtr/supi-core/package.json +1 -1
  4. package/node_modules/@mrclrchtr/supi-core/src/debug-identity.ts +11 -0
  5. package/node_modules/@mrclrchtr/supi-core/src/debug.ts +5 -0
  6. package/node_modules/@mrclrchtr/supi-lsp/README.md +23 -3
  7. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/package.json +1 -1
  8. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/package.json +1 -1
  9. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/debug-identity.ts +11 -0
  10. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/debug.ts +5 -0
  11. package/node_modules/@mrclrchtr/supi-lsp/node_modules/ignore/README.md +52 -0
  12. package/node_modules/@mrclrchtr/supi-lsp/node_modules/ignore/index.js +785 -129
  13. package/node_modules/@mrclrchtr/supi-lsp/node_modules/ignore/legacy.js +660 -98
  14. package/node_modules/@mrclrchtr/supi-lsp/node_modules/ignore/package.json +10 -4
  15. package/node_modules/@mrclrchtr/supi-lsp/package.json +4 -5
  16. package/node_modules/@mrclrchtr/supi-lsp/src/api.ts +16 -7
  17. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-cache.ts +1 -1
  18. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-capabilities.ts +0 -5
  19. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-publication.ts +6 -3
  20. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-refresh.ts +2 -1
  21. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-timing.ts +5 -2
  22. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostics.ts +2 -1
  23. package/node_modules/@mrclrchtr/supi-lsp/src/client/client.ts +17 -5
  24. package/node_modules/@mrclrchtr/supi-lsp/src/client/transport.ts +40 -18
  25. package/node_modules/@mrclrchtr/supi-lsp/src/config/config.ts +164 -30
  26. package/node_modules/@mrclrchtr/supi-lsp/src/config/lsp-settings.ts +9 -87
  27. package/node_modules/@mrclrchtr/supi-lsp/src/config/server-config.ts +0 -1
  28. package/node_modules/@mrclrchtr/supi-lsp/src/config/tsconfig-scope.ts +4 -5
  29. package/node_modules/@mrclrchtr/supi-lsp/src/config/types.ts +0 -29
  30. package/node_modules/@mrclrchtr/supi-lsp/src/debug-telemetry.ts +7 -21
  31. package/node_modules/@mrclrchtr/supi-lsp/src/diagnostics/diagnostic-severity.ts +6 -0
  32. package/node_modules/@mrclrchtr/supi-lsp/src/diagnostics/diagnostic-summary.ts +11 -14
  33. package/node_modules/@mrclrchtr/supi-lsp/src/diagnostics/evidence.ts +0 -5
  34. package/node_modules/@mrclrchtr/supi-lsp/src/diagnostics/stale-diagnostics.ts +0 -1
  35. package/node_modules/@mrclrchtr/supi-lsp/src/diagnostics/workspace-sentinels.ts +29 -49
  36. package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-client-state.ts +1 -1
  37. package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-diagnostics.ts +4 -8
  38. package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-helpers.ts +1 -9
  39. package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-process-crash-report.ts +106 -0
  40. package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-project-info.ts +6 -3
  41. package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-workspace-recovery.ts +9 -1
  42. package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-workspace-symbol.ts +26 -41
  43. package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager.ts +577 -152
  44. package/node_modules/@mrclrchtr/supi-lsp/src/provider/lsp-semantic-provider.ts +4 -1
  45. package/node_modules/@mrclrchtr/supi-lsp/src/provider/semantic-symbol-mapper.ts +59 -28
  46. package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-controller.ts +27 -19
  47. package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-diagnostics.ts +54 -0
  48. package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-registration.ts +0 -17
  49. package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-registry.ts +74 -19
  50. package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-transition-debug.ts +5 -2
  51. package/node_modules/@mrclrchtr/supi-lsp/src/session/scanner.ts +24 -21
  52. package/node_modules/@mrclrchtr/supi-lsp/src/session/workspace-lsp-runtime.ts +29 -3
  53. package/node_modules/@mrclrchtr/supi-lsp/src/summary.ts +19 -40
  54. package/node_modules/@mrclrchtr/supi-lsp/src/utils.ts +13 -19
  55. package/node_modules/@mrclrchtr/supi-lsp/src/workspace-path-policy.ts +296 -0
  56. package/node_modules/@mrclrchtr/supi-tree-sitter/README.md +6 -18
  57. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/package.json +1 -1
  58. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/package.json +1 -1
  59. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/debug-identity.ts +11 -0
  60. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/debug.ts +5 -0
  61. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/README.md +29 -1
  62. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/debug/web-tree-sitter.cjs +99 -19
  63. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/debug/web-tree-sitter.cjs.map +4 -4
  64. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/debug/web-tree-sitter.js +94 -18
  65. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/debug/web-tree-sitter.js.map +4 -4
  66. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/debug/web-tree-sitter.wasm +0 -0
  67. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/debug/web-tree-sitter.wasm.map +14 -14
  68. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/package.json +13 -8
  69. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/web-tree-sitter.cjs +97 -19
  70. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/web-tree-sitter.cjs.map +4 -4
  71. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/web-tree-sitter.d.cts +28 -8
  72. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/web-tree-sitter.d.cts.map +1 -1
  73. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/web-tree-sitter.d.ts +28 -8
  74. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/web-tree-sitter.d.ts.map +1 -1
  75. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/web-tree-sitter.js +92 -18
  76. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/web-tree-sitter.js.map +4 -4
  77. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/web-tree-sitter.wasm +0 -0
  78. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/web-tree-sitter.wasm.map +17 -17
  79. package/node_modules/@mrclrchtr/supi-tree-sitter/package.json +7 -6
  80. package/node_modules/@mrclrchtr/supi-tree-sitter/resources/grammars/kotlin/tree-sitter-kotlin.wasm +0 -0
  81. package/node_modules/@mrclrchtr/supi-tree-sitter/resources/grammars/kotlin/tree-sitter-kotlin.wasm.json +2 -2
  82. package/node_modules/@mrclrchtr/supi-tree-sitter/resources/grammars/sql/tree-sitter-sql.wasm +0 -0
  83. package/node_modules/@mrclrchtr/supi-tree-sitter/resources/grammars/sql/tree-sitter-sql.wasm.json +2 -2
  84. package/node_modules/@mrclrchtr/supi-tree-sitter/src/api.ts +1 -11
  85. package/node_modules/@mrclrchtr/supi-tree-sitter/src/coordinates.ts +19 -0
  86. package/node_modules/@mrclrchtr/supi-tree-sitter/src/language.ts +5 -23
  87. package/node_modules/@mrclrchtr/supi-tree-sitter/src/operation-support.ts +3 -21
  88. package/node_modules/@mrclrchtr/supi-tree-sitter/src/provider/tree-sitter-provider.ts +5 -9
  89. package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/runtime-controller.ts +10 -20
  90. package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/session.ts +7 -1
  91. package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/structural-worker-client.ts +9 -5
  92. package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/structural-worker-protocol.ts +134 -9
  93. package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/call-name.ts +96 -0
  94. package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/call-sites.ts +11 -12
  95. package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/callees.ts +39 -56
  96. package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/exports.ts +9 -7
  97. package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/js-binding-pattern.ts +26 -0
  98. package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/node-at.ts +2 -21
  99. package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/outline-html-sql.ts +144 -12
  100. package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/outline.ts +50 -37
  101. package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/scope.ts +138 -0
  102. package/node_modules/@mrclrchtr/supi-tree-sitter/src/types.ts +0 -7
  103. package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/parsed-file-store.ts +1 -14
  104. package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/runtime.ts +0 -10
  105. package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/service.ts +6 -8
  106. package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/worker-main.ts +34 -10
  107. package/package.json +5 -5
  108. package/src/analysis/capability/capability-warnings.ts +4 -22
  109. package/src/analysis/search/ast-scan-timing.ts +4 -2
  110. package/src/analysis/search/ast-scan.ts +1 -1
  111. package/src/api.ts +1 -0
  112. package/src/extension.ts +4 -2
  113. package/src/session/health-refresh.ts +26 -2
  114. package/src/session/health-types.ts +19 -0
  115. package/src/session/input/health-refactor.ts +16 -14
  116. package/src/session/refactor-types.ts +14 -1
  117. package/src/session/refactor-workflow.ts +21 -16
  118. package/src/substrate/lsp/lifecycle.ts +0 -3
  119. package/src/substrate/lsp/maintenance.ts +4 -5
  120. package/src/substrate/lsp/settings.ts +7 -9
  121. package/src/substrate/lsp/state.ts +0 -2
  122. package/src/substrate/workspace-provider-host.ts +2 -2
  123. package/src/tool/code_health/file-scope-markdown.ts +43 -0
  124. package/src/tool/code_health/markdown.ts +66 -66
  125. package/src/tool/code_health/refresh-outcome.ts +76 -0
  126. package/src/tool/code_health/refresh-status.ts +212 -23
  127. package/src/tool/code_health/result.ts +1 -0
  128. package/src/tool/code_health/tui.ts +18 -5
  129. package/src/tool/code_refactor_apply/guidance.ts +3 -2
  130. package/src/tool/code_refactor_plan/guidance.ts +1 -1
  131. package/src/tool/register.ts +5 -2
  132. package/src/tool/schemas.ts +21 -11
  133. package/node_modules/@mrclrchtr/supi-lsp/src/config/server-actions.ts +0 -59
  134. package/node_modules/@mrclrchtr/supi-lsp/src/pattern-matcher.ts +0 -24
  135. package/node_modules/@mrclrchtr/supi-tree-sitter/scripts/generate-kotlin-wasm.mjs +0 -132
  136. package/node_modules/@mrclrchtr/supi-tree-sitter/scripts/generate-sql-wasm.mjs +0 -158
  137. package/node_modules/@mrclrchtr/supi-tree-sitter/scripts/vendor-wasm.mjs +0 -167
  138. package/node_modules/@mrclrchtr/supi-tree-sitter/src/index.ts +0 -42
  139. package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/runtime-registration.ts +0 -37
  140. package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/service-registry.ts +0 -30
  141. package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/structure.ts +0 -8
@@ -3,6 +3,7 @@ import type {
3
3
  ActiveCoverageSummaryEntry,
4
4
  OutstandingDiagnosticSummaryEntry,
5
5
  } from "./manager/manager-types.ts";
6
+ import type { AutomaticLspPathPolicy } from "./workspace-path-policy.ts";
6
7
 
7
8
  /**
8
9
  * Display form for a file path used both for human-readable LSP output and as
@@ -67,9 +68,14 @@ export function normalizeRelevantPaths(relevantPaths: string[]): string[] {
67
68
  * - candidate has no "/" and no ".": treat as a directory name anywhere in the path
68
69
  * - otherwise: treat as a filename and match the basename
69
70
  */
70
- export function isPathRelevant(filePath: string, relevantPaths: string[], cwd: string): boolean {
71
+ export function isPathRelevant(
72
+ filePath: string,
73
+ relevantPaths: string[],
74
+ cwd: string,
75
+ policy: AutomaticLspPathPolicy,
76
+ ): boolean {
71
77
  const normalizedFilePath = normalizeRelevantPath(filePath);
72
- if (shouldIgnoreLspPath(normalizedFilePath, cwd)) return false;
78
+ if (shouldIgnoreLspPath(normalizedFilePath, cwd, policy)) return false;
73
79
 
74
80
  return relevantPaths.some((candidate) => {
75
81
  if (normalizedFilePath === candidate) return true;
@@ -86,48 +92,21 @@ export function isPathRelevant(filePath: string, relevantPaths: string[], cwd: s
86
92
 
87
93
  import { isFileExcludedByTsconfig } from "./config/tsconfig-scope.ts";
88
94
 
89
- /** Check whether a file path is inside the project tree (within cwd, not node_modules/.pnpm/out-of-tree).
90
- * Does NOT check tsconfig exclusion use `shouldIgnoreLspPath` for diagnostics/guidance filtering. */
91
- export function isInProjectTree(filePath: string, cwd: string): boolean {
92
- const normalized = normalizeRelevantPath(filePath);
93
- if (
94
- normalized === "node_modules" ||
95
- normalized.startsWith("node_modules/") ||
96
- normalized.includes("/node_modules/") ||
97
- normalized === ".pnpm" ||
98
- normalized.startsWith(".pnpm/") ||
99
- normalized.includes("/.pnpm/")
100
- ) {
101
- return false;
102
- }
103
-
104
- const absolutePath = path.resolve(cwd, filePath);
105
- const relativePath = path.relative(cwd, absolutePath);
106
- return !(relativePath.startsWith(`..${path.sep}`) || relativePath === "..");
95
+ /** Check whether the supplied automatic path policy allows a file path. */
96
+ export function isInProjectTree(filePath: string, policy: AutomaticLspPathPolicy): boolean {
97
+ return policy.isEligible(filePath, "file");
107
98
  }
108
99
 
109
- /** Check whether a file path is inside the current project (not ignored and within cwd). */
110
- export function isProjectSource(filePath: string, cwd: string): boolean {
111
- return isInProjectTree(filePath, cwd);
112
- }
113
-
114
- export function shouldIgnoreLspPath(filePath: string, cwd: string): boolean {
115
- if (!isInProjectTree(filePath, cwd)) return true;
100
+ /** Check automatic and tsconfig diagnostic scope for one file path. */
101
+ export function shouldIgnoreLspPath(
102
+ filePath: string,
103
+ cwd: string,
104
+ policy: AutomaticLspPathPolicy,
105
+ ): boolean {
106
+ if (!isInProjectTree(filePath, policy)) return true;
116
107
 
117
108
  const normalized = normalizeRelevantPath(filePath);
118
- if (
119
- normalized === "node_modules" ||
120
- normalized.startsWith("node_modules/") ||
121
- normalized.includes("/node_modules/") ||
122
- normalized === ".pnpm" ||
123
- normalized.startsWith(".pnpm/") ||
124
- normalized.includes("/.pnpm/") ||
125
- isFileExcludedByTsconfig(normalized, cwd)
126
- ) {
127
- return true;
128
- }
129
-
130
- return false;
109
+ return isFileExcludedByTsconfig(normalized, cwd);
131
110
  }
132
111
 
133
112
  function normalizeRelevantPath(filePath: string): string {
@@ -3,12 +3,6 @@
3
3
  import * as fs from "node:fs";
4
4
  import * as path from "node:path";
5
5
 
6
- export {
7
- fileToUri,
8
- resolveToolPath as resolveSessionPath,
9
- uriToFile,
10
- } from "@mrclrchtr/supi-core/path";
11
-
12
6
  // ── Language ID Detection ─────────────────────────────────────────────
13
7
 
14
8
  const EXT_TO_LANGUAGE: Record<string, string> = {
@@ -68,11 +62,6 @@ export function detectLanguageId(filePath: string): string {
68
62
  return EXT_TO_LANGUAGE[ext] ?? ext;
69
63
  }
70
64
 
71
- /** Get the file extension (without dot) from a file path. */
72
- export function getFileExtension(filePath: string): string {
73
- return path.extname(filePath).slice(1).toLowerCase();
74
- }
75
-
76
65
  // ── PATH Validation ───────────────────────────────────────────────────
77
66
 
78
67
  /**
@@ -80,9 +69,9 @@ export function getFileExtension(filePath: string): string {
80
69
  * Uses synchronous check to avoid complexity.
81
70
  */
82
71
  export function commandExists(command: string): boolean {
83
- // If it's an absolute path, check directly
72
+ // If it is an absolute path, validate the exact executable file.
84
73
  if (path.isAbsolute(command)) {
85
- return fs.existsSync(command);
74
+ return isExecutableFile(command);
86
75
  }
87
76
 
88
77
  const pathDirs = (process.env.PATH ?? "").split(path.delimiter);
@@ -92,13 +81,18 @@ export function commandExists(command: string): boolean {
92
81
  for (const dir of pathDirs) {
93
82
  for (const ext of extensions) {
94
83
  const fullPath = path.join(dir, command + ext);
95
- try {
96
- fs.accessSync(fullPath, fs.constants.X_OK);
97
- return true;
98
- } catch {
99
- // Not found here, continue
100
- }
84
+ if (isExecutableFile(fullPath)) return true;
101
85
  }
102
86
  }
103
87
  return false;
104
88
  }
89
+
90
+ function isExecutableFile(filePath: string): boolean {
91
+ try {
92
+ if (!fs.statSync(filePath).isFile()) return false;
93
+ fs.accessSync(filePath, fs.constants.X_OK);
94
+ return true;
95
+ } catch {
96
+ return false;
97
+ }
98
+ }
@@ -0,0 +1,296 @@
1
+ import { type Dirent, lstatSync, readdirSync, readFileSync, realpathSync, statSync } from "node:fs";
2
+ import * as path from "node:path";
3
+ import ignore from "ignore";
4
+ import { loadLspSettings } from "./config/lsp-settings.ts";
5
+
6
+ /** Directory names that automatic LSP work never enters or selects. */
7
+ export const AUTOMATIC_LSP_EXCLUDED_DIRECTORIES: readonly string[] = Object.freeze([
8
+ ".git",
9
+ ".cache",
10
+ ".pi",
11
+ ".pnpm",
12
+ "node_modules",
13
+ "dist",
14
+ "build",
15
+ "out",
16
+ "coverage",
17
+ ".next",
18
+ ".nuxt",
19
+ ".turbo",
20
+ "__pycache__",
21
+ ]);
22
+
23
+ const BUILT_IN_EXCLUSIONS = new Set(AUTOMATIC_LSP_EXCLUDED_DIRECTORIES);
24
+
25
+ /** Automatic path rules for one Workspace LSP runtime. The rules do not change. */
26
+ export interface AutomaticLspPathPolicy {
27
+ /** Canonical absolute workspace root used for all matching. */
28
+ readonly workspaceRoot: string;
29
+ /**
30
+ * True when automatic LSP work can use the path.
31
+ * The policy reads the path kind when the caller does not supply it.
32
+ */
33
+ isEligible(candidate: string, kind?: "file" | "directory"): boolean;
34
+ }
35
+
36
+ /** Create the automatic path rules for one Workspace LSP runtime. */
37
+ export function createAutomaticLspPathPolicy(
38
+ workspaceRoot: string,
39
+ excludePatterns: readonly string[],
40
+ ): AutomaticLspPathPolicy {
41
+ const canonicalRoot = canonicalPath(workspaceRoot);
42
+ const configuredExclusions = createIgnoreMatcher([...excludePatterns]);
43
+ const repositoryRules = compileRepositoryRules(canonicalRoot, configuredExclusions);
44
+ return Object.freeze({
45
+ workspaceRoot: canonicalRoot,
46
+ isEligible(candidate: string, kind?: "file" | "directory"): boolean {
47
+ const relativePath = normalizeCandidate(canonicalRoot, workspaceRoot, candidate);
48
+ if (relativePath === null) return false;
49
+ const candidateKind = kind ?? readCandidateKind(canonicalRoot, relativePath);
50
+ if (!isAutomaticCandidateAllowed(workspaceRoot, relativePath, candidateKind)) return false;
51
+ return isEligibleRelativePath(
52
+ relativePath,
53
+ candidateKind === "directory",
54
+ configuredExclusions,
55
+ repositoryRules,
56
+ );
57
+ },
58
+ });
59
+ }
60
+
61
+ /**
62
+ * Create path rules for an automatic LSP helper used outside a runtime.
63
+ * Runtime-owned operations should pass the rules created at startup instead.
64
+ */
65
+ export function createDefaultAutomaticLspPathPolicy(workspaceRoot: string): AutomaticLspPathPolicy {
66
+ return createAutomaticLspPathPolicy(workspaceRoot, loadLspSettings(workspaceRoot).exclude);
67
+ }
68
+
69
+ /**
70
+ * Walk paths allowed by the policy without visiting symbolic-link directories.
71
+ *
72
+ * The visitor receives only entries that the automatic LSP policy allows.
73
+ * Return `true` from the visitor to stop the walk after that directory.
74
+ */
75
+ export function walkAutomaticLspTree(
76
+ policy: AutomaticLspPathPolicy,
77
+ directory: string,
78
+ depth: number,
79
+ onDirectory: (directory: string, entries: readonly Dirent[]) => boolean | undefined,
80
+ ): void {
81
+ if (!policy.isEligible(directory, "directory")) return;
82
+ walkDirectoryTree({
83
+ directory,
84
+ depth,
85
+ includeEntry: (candidate, entry) =>
86
+ policy.isEligible(candidate, entry.isDirectory() ? "directory" : "file"),
87
+ onDirectory,
88
+ });
89
+ }
90
+
91
+ interface DirectoryWalkOptions {
92
+ directory: string;
93
+ depth: number;
94
+ includeEntry: (candidate: string, entry: Dirent) => boolean;
95
+ onDirectory: (directory: string, entries: readonly Dirent[]) => boolean | undefined;
96
+ beforeFilter?: (directory: string) => void;
97
+ }
98
+
99
+ function walkDirectoryTree(options: DirectoryWalkOptions): boolean {
100
+ const { directory, depth, includeEntry, onDirectory, beforeFilter } = options;
101
+ beforeFilter?.(directory);
102
+ let entries: Dirent[];
103
+ try {
104
+ entries = [...readdirSync(directory, { withFileTypes: true })].sort((a, b) =>
105
+ a.name.localeCompare(b.name),
106
+ );
107
+ } catch {
108
+ return false;
109
+ }
110
+
111
+ const eligibleEntries = entries.filter((entry) =>
112
+ includeEntry(path.join(directory, entry.name), entry),
113
+ );
114
+ if (onDirectory(directory, eligibleEntries) === true) return true;
115
+ if (depth <= 0) return false;
116
+
117
+ for (const entry of eligibleEntries) {
118
+ if (!entry.isDirectory()) continue;
119
+ if (
120
+ walkDirectoryTree({
121
+ ...options,
122
+ directory: path.join(directory, entry.name),
123
+ depth: depth - 1,
124
+ })
125
+ ) {
126
+ return true;
127
+ }
128
+ }
129
+ return false;
130
+ }
131
+
132
+ type IgnoreMatcher = ReturnType<typeof ignore>;
133
+
134
+ function createIgnoreMatcher(patterns: string | readonly string[]): IgnoreMatcher {
135
+ return ignore({ ignorecase: false }).add(patterns);
136
+ }
137
+
138
+ interface RepositoryIgnoreRules {
139
+ readonly base: string;
140
+ readonly matcher: IgnoreMatcher;
141
+ }
142
+
143
+ function compileRepositoryRules(
144
+ workspaceRoot: string,
145
+ configuredExclusions: IgnoreMatcher,
146
+ ): readonly RepositoryIgnoreRules[] {
147
+ const rules: RepositoryIgnoreRules[] = [];
148
+
149
+ walkDirectoryTree({
150
+ directory: workspaceRoot,
151
+ depth: Number.MAX_SAFE_INTEGER,
152
+ includeEntry: (candidate, entry) => {
153
+ if (entry.isSymbolicLink() || !entry.isDirectory()) return false;
154
+ const relativePath = path.relative(workspaceRoot, candidate).split(path.sep).join("/");
155
+ return isEligibleRelativePath(relativePath, true, configuredExclusions, rules);
156
+ },
157
+ onDirectory: () => undefined,
158
+ beforeFilter: (directory) => {
159
+ const relativeDirectory = path.relative(workspaceRoot, directory).split(path.sep).join("/");
160
+ const nestedRules = readRepositoryIgnore(directory, relativeDirectory);
161
+ if (nestedRules) rules.push(nestedRules);
162
+ },
163
+ });
164
+
165
+ return Object.freeze([...rules]);
166
+ }
167
+
168
+ function readRepositoryIgnore(
169
+ directory: string,
170
+ relativeDirectory: string,
171
+ ): RepositoryIgnoreRules | null {
172
+ try {
173
+ const matcher = createIgnoreMatcher(readFileSync(path.join(directory, ".gitignore"), "utf-8"));
174
+ return Object.freeze({ base: relativeDirectory, matcher });
175
+ } catch {
176
+ return null;
177
+ }
178
+ }
179
+
180
+ function isEligibleRelativePath(
181
+ relativePath: string,
182
+ directory: boolean,
183
+ configuredExclusions: IgnoreMatcher,
184
+ repositoryRules: readonly RepositoryIgnoreRules[],
185
+ ): boolean {
186
+ const segments = relativePath.split("/");
187
+ if (
188
+ segments.some(
189
+ (segment, index) =>
190
+ BUILT_IN_EXCLUSIONS.has(segment) && (directory || index < segments.length - 1),
191
+ )
192
+ ) {
193
+ return false;
194
+ }
195
+ if (relativePath === "") return true;
196
+
197
+ const matchPath = directory ? `${relativePath}/` : relativePath;
198
+ if (configuredExclusions.ignores(matchPath)) return false;
199
+ return !isRepositoryIgnored(relativePath, directory, repositoryRules);
200
+ }
201
+
202
+ function isRepositoryIgnored(
203
+ relativePath: string,
204
+ directory: boolean,
205
+ repositoryRules: readonly RepositoryIgnoreRules[],
206
+ ): boolean {
207
+ let ignored = false;
208
+ for (const rules of repositoryRules) {
209
+ const localPath = relativeToRuleBase(rules.base, relativePath);
210
+ if (localPath === null || localPath === "") continue;
211
+ const result = rules.matcher.test(directory ? `${localPath}/` : localPath);
212
+ if (result.ignored) ignored = true;
213
+ else if (result.unignored) ignored = false;
214
+ }
215
+ return ignored;
216
+ }
217
+
218
+ function relativeToRuleBase(base: string, relativePath: string): string | null {
219
+ if (base === "") return relativePath;
220
+ if (relativePath === base) return "";
221
+ return relativePath.startsWith(`${base}/`) ? relativePath.slice(base.length + 1) : null;
222
+ }
223
+
224
+ function readCandidateKind(canonicalRoot: string, relativePath: string): "file" | "directory" {
225
+ try {
226
+ return statSync(path.resolve(canonicalRoot, relativePath)).isDirectory() ? "directory" : "file";
227
+ } catch {
228
+ return "file";
229
+ }
230
+ }
231
+
232
+ function isAutomaticCandidateAllowed(
233
+ suppliedRoot: string,
234
+ relativePath: string,
235
+ kind: "file" | "directory",
236
+ ): boolean {
237
+ if (relativePath === "") return true;
238
+ let current = path.resolve(suppliedRoot);
239
+ const segments = relativePath.split("/");
240
+
241
+ for (const [index, segment] of segments.entries()) {
242
+ current = path.join(current, segment);
243
+ let link: ReturnType<typeof lstatSync>;
244
+ try {
245
+ link = lstatSync(current);
246
+ } catch {
247
+ return true;
248
+ }
249
+ if (!link.isSymbolicLink()) continue;
250
+ if (index < segments.length - 1) return false;
251
+
252
+ try {
253
+ const target = statSync(current);
254
+ return kind === "file" && target.isFile();
255
+ } catch {
256
+ return false;
257
+ }
258
+ }
259
+
260
+ return true;
261
+ }
262
+
263
+ function canonicalPath(candidate: string): string {
264
+ try {
265
+ return realpathSync(candidate);
266
+ } catch {
267
+ return path.resolve(candidate);
268
+ }
269
+ }
270
+
271
+ function normalizeCandidate(
272
+ canonicalRoot: string,
273
+ suppliedRoot: string,
274
+ candidate: string,
275
+ ): string | null {
276
+ const absoluteCandidate = path.isAbsolute(candidate)
277
+ ? mapSuppliedRootToCanonical(canonicalRoot, suppliedRoot, candidate)
278
+ : path.resolve(canonicalRoot, candidate);
279
+ const relativePath = path.relative(canonicalRoot, absoluteCandidate);
280
+ if (relativePath === "") return "";
281
+ if (relativePath === ".." || relativePath.startsWith(`..${path.sep}`)) return null;
282
+ return relativePath.split(path.sep).join("/");
283
+ }
284
+
285
+ function mapSuppliedRootToCanonical(
286
+ canonicalRoot: string,
287
+ suppliedRoot: string,
288
+ candidate: string,
289
+ ): string {
290
+ const resolvedSuppliedRoot = path.resolve(suppliedRoot);
291
+ const relativePath = path.relative(resolvedSuppliedRoot, path.resolve(candidate));
292
+ if (relativePath === "" || (relativePath !== ".." && !relativePath.startsWith(`..${path.sep}`))) {
293
+ return path.resolve(canonicalRoot, relativePath);
294
+ }
295
+ return path.resolve(candidate);
296
+ }
@@ -25,7 +25,6 @@ npm install @mrclrchtr/supi-tree-sitter
25
25
 
26
26
  This package provides the parser-backed structural substrate consumed by `@mrclrchtr/supi-code-intelligence`:
27
27
 
28
- - a shared session-scoped Tree-sitter service for structural analysis
29
28
  - an owned parsing session API for direct library consumers
30
29
  - a `StructuralProvider` adapter published through `./provider/tree-sitter-provider`
31
30
  - a long-lived owned Structural Worker that keeps Pi responsive during parser-backed work
@@ -59,18 +58,20 @@ Outline collection supports every listed family. HTML outlines contain elements
59
58
  `@mrclrchtr/supi-tree-sitter` is the **structural substrate** in SuPi's
60
59
  code-understanding stack. It depends on `@mrclrchtr/supi-core` and
61
60
  `@mrclrchtr/supi-code-runtime` for shared contracts, and provides structural
62
- analysis via a session-scoped Tree-sitter service that publishes its
63
- capabilities into the shared workspace runtime.
61
+ analysis via a lifecycle controller that publishes its capabilities into
62
+ the shared workspace runtime.
64
63
 
65
64
  ```text
66
65
  supi-code-runtime ← shared contracts + workspace runtime
67
66
 
68
- supi-tree-sitter ← one owned Structural Worker + session-scoped service + runtime capabilities
67
+ supi-tree-sitter ← one owned Structural Worker + runtime capabilities
69
68
  ```
70
69
 
71
70
  ## Package surfaces
72
71
 
73
- - `@mrclrchtr/supi-tree-sitter/api` reusable parsing session factory, shared session-scoped structural service access, and shared result types
72
+ There is no package-root export. Use one of these explicit subpaths:
73
+
74
+ - `@mrclrchtr/supi-tree-sitter/api` — reusable parsing session factory, lifecycle controller, language helpers, and shared result types
74
75
  - `@mrclrchtr/supi-tree-sitter/provider/tree-sitter-provider` — shared StructuralProvider adapter
75
76
 
76
77
  This is a **library-only** package. Public tool registration and pi event handlers belong to `@mrclrchtr/supi-code-intelligence`.
@@ -89,17 +90,6 @@ const callees = await session.calleesAt("src/index.ts", 42, 10);
89
90
  await session.dispose();
90
91
  ```
91
92
 
92
- Shared session-scoped service example:
93
-
94
- ```ts
95
- import { getSessionTreeSitterService } from "@mrclrchtr/supi-tree-sitter/api";
96
-
97
- const state = getSessionTreeSitterService("/project");
98
- if (state.kind === "ready") {
99
- const outline = await state.service.outline("src/index.ts");
100
- }
101
- ```
102
-
103
93
  ## Structural performance baseline
104
94
 
105
95
  Run the stable outline fixture on the same machine and dependency versions:
@@ -117,7 +107,6 @@ All structural service operations accept optional `CodeRequestControl`. The pare
117
107
  ## Source
118
108
 
119
109
  - `src/api.ts` — public library entrypoint
120
- - `src/index.ts` — re-export surface
121
110
  - `src/worker/bootstrap.mjs` — package-owned Worker bootstrap and direct `jiti` loader
122
111
  - `src/worker/runtime.ts` — Worker-only parser and query runtime
123
112
  - `src/worker/parsed-file-store.ts` — Worker-only parsed-file and compiled-query reuse
@@ -125,6 +114,5 @@ All structural service operations accept optional `CodeRequestControl`. The pare
125
114
  - `src/session/runtime-controller.ts` — generation-fenced shared Worker lifecycle
126
115
  - `src/session/session.ts` — asynchronous Worker-proxy service and owned session API
127
116
  - `src/operation-support.ts` — authoritative operation-specific extension support
128
- - `src/session/service-registry.ts` — shared session-scoped structural service registry
129
117
  - `src/provider/tree-sitter-provider.ts` — `StructuralProvider` adapter consumed by `@mrclrchtr/supi-code-intelligence`
130
118
  - `src/tool/outline.ts`, `src/tool/outline-*.ts`, `src/tool/imports.ts`, `src/tool/exports.ts`, `src/tool/node-at.ts`, `src/tool/callees.ts`, `src/tool/call-sites.ts` — Worker-internal structural analyses
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mrclrchtr/supi-code-runtime",
3
- "version": "6.1.0",
3
+ "version": "6.3.0",
4
4
  "description": "Shared workspace context and capability contracts for code intelligence",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mrclrchtr/supi-core",
3
- "version": "6.1.0",
3
+ "version": "6.3.0",
4
4
  "description": "Shared settings, configuration, reporting, and session infrastructure",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -0,0 +1,11 @@
1
+ /** Maximum UTF-16 code-unit length of one debug identity string. */
2
+ export const MAX_DEBUG_IDENTITY_STRING = 512;
3
+
4
+ /** Marker appended to a truncated debug identity string. */
5
+ export const DEBUG_IDENTITY_TRUNCATION_MARKER = "…";
6
+
7
+ /** Bound one debug identity string, including its truncation marker. */
8
+ export function truncateDebugIdentity(value: string): string {
9
+ if (value.length <= MAX_DEBUG_IDENTITY_STRING) return value;
10
+ return `${value.slice(0, MAX_DEBUG_IDENTITY_STRING - 1)}${DEBUG_IDENTITY_TRUNCATION_MARKER}`;
11
+ }
@@ -3,6 +3,11 @@
3
3
  // Kept separate from debug-registry.ts so debug-timing.ts can import the
4
4
  // registry without creating an import cycle through the barrel re-export.
5
5
 
6
+ export {
7
+ DEBUG_IDENTITY_TRUNCATION_MARKER,
8
+ MAX_DEBUG_IDENTITY_STRING,
9
+ truncateDebugIdentity,
10
+ } from "./debug-identity.ts";
6
11
  // biome-ignore lint/performance/noReExportAll: preserve the stable debug domain entry point
7
12
  export * from "./debug-registry.ts";
8
13
  // biome-ignore lint/performance/noReExportAll: preserve the stable debug domain entry point
@@ -174,7 +174,9 @@ For example, you can download the JavaScript `.wasm` file from the tree-sitter-j
174
174
  You can also generate the `.wasm` file for your desired grammar. Shown below is an example of how to generate the `.wasm`
175
175
  file for the JavaScript grammar.
176
176
 
177
- **IMPORTANT**: [Emscripten][emscripten], [Docker][docker], or [Podman][podman] need to be installed.
177
+ > [!NOTE]
178
+ > Since v0.26.1, `tree-sitter build --wasm` uses [wasi-sdk][] and will automatically download it on first use.
179
+ > No additional tools need to be installed.
178
180
 
179
181
  First install `tree-sitter-cli`, and the tree-sitter language for which to generate `.wasm`
180
182
  (`tree-sitter-javascript` in this example):
@@ -191,6 +193,19 @@ npx tree-sitter build --wasm node_modules/tree-sitter-javascript
191
193
 
192
194
  If everything is fine, file `tree-sitter-javascript.wasm` should be generated in current directory.
193
195
 
196
+ ### Wasm compatibility
197
+
198
+ `web-tree-sitter` supports the same parser ABI versions as the corresponding tree-sitter library:
199
+
200
+ | web-tree-sitter version | Min parser ABI version | Max parser ABI version |
201
+ |-------------------------|------------------------|------------------------|
202
+ | 0.24.x | 13 | 14 |
203
+ | >= 0.25.0 | 13 | 15 |
204
+
205
+ > [!WARNING]
206
+ > Some prebuilt `.wasm` files use an older dynamic-linking format that newer versions of `web-tree-sitter` cannot
207
+ > load, even if their parser ABI is supported. Rebuild these files using a current tree-sitter CLI.
208
+
194
209
  ### Running .wasm in Node.js
195
210
 
196
211
  Notice that executing `.wasm` files in Node.js is considerably slower than running [Node.js bindings][node bindings].
@@ -209,6 +224,18 @@ const Parser = require('web-tree-sitter');
209
224
  })();
210
225
  ```
211
226
 
227
+ ### Loading a pre-compiled WebAssembly module
228
+
229
+ Some environments, such as Cloudflare Workers and Vercel Edge Functions, import
230
+ `.wasm` files as `WebAssembly.Module` objects. You can pass those modules to `Language.loadSync`:
231
+
232
+ ```javascript
233
+ import treeSitterJavaScript from 'tree-sitter-javascript.wasm';
234
+ // treeSitterJavaScript is of type `WebAssembly.Module`
235
+ const JavaScript = Language.loadSync(treeSitterJavaScript);
236
+ parser.setLanguage(JavaScript);
237
+ ```
238
+
212
239
  ### Running .wasm in browser
213
240
 
214
241
  `web-tree-sitter` can run in the browser, but there are some common pitfalls.
@@ -263,3 +290,4 @@ following to your webpack config:
263
290
  [node bindings]: https://github.com/tree-sitter/node-tree-sitter
264
291
  [npm module]: https://www.npmjs.com/package/web-tree-sitter
265
292
  [podman]: https://podman.io
293
+ [wasi-sdk]: https://github.com/WebAssembly/wasi-sdk