@mrclrchtr/supi-code-intelligence 6.1.0 → 6.2.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/README.md +1 -1
- package/node_modules/@mrclrchtr/supi-code-runtime/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-core/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-core/src/debug-identity.ts +11 -0
- package/node_modules/@mrclrchtr/supi-core/src/debug.ts +5 -0
- package/node_modules/@mrclrchtr/supi-lsp/README.md +21 -3
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/debug-identity.ts +11 -0
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/debug.ts +5 -0
- package/node_modules/@mrclrchtr/supi-lsp/package.json +4 -5
- package/node_modules/@mrclrchtr/supi-lsp/src/api.ts +12 -7
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-cache.ts +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-capabilities.ts +0 -5
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-publication.ts +6 -3
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-refresh.ts +2 -1
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-timing.ts +5 -2
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostics.ts +2 -1
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client.ts +17 -5
- package/node_modules/@mrclrchtr/supi-lsp/src/client/transport.ts +40 -18
- package/node_modules/@mrclrchtr/supi-lsp/src/config/config.ts +164 -30
- package/node_modules/@mrclrchtr/supi-lsp/src/config/lsp-settings.ts +9 -87
- package/node_modules/@mrclrchtr/supi-lsp/src/config/server-config.ts +0 -1
- package/node_modules/@mrclrchtr/supi-lsp/src/config/tsconfig-scope.ts +4 -5
- package/node_modules/@mrclrchtr/supi-lsp/src/config/types.ts +0 -29
- package/node_modules/@mrclrchtr/supi-lsp/src/debug-telemetry.ts +7 -21
- package/node_modules/@mrclrchtr/supi-lsp/src/diagnostics/diagnostic-severity.ts +6 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/diagnostics/diagnostic-summary.ts +11 -14
- package/node_modules/@mrclrchtr/supi-lsp/src/diagnostics/evidence.ts +0 -5
- package/node_modules/@mrclrchtr/supi-lsp/src/diagnostics/stale-diagnostics.ts +0 -1
- package/node_modules/@mrclrchtr/supi-lsp/src/diagnostics/workspace-sentinels.ts +29 -49
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-client-state.ts +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-diagnostics.ts +4 -8
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-helpers.ts +1 -9
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-project-info.ts +6 -3
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-workspace-recovery.ts +9 -1
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-workspace-symbol.ts +26 -41
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager.ts +275 -99
- package/node_modules/@mrclrchtr/supi-lsp/src/provider/lsp-semantic-provider.ts +4 -1
- package/node_modules/@mrclrchtr/supi-lsp/src/provider/semantic-symbol-mapper.ts +59 -28
- package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-controller.ts +27 -19
- package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-diagnostics.ts +26 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-registration.ts +0 -17
- package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-registry.ts +83 -19
- package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-transition-debug.ts +5 -2
- package/node_modules/@mrclrchtr/supi-lsp/src/session/scanner.ts +24 -21
- package/node_modules/@mrclrchtr/supi-lsp/src/session/workspace-lsp-runtime.ts +29 -3
- package/node_modules/@mrclrchtr/supi-lsp/src/summary.ts +19 -40
- package/node_modules/@mrclrchtr/supi-lsp/src/utils.ts +13 -19
- package/node_modules/@mrclrchtr/supi-lsp/src/workspace-path-policy.ts +287 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/README.md +6 -18
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/debug-identity.ts +11 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/debug.ts +5 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/package.json +3 -5
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/api.ts +1 -11
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/coordinates.ts +19 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/language.ts +5 -23
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/operation-support.ts +3 -21
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/provider/tree-sitter-provider.ts +5 -9
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/runtime-controller.ts +10 -20
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/session.ts +7 -1
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/structural-worker-client.ts +9 -5
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/structural-worker-protocol.ts +134 -9
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/call-name.ts +96 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/call-sites.ts +11 -12
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/callees.ts +39 -56
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/exports.ts +9 -7
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/js-binding-pattern.ts +26 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/node-at.ts +2 -21
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/outline-html-sql.ts +144 -12
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/outline.ts +50 -37
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/scope.ts +138 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/types.ts +0 -7
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/parsed-file-store.ts +1 -14
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/runtime.ts +0 -10
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/service.ts +6 -8
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/worker-main.ts +34 -10
- package/package.json +5 -5
- package/src/analysis/capability/capability-warnings.ts +4 -22
- package/src/analysis/search/ast-scan-timing.ts +4 -2
- package/src/analysis/search/ast-scan.ts +1 -1
- package/src/api.ts +1 -0
- package/src/extension.ts +4 -2
- package/src/session/health-refresh.ts +24 -2
- package/src/session/health-types.ts +19 -0
- package/src/session/input/health-refactor.ts +16 -14
- package/src/session/refactor-types.ts +14 -1
- package/src/session/refactor-workflow.ts +21 -16
- package/src/substrate/lsp/lifecycle.ts +0 -3
- package/src/substrate/lsp/maintenance.ts +4 -5
- package/src/substrate/lsp/settings.ts +6 -9
- package/src/substrate/lsp/state.ts +0 -2
- package/src/substrate/workspace-provider-host.ts +2 -2
- package/src/tool/code_health/file-scope-markdown.ts +43 -0
- package/src/tool/code_health/markdown.ts +66 -66
- package/src/tool/code_health/refresh-outcome.ts +46 -0
- package/src/tool/code_health/refresh-status.ts +166 -23
- package/src/tool/code_health/result.ts +1 -0
- package/src/tool/code_health/tui.ts +18 -5
- package/src/tool/code_refactor_apply/guidance.ts +3 -2
- package/src/tool/code_refactor_plan/guidance.ts +1 -1
- package/src/tool/register.ts +5 -2
- package/src/tool/schemas.ts +21 -11
- package/node_modules/@mrclrchtr/supi-lsp/src/config/server-actions.ts +0 -59
- package/node_modules/@mrclrchtr/supi-lsp/src/pattern-matcher.ts +0 -24
- package/node_modules/@mrclrchtr/supi-tree-sitter/scripts/generate-kotlin-wasm.mjs +0 -132
- package/node_modules/@mrclrchtr/supi-tree-sitter/scripts/generate-sql-wasm.mjs +0 -158
- package/node_modules/@mrclrchtr/supi-tree-sitter/scripts/vendor-wasm.mjs +0 -167
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/index.ts +0 -42
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/runtime-registration.ts +0 -37
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/service-registry.ts +0 -30
- 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(
|
|
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
|
|
90
|
-
|
|
91
|
-
|
|
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);
|
|
107
98
|
}
|
|
108
99
|
|
|
109
|
-
/** Check
|
|
110
|
-
export function
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
if (!isInProjectTree(filePath,
|
|
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
|
-
|
|
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
|
|
72
|
+
// If it is an absolute path, validate the exact executable file.
|
|
84
73
|
if (path.isAbsolute(command)) {
|
|
85
|
-
return
|
|
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
|
-
|
|
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,287 @@
|
|
|
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
|
+
/** True when automatic LSP work can use the path. */
|
|
30
|
+
isEligible(candidate: string, kind?: "file" | "directory"): boolean;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Create the automatic path rules for one Workspace LSP runtime. */
|
|
34
|
+
export function createAutomaticLspPathPolicy(
|
|
35
|
+
workspaceRoot: string,
|
|
36
|
+
excludePatterns: readonly string[],
|
|
37
|
+
): AutomaticLspPathPolicy {
|
|
38
|
+
const canonicalRoot = canonicalPath(workspaceRoot);
|
|
39
|
+
const configuredExclusions = createIgnoreMatcher([...excludePatterns]);
|
|
40
|
+
const repositoryRules = compileRepositoryRules(canonicalRoot, configuredExclusions);
|
|
41
|
+
return Object.freeze({
|
|
42
|
+
workspaceRoot: canonicalRoot,
|
|
43
|
+
isEligible(candidate: string, kind: "file" | "directory" = "file"): boolean {
|
|
44
|
+
const relativePath = normalizeCandidate(canonicalRoot, workspaceRoot, candidate);
|
|
45
|
+
if (
|
|
46
|
+
relativePath === null ||
|
|
47
|
+
!isAutomaticCandidateAllowed(workspaceRoot, relativePath, kind)
|
|
48
|
+
) {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
return isEligibleRelativePath(
|
|
52
|
+
relativePath,
|
|
53
|
+
kind === "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
|
+
walkDirectoryTree({
|
|
82
|
+
directory,
|
|
83
|
+
depth,
|
|
84
|
+
includeEntry: (candidate, entry) =>
|
|
85
|
+
policy.isEligible(candidate, entry.isDirectory() ? "directory" : "file"),
|
|
86
|
+
onDirectory,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
interface DirectoryWalkOptions {
|
|
91
|
+
directory: string;
|
|
92
|
+
depth: number;
|
|
93
|
+
includeEntry: (candidate: string, entry: Dirent) => boolean;
|
|
94
|
+
onDirectory: (directory: string, entries: readonly Dirent[]) => boolean | undefined;
|
|
95
|
+
beforeFilter?: (directory: string) => void;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function walkDirectoryTree(options: DirectoryWalkOptions): boolean {
|
|
99
|
+
const { directory, depth, includeEntry, onDirectory, beforeFilter } = options;
|
|
100
|
+
beforeFilter?.(directory);
|
|
101
|
+
let entries: Dirent[];
|
|
102
|
+
try {
|
|
103
|
+
entries = [...readdirSync(directory, { withFileTypes: true })].sort((a, b) =>
|
|
104
|
+
a.name.localeCompare(b.name),
|
|
105
|
+
);
|
|
106
|
+
} catch {
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const eligibleEntries = entries.filter((entry) =>
|
|
111
|
+
includeEntry(path.join(directory, entry.name), entry),
|
|
112
|
+
);
|
|
113
|
+
if (onDirectory(directory, eligibleEntries) === true) return true;
|
|
114
|
+
if (depth <= 0) return false;
|
|
115
|
+
|
|
116
|
+
for (const entry of eligibleEntries) {
|
|
117
|
+
if (!entry.isDirectory()) continue;
|
|
118
|
+
if (
|
|
119
|
+
walkDirectoryTree({
|
|
120
|
+
...options,
|
|
121
|
+
directory: path.join(directory, entry.name),
|
|
122
|
+
depth: depth - 1,
|
|
123
|
+
})
|
|
124
|
+
) {
|
|
125
|
+
return true;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
type IgnoreMatcher = ReturnType<typeof ignore>;
|
|
132
|
+
|
|
133
|
+
function createIgnoreMatcher(patterns: string | readonly string[]): IgnoreMatcher {
|
|
134
|
+
return ignore({ ignorecase: false }).add(patterns);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
interface RepositoryIgnoreRules {
|
|
138
|
+
readonly base: string;
|
|
139
|
+
readonly matcher: IgnoreMatcher;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function compileRepositoryRules(
|
|
143
|
+
workspaceRoot: string,
|
|
144
|
+
configuredExclusions: IgnoreMatcher,
|
|
145
|
+
): readonly RepositoryIgnoreRules[] {
|
|
146
|
+
const rules: RepositoryIgnoreRules[] = [];
|
|
147
|
+
|
|
148
|
+
walkDirectoryTree({
|
|
149
|
+
directory: workspaceRoot,
|
|
150
|
+
depth: Number.MAX_SAFE_INTEGER,
|
|
151
|
+
includeEntry: (candidate, entry) => {
|
|
152
|
+
if (entry.isSymbolicLink() || !entry.isDirectory()) return false;
|
|
153
|
+
const relativePath = path.relative(workspaceRoot, candidate).split(path.sep).join("/");
|
|
154
|
+
return isEligibleRelativePath(relativePath, true, configuredExclusions, rules);
|
|
155
|
+
},
|
|
156
|
+
onDirectory: () => undefined,
|
|
157
|
+
beforeFilter: (directory) => {
|
|
158
|
+
const relativeDirectory = path.relative(workspaceRoot, directory).split(path.sep).join("/");
|
|
159
|
+
const nestedRules = readRepositoryIgnore(directory, relativeDirectory);
|
|
160
|
+
if (nestedRules) rules.push(nestedRules);
|
|
161
|
+
},
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
return Object.freeze([...rules]);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function readRepositoryIgnore(
|
|
168
|
+
directory: string,
|
|
169
|
+
relativeDirectory: string,
|
|
170
|
+
): RepositoryIgnoreRules | null {
|
|
171
|
+
try {
|
|
172
|
+
const matcher = createIgnoreMatcher(readFileSync(path.join(directory, ".gitignore"), "utf-8"));
|
|
173
|
+
return Object.freeze({ base: relativeDirectory, matcher });
|
|
174
|
+
} catch {
|
|
175
|
+
return null;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function isEligibleRelativePath(
|
|
180
|
+
relativePath: string,
|
|
181
|
+
directory: boolean,
|
|
182
|
+
configuredExclusions: IgnoreMatcher,
|
|
183
|
+
repositoryRules: readonly RepositoryIgnoreRules[],
|
|
184
|
+
): boolean {
|
|
185
|
+
const segments = relativePath.split("/");
|
|
186
|
+
if (
|
|
187
|
+
segments.some(
|
|
188
|
+
(segment, index) =>
|
|
189
|
+
BUILT_IN_EXCLUSIONS.has(segment) && (directory || index < segments.length - 1),
|
|
190
|
+
)
|
|
191
|
+
) {
|
|
192
|
+
return false;
|
|
193
|
+
}
|
|
194
|
+
if (relativePath === "") return true;
|
|
195
|
+
|
|
196
|
+
const matchPath = directory ? `${relativePath}/` : relativePath;
|
|
197
|
+
if (configuredExclusions.ignores(matchPath)) return false;
|
|
198
|
+
return !isRepositoryIgnored(relativePath, directory, repositoryRules);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function isRepositoryIgnored(
|
|
202
|
+
relativePath: string,
|
|
203
|
+
directory: boolean,
|
|
204
|
+
repositoryRules: readonly RepositoryIgnoreRules[],
|
|
205
|
+
): boolean {
|
|
206
|
+
let ignored = false;
|
|
207
|
+
for (const rules of repositoryRules) {
|
|
208
|
+
const localPath = relativeToRuleBase(rules.base, relativePath);
|
|
209
|
+
if (localPath === null || localPath === "") continue;
|
|
210
|
+
const result = rules.matcher.test(directory ? `${localPath}/` : localPath);
|
|
211
|
+
if (result.ignored) ignored = true;
|
|
212
|
+
else if (result.unignored) ignored = false;
|
|
213
|
+
}
|
|
214
|
+
return ignored;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function relativeToRuleBase(base: string, relativePath: string): string | null {
|
|
218
|
+
if (base === "") return relativePath;
|
|
219
|
+
if (relativePath === base) return "";
|
|
220
|
+
return relativePath.startsWith(`${base}/`) ? relativePath.slice(base.length + 1) : null;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function isAutomaticCandidateAllowed(
|
|
224
|
+
suppliedRoot: string,
|
|
225
|
+
relativePath: string,
|
|
226
|
+
kind: "file" | "directory",
|
|
227
|
+
): boolean {
|
|
228
|
+
if (relativePath === "") return true;
|
|
229
|
+
let current = path.resolve(suppliedRoot);
|
|
230
|
+
const segments = relativePath.split("/");
|
|
231
|
+
|
|
232
|
+
for (const [index, segment] of segments.entries()) {
|
|
233
|
+
current = path.join(current, segment);
|
|
234
|
+
let link: ReturnType<typeof lstatSync>;
|
|
235
|
+
try {
|
|
236
|
+
link = lstatSync(current);
|
|
237
|
+
} catch {
|
|
238
|
+
return true;
|
|
239
|
+
}
|
|
240
|
+
if (!link.isSymbolicLink()) continue;
|
|
241
|
+
if (index < segments.length - 1) return false;
|
|
242
|
+
|
|
243
|
+
try {
|
|
244
|
+
const target = statSync(current);
|
|
245
|
+
return kind === "file" && target.isFile();
|
|
246
|
+
} catch {
|
|
247
|
+
return false;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
return true;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function canonicalPath(candidate: string): string {
|
|
255
|
+
try {
|
|
256
|
+
return realpathSync(candidate);
|
|
257
|
+
} catch {
|
|
258
|
+
return path.resolve(candidate);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function normalizeCandidate(
|
|
263
|
+
canonicalRoot: string,
|
|
264
|
+
suppliedRoot: string,
|
|
265
|
+
candidate: string,
|
|
266
|
+
): string | null {
|
|
267
|
+
const absoluteCandidate = path.isAbsolute(candidate)
|
|
268
|
+
? mapSuppliedRootToCanonical(canonicalRoot, suppliedRoot, candidate)
|
|
269
|
+
: path.resolve(canonicalRoot, candidate);
|
|
270
|
+
const relativePath = path.relative(canonicalRoot, absoluteCandidate);
|
|
271
|
+
if (relativePath === "") return "";
|
|
272
|
+
if (relativePath === ".." || relativePath.startsWith(`..${path.sep}`)) return null;
|
|
273
|
+
return relativePath.split(path.sep).join("/");
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
function mapSuppliedRootToCanonical(
|
|
277
|
+
canonicalRoot: string,
|
|
278
|
+
suppliedRoot: string,
|
|
279
|
+
candidate: string,
|
|
280
|
+
): string {
|
|
281
|
+
const resolvedSuppliedRoot = path.resolve(suppliedRoot);
|
|
282
|
+
const relativePath = path.relative(resolvedSuppliedRoot, path.resolve(candidate));
|
|
283
|
+
if (relativePath === "" || (relativePath !== ".." && !relativePath.startsWith(`..${path.sep}`))) {
|
|
284
|
+
return path.resolve(canonicalRoot, relativePath);
|
|
285
|
+
}
|
|
286
|
+
return path.resolve(candidate);
|
|
287
|
+
}
|
|
@@ -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
|
|
63
|
-
|
|
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 +
|
|
67
|
+
supi-tree-sitter ← one owned Structural Worker + runtime capabilities
|
|
69
68
|
```
|
|
70
69
|
|
|
71
70
|
## Package surfaces
|
|
72
71
|
|
|
73
|
-
|
|
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
|
|
@@ -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
|
+
}
|
package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/debug.ts
CHANGED
|
@@ -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
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mrclrchtr/supi-tree-sitter",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.2.0",
|
|
4
4
|
"description": "Structural AST analysis for SuPi code intelligence",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -30,14 +30,13 @@
|
|
|
30
30
|
"src/**/*.mjs",
|
|
31
31
|
"src/**/*.json",
|
|
32
32
|
"resources",
|
|
33
|
-
"scripts/*.mjs",
|
|
34
33
|
"!__tests__"
|
|
35
34
|
],
|
|
36
35
|
"dependencies": {
|
|
37
36
|
"jiti": "^2.7.0",
|
|
38
37
|
"web-tree-sitter": "^0.26.8",
|
|
39
|
-
"@mrclrchtr/supi-code-runtime": "6.
|
|
40
|
-
"@mrclrchtr/supi-core": "6.
|
|
38
|
+
"@mrclrchtr/supi-code-runtime": "6.2.0",
|
|
39
|
+
"@mrclrchtr/supi-core": "6.2.0"
|
|
41
40
|
},
|
|
42
41
|
"bundledDependencies": [
|
|
43
42
|
"@mrclrchtr/supi-code-runtime",
|
|
@@ -45,7 +44,6 @@
|
|
|
45
44
|
"jiti",
|
|
46
45
|
"web-tree-sitter"
|
|
47
46
|
],
|
|
48
|
-
"main": "src/api.ts",
|
|
49
47
|
"exports": {
|
|
50
48
|
"./api": "./src/api.ts",
|
|
51
49
|
"./package.json": "./package.json",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Public
|
|
1
|
+
// Public Tree-sitter session factory, lifecycle controller, and shared types.
|
|
2
2
|
//
|
|
3
3
|
// This package is library-only — no pi extension surface.
|
|
4
4
|
// Exports structured runtime/service APIs only.
|
|
@@ -7,19 +7,11 @@
|
|
|
7
7
|
// Language detection helpers
|
|
8
8
|
export {
|
|
9
9
|
detectGrammar,
|
|
10
|
-
getSupportedExtension,
|
|
11
10
|
getSupportedExtensions,
|
|
12
|
-
isJsTsGrammar,
|
|
13
|
-
isSupportedFile,
|
|
14
11
|
} from "./language.ts";
|
|
15
12
|
export type { StructuralSearchOperation } from "./operation-support.ts";
|
|
16
13
|
export { getStructuralSearchSupportedExtensions } from "./operation-support.ts";
|
|
17
|
-
export type {
|
|
18
|
-
TsControllerState,
|
|
19
|
-
TsStartResult,
|
|
20
|
-
} from "./session/runtime-controller.ts";
|
|
21
14
|
export { TreeSitterRuntimeController } from "./session/runtime-controller.ts";
|
|
22
|
-
export { getSessionTreeSitterService } from "./session/service-registry.ts";
|
|
23
15
|
export { createTreeSitterSession } from "./session/session.ts";
|
|
24
16
|
|
|
25
17
|
// Shared types
|
|
@@ -32,8 +24,6 @@ export type {
|
|
|
32
24
|
NodeAtResult,
|
|
33
25
|
OutlineItem,
|
|
34
26
|
QueryCapture,
|
|
35
|
-
SessionTreeSitterService,
|
|
36
|
-
SessionTreeSitterServiceState,
|
|
37
27
|
SourceRange,
|
|
38
28
|
SupportedExtension,
|
|
39
29
|
TreeSitterResult,
|
|
@@ -81,6 +81,25 @@ export function nodeToRange(
|
|
|
81
81
|
};
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
+
/** Validate that a public position exists in the source bounds. */
|
|
85
|
+
export function validatePublicPositionBounds(
|
|
86
|
+
line: number,
|
|
87
|
+
character: number,
|
|
88
|
+
source: string,
|
|
89
|
+
): { kind: "validation-error"; message: string } | null {
|
|
90
|
+
const lines = splitSourceLines(source);
|
|
91
|
+
if (line > lines.length) {
|
|
92
|
+
return { kind: "validation-error", message: "line is beyond end of file" };
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const lineText = lines[line - 1] ?? "";
|
|
96
|
+
if (character > lineText.length + 1) {
|
|
97
|
+
return { kind: "validation-error", message: "character is beyond end of line" };
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
|
|
84
103
|
/** Split source into logical lines without CRLF line-ending bytes. */
|
|
85
104
|
export function splitSourceLines(source: string): string[] {
|
|
86
105
|
return source.replace(/\r\n?/g, "\n").split("\n");
|