@shanepadgett/tau-agent 0.33.0 → 0.34.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/docs/context.md +29 -20
- package/docs/extending-tau-agent.md +63 -24
- package/extensions/appshot/index.ts +112 -111
- package/extensions/aside/README.md +7 -0
- package/extensions/aside/index.ts +168 -0
- package/extensions/aside/panel.ts +83 -0
- package/extensions/branch/index.ts +38 -21
- package/extensions/cache-diagnostics/index.ts +70 -58
- package/extensions/checkpoint/README.md +9 -0
- package/extensions/checkpoint/checkpoint-budget.ts +79 -0
- package/extensions/checkpoint/checkpoint.ts +277 -0
- package/extensions/checkpoint/index.ts +102 -0
- package/extensions/checkpoint/messages.ts +169 -0
- package/extensions/checkpoint/prompt.ts +24 -0
- package/extensions/checkpoint/settings.ts +27 -0
- package/extensions/commit/commit-effort.ts +3 -3
- package/extensions/context/README.md +12 -8
- package/extensions/context/definitions.ts +178 -55
- package/extensions/context/index.ts +192 -160
- package/extensions/context/panel.ts +57 -37
- package/extensions/context/sync.ts +139 -89
- package/extensions/context/validation.ts +37 -25
- package/extensions/effort/README.md +1 -1
- package/extensions/effort/index.ts +3 -3
- package/extensions/effort/state.ts +3 -18
- package/extensions/explore/guidance.ts +6 -47
- package/extensions/explore/index.ts +38 -53
- package/extensions/explore/{ast/read → read}/hook.ts +12 -7
- package/extensions/explore/{ast/tools → tools}/ast-search.ts +15 -19
- package/extensions/explore/{ast/tools → tools}/context.ts +16 -23
- package/extensions/explore/{ast/tools → tools}/deps.ts +11 -15
- package/extensions/explore/{ast/tools → tools}/discover.ts +15 -17
- package/extensions/explore/{ast/tools → tools}/impact.ts +20 -23
- package/extensions/explore/{ast/tools → tools}/outline.ts +21 -24
- package/extensions/explore/{ast/tools → tools}/relationships.ts +23 -32
- package/extensions/explore/{ast/tools → tools}/render.ts +1 -1
- package/extensions/explore/{ast/tools → tools}/reverse-deps.ts +11 -15
- package/extensions/explore/{ast/tools → tools}/show.ts +16 -19
- package/extensions/explore/tools/target-params.ts +9 -0
- package/extensions/footer/index.ts +75 -56
- package/extensions/handoff/index.ts +8 -30
- package/extensions/image-gen/index.ts +46 -40
- package/extensions/patch/executor.ts +190 -135
- package/extensions/patch/matcher.ts +82 -48
- package/extensions/patch/parser.ts +149 -105
- package/extensions/patch/render.ts +32 -39
- package/extensions/patch/summary.ts +30 -26
- package/extensions/ready/README.md +7 -0
- package/extensions/ready/index.ts +58 -0
- package/extensions/ready/model.ts +128 -0
- package/extensions/ready/packs.ts +272 -0
- package/extensions/ready/render.ts +445 -0
- package/extensions/ready/scan.ts +760 -0
- package/extensions/reference/panel.ts +28 -16
- package/extensions/review/index.ts +63 -51
- package/extensions/review/panel.ts +5 -34
- package/extensions/runtime-context/context.ts +30 -26
- package/extensions/script-runner/README.md +2 -2
- package/extensions/script-runner/index.ts +130 -93
- package/extensions/silent-command-runner/index.ts +38 -23
- package/extensions/subagent/agents/context-sync.md +185 -33
- package/extensions/subagent/agents/scout.md +2 -2
- package/extensions/subagent/agents.ts +137 -84
- package/extensions/subagent/cmux-dashboard.ts +39 -30
- package/extensions/subagent/index.ts +79 -59
- package/extensions/subagent/resume.ts +1 -1
- package/extensions/subagent/run.ts +304 -193
- package/extensions/subagent/runtime.ts +781 -343
- package/extensions/tau/index.ts +47 -38
- package/extensions/tau-help/help.md +14 -6
- package/extensions/tool-loader/README.md +4 -2
- package/extensions/tool-loader/index.ts +77 -52
- package/extensions/web/index.ts +6 -3
- package/extensions/web/webfetch.ts +114 -86
- package/package.json +3 -3
- package/schemas/tau.schema.json +20 -33
- package/shared/bounded-text-result.ts +1 -0
- package/shared/checkpoint-visibility.ts +9 -0
- package/shared/events.ts +28 -10
- package/shared/isolated-session.ts +71 -29
- package/shared/model-effort.ts +9 -19
- package/shared/model-fallback/index.ts +66 -41
- package/shared/settings/load.ts +45 -38
- package/shared/temporary-output-store.ts +16 -10
- package/{extensions/explore → src}/ast/adapter.ts +11 -2
- package/{extensions/explore → src}/ast/engine.ts +54 -38
- package/{extensions/explore → src}/ast/format/ast-search.ts +15 -6
- package/{extensions/explore → src}/ast/format/context.ts +3 -6
- package/src/ast/format/deps.ts +94 -0
- package/{extensions/explore → src}/ast/format/discover.ts +2 -2
- package/{extensions/explore → src}/ast/format/impact.ts +9 -11
- package/{extensions/explore → src}/ast/format/outline.ts +2 -2
- package/{extensions/explore → src}/ast/format/relationships.ts +11 -7
- package/{extensions/explore → src}/ast/format/show.ts +1 -1
- package/src/ast/format/targeted-result.ts +20 -0
- package/src/ast/grammars/c_sharp.wasm +0 -0
- package/{extensions/explore → src}/ast/grammars/manifest.json +9 -12
- package/{extensions/explore → src}/ast/grammars/manifest.ts +0 -4
- package/{extensions/explore → src}/ast/graph/file-graph.ts +210 -98
- package/{extensions/explore → src}/ast/graph/relationships.ts +285 -72
- package/{extensions/explore → src}/ast/ir.ts +6 -0
- package/src/ast/languages/csharp-file-deps.ts +90 -0
- package/{extensions/explore → src}/ast/languages/csharp.ts +9 -40
- package/src/ast/languages/dotted-index.ts +131 -0
- package/{extensions/explore → src}/ast/languages/file-dep-util.ts +22 -13
- package/src/ast/languages/fixtures/sample.cs +53 -0
- package/src/ast/languages/fixtures/sample.cs.expected.json +156 -0
- package/src/ast/languages/fixtures/sample.go +37 -0
- package/src/ast/languages/fixtures/sample.go.expected.json +96 -0
- package/src/ast/languages/fixtures/sample.java +40 -0
- package/src/ast/languages/fixtures/sample.java.expected.json +126 -0
- package/src/ast/languages/fixtures/sample.kt +31 -0
- package/src/ast/languages/fixtures/sample.kt.expected.json +134 -0
- package/src/ast/languages/fixtures/sample.md +23 -0
- package/src/ast/languages/fixtures/sample.md.expected.json +44 -0
- package/src/ast/languages/fixtures/sample.rs +54 -0
- package/src/ast/languages/fixtures/sample.rs.expected.json +178 -0
- package/src/ast/languages/fixtures/sample.swift +45 -0
- package/src/ast/languages/fixtures/sample.swift.expected.json +147 -0
- package/src/ast/languages/fixtures/sample.ts +45 -0
- package/src/ast/languages/fixtures/sample.ts.expected.json +141 -0
- package/src/ast/languages/fixtures/sample.tsx +36 -0
- package/src/ast/languages/fixtures/sample.tsx.expected.json +59 -0
- package/{extensions/explore → src}/ast/languages/go-file-deps.ts +6 -0
- package/{extensions/explore → src}/ast/languages/go.ts +21 -3
- package/{extensions/explore → src}/ast/languages/java.ts +5 -38
- package/src/ast/languages/jvm-file-deps.ts +135 -0
- package/{extensions/explore → src}/ast/languages/kotlin.ts +5 -39
- package/{extensions/explore → src}/ast/languages/rust-file-deps.ts +3 -3
- package/{extensions/explore → src}/ast/languages/rust.ts +21 -13
- package/{extensions/explore → src}/ast/languages/swift-file-deps.ts +22 -15
- package/{extensions/explore → src}/ast/languages/swift.ts +9 -39
- package/{extensions/explore → src}/ast/languages/tree.ts +47 -0
- package/{extensions/explore → src}/ast/languages/typescript-file-deps.ts +108 -0
- package/{extensions/explore → src}/ast/languages/typescript-package-surface.ts +4 -3
- package/{extensions/explore → src}/ast/languages/typescript.ts +3 -5
- package/{extensions/explore → src}/ast/markdown.ts +72 -47
- package/{extensions/explore → src}/ast/queries/ast-search.ts +164 -146
- package/{extensions/explore → src}/ast/queries/composite-target.ts +1 -1
- package/{extensions/explore → src}/ast/queries/context.ts +116 -80
- package/{extensions/explore → src}/ast/queries/discover.ts +154 -105
- package/src/ast/queries/impact.ts +291 -0
- package/{extensions/explore → src}/ast/queries/outline.ts +4 -2
- package/{extensions/explore → src}/ast/queries/show.ts +46 -42
- package/{extensions/explore → src}/ast/query.ts +1 -1
- package/{extensions/explore → src}/ast/registry.ts +1 -2
- package/{extensions/explore → src}/ast/scan.ts +1 -1
- package/src/ast/session.ts +50 -0
- package/{extensions/explore → src}/ast/slice.ts +8 -0
- package/{extensions/explore → src/ast}/traverse.ts +32 -20
- package/src/file-injection/index.ts +547 -0
- package/src/image-generation/client.ts +50 -31
- package/src/image-generation/index.ts +177 -134
- package/src/index.ts +2 -0
- package/src/tool-loading/index.ts +58 -0
- package/extensions/context/evidence.ts +0 -517
- package/extensions/context/projection.ts +0 -141
- package/extensions/context/state.ts +0 -30
- package/extensions/context/write-scope.ts +0 -109
- package/extensions/explore/ast/format/deps.ts +0 -86
- package/extensions/explore/ast/grammars/odin.wasm +0 -0
- package/extensions/explore/ast/languages/csharp-file-deps.ts +0 -189
- package/extensions/explore/ast/languages/fixtures/sample.cs +0 -17
- package/extensions/explore/ast/languages/fixtures/sample.java +0 -21
- package/extensions/explore/ast/languages/fixtures/sample.kt +0 -15
- package/extensions/explore/ast/languages/fixtures/sample.odin +0 -28
- package/extensions/explore/ast/languages/fixtures/sample.rs +0 -31
- package/extensions/explore/ast/languages/fixtures/sample.swift +0 -25
- package/extensions/explore/ast/languages/jvm-file-deps.ts +0 -217
- package/extensions/explore/ast/languages/odin-file-deps.ts +0 -175
- package/extensions/explore/ast/languages/odin.ts +0 -407
- package/extensions/explore/ast/queries/impact.ts +0 -250
- package/extensions/explore/outline-injection.ts +0 -151
- package/extensions/explore/read/autoread.ts +0 -118
- package/extensions/working-memory/README.md +0 -17
- package/extensions/working-memory/checkpoint.ts +0 -242
- package/extensions/working-memory/index.ts +0 -341
- package/extensions/working-memory/memory.ts +0 -235
- package/extensions/working-memory/render.ts +0 -185
- package/extensions/working-memory/settings.ts +0 -38
- package/extensions/working-memory/state.ts +0 -158
- package/shared/autoread.ts +0 -234
- package/shared/context-messages.ts +0 -19
- package/shared/outline-injection.ts +0 -56
- /package/{extensions/explore → src}/ast/format/composite.ts +0 -0
- /package/{extensions/explore → src}/ast/grammars/kotlin.wasm +0 -0
- /package/{extensions/explore → src}/ast/grammars/swift.wasm +0 -0
- /package/{extensions/explore → src}/ast/identity.ts +0 -0
- /package/{extensions/explore → src}/ast/package-surface.ts +0 -0
- /package/{extensions/explore/ast/read/policy.ts → src/ast/read-policy.ts} +0 -0
|
@@ -1,217 +0,0 @@
|
|
|
1
|
-
import { dirname, join, relative, resolve, sep } from "node:path";
|
|
2
|
-
import type { FileDepResolver } from "../adapter.ts";
|
|
3
|
-
import { externalId, firstExistingFile, internalPaths, isWithin } from "./file-dep-util.ts";
|
|
4
|
-
|
|
5
|
-
type JvmFileDepOptions = {
|
|
6
|
-
/** File extensions to match, e.g. .java / .kt */
|
|
7
|
-
extensions: readonly string[];
|
|
8
|
-
/** Leading packages always treated as external. */
|
|
9
|
-
externalPrefixes: readonly string[];
|
|
10
|
-
/** Extra source-root suffixes under scope (relative). */
|
|
11
|
-
sourceRootSuffixes: readonly string[];
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
function stripImportNoise(specifier: string): { path: string; wildcard: boolean } {
|
|
15
|
-
let s = specifier.trim().replace(/;$/u, "");
|
|
16
|
-
s = s.replace(/^static\s+/u, "");
|
|
17
|
-
const wildcard = s.endsWith(".*");
|
|
18
|
-
if (wildcard) {
|
|
19
|
-
// Keep full package path — do not peel segments (all-lowercase packages).
|
|
20
|
-
return { path: s.slice(0, -2).trim(), wildcard: true };
|
|
21
|
-
}
|
|
22
|
-
// Static/member imports: pkg.Type.member or pkg.Type.Companion.member → peel trailing
|
|
23
|
-
// non-type segments (lowercase start, or Companion/INSTANCE).
|
|
24
|
-
const parts = s.split(".").filter((part) => part.length > 0);
|
|
25
|
-
while (parts.length > 1) {
|
|
26
|
-
const last = parts[parts.length - 1];
|
|
27
|
-
if (last === undefined) break;
|
|
28
|
-
if (last === "Companion" || last === "INSTANCE" || last === "Default") {
|
|
29
|
-
parts.pop();
|
|
30
|
-
continue;
|
|
31
|
-
}
|
|
32
|
-
// Java/Kotlin type names are typically UpperCamel; members start lower.
|
|
33
|
-
const start = last.charAt(0);
|
|
34
|
-
if (start !== "" && start === start.toLowerCase() && start !== start.toUpperCase()) {
|
|
35
|
-
parts.pop();
|
|
36
|
-
continue;
|
|
37
|
-
}
|
|
38
|
-
break;
|
|
39
|
-
}
|
|
40
|
-
return { path: parts.join("."), wildcard: false };
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function isExternalPackage(path: string, prefixes: readonly string[]): boolean {
|
|
44
|
-
for (const prefix of prefixes) {
|
|
45
|
-
if (path === prefix || path.startsWith(`${prefix}.`)) return true;
|
|
46
|
-
}
|
|
47
|
-
return false;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function segmentsOf(dotted: string): string[] {
|
|
51
|
-
return dotted.split(".").filter((part) => part.length > 0);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/** Infer source root by peeling package segments off dirname(fromPath). */
|
|
55
|
-
function inferredSourceRoot(fromPath: string, packageSegments: readonly string[]): string | undefined {
|
|
56
|
-
if (packageSegments.length === 0) return dirname(fromPath);
|
|
57
|
-
const dir = resolve(dirname(fromPath));
|
|
58
|
-
const parts = dir.split(sep);
|
|
59
|
-
if (parts.length < packageSegments.length) return undefined;
|
|
60
|
-
const tail = parts.slice(parts.length - packageSegments.length);
|
|
61
|
-
if (tail.join("/") !== packageSegments.join("/") && tail.join(sep) !== packageSegments.join(sep)) {
|
|
62
|
-
// compare case-sensitive path segments
|
|
63
|
-
for (let i = 0; i < packageSegments.length; i += 1) {
|
|
64
|
-
if (tail[i] !== packageSegments[i]) return undefined;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
return parts.slice(0, parts.length - packageSegments.length).join(sep) || sep;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/** Resolve a type import (`pkg.Type`) to 0–1 files under a source root. No package dumps. */
|
|
71
|
-
async function resolveTypeAgainstRoot(
|
|
72
|
-
host: Parameters<FileDepResolver>[2],
|
|
73
|
-
sourceRoot: string,
|
|
74
|
-
segments: readonly string[],
|
|
75
|
-
extensions: readonly string[],
|
|
76
|
-
): Promise<string | undefined> {
|
|
77
|
-
if (!isWithin(host.scopeRoot, sourceRoot) && resolve(sourceRoot) !== resolve(host.scopeRoot)) {
|
|
78
|
-
return undefined;
|
|
79
|
-
}
|
|
80
|
-
if (segments.length === 0) return undefined;
|
|
81
|
-
const typeName = segments[segments.length - 1];
|
|
82
|
-
const pkg = segments.slice(0, -1);
|
|
83
|
-
if (typeName === undefined) return undefined;
|
|
84
|
-
const dir = pkg.length === 0 ? sourceRoot : join(sourceRoot, ...pkg);
|
|
85
|
-
const candidates = extensions.map((ext) => join(dir, `${typeName}${ext}`));
|
|
86
|
-
const file = await firstExistingFile(host, candidates);
|
|
87
|
-
if (file !== undefined && isWithin(host.scopeRoot, file)) return file;
|
|
88
|
-
return undefined;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
function sourceRootsFor(
|
|
92
|
-
host: Parameters<FileDepResolver>[2],
|
|
93
|
-
fromPath: string,
|
|
94
|
-
importSegments: readonly string[],
|
|
95
|
-
suffixes: readonly string[],
|
|
96
|
-
): string[] {
|
|
97
|
-
const roots = new Set<string>();
|
|
98
|
-
// Peel one segment as possible type from fromPath package guess via path
|
|
99
|
-
const fromDir = dirname(fromPath);
|
|
100
|
-
const relToScope = relative(host.scopeRoot, fromDir);
|
|
101
|
-
if (!relToScope.startsWith("..") && !isAbsoluteRel(relToScope)) {
|
|
102
|
-
// try every ancestor as potential source root
|
|
103
|
-
const parts = relToScope.length === 0 ? [] : relToScope.split(sep);
|
|
104
|
-
for (let i = 0; i <= parts.length; i += 1) {
|
|
105
|
-
roots.add(i === 0 ? host.scopeRoot : join(host.scopeRoot, ...parts.slice(0, i)));
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
const inferred = inferredSourceRoot(fromPath, guessPackageSegments(fromPath, host.scopeRoot));
|
|
109
|
-
if (inferred !== undefined) roots.add(inferred);
|
|
110
|
-
|
|
111
|
-
for (const suffix of suffixes) {
|
|
112
|
-
roots.add(join(host.scopeRoot, ...suffix.split("/").filter((p) => p.length > 0)));
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// Prefer roots that could contain the import path
|
|
116
|
-
void importSegments;
|
|
117
|
-
return [...roots];
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
function isAbsoluteRel(rel: string): boolean {
|
|
121
|
-
return rel.length > 0 && (rel.startsWith(sep) || /^[A-Za-z]:[\\/]/u.test(rel));
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
function guessPackageSegments(fromPath: string, scopeRoot: string): string[] {
|
|
125
|
-
const rel = relative(scopeRoot, dirname(fromPath));
|
|
126
|
-
if (rel.startsWith("..") || rel.length === 0) return [];
|
|
127
|
-
const parts = rel.split(sep);
|
|
128
|
-
// Drop common source root prefixes
|
|
129
|
-
const dropPrefixes = [
|
|
130
|
-
["src", "main", "java"],
|
|
131
|
-
["src", "main", "kotlin"],
|
|
132
|
-
["src", "commonMain", "kotlin"],
|
|
133
|
-
["src", "commonMain", "java"],
|
|
134
|
-
["src", "jvmMain", "kotlin"],
|
|
135
|
-
["src", "test", "java"],
|
|
136
|
-
["src", "test", "kotlin"],
|
|
137
|
-
["src"],
|
|
138
|
-
];
|
|
139
|
-
for (const prefix of dropPrefixes) {
|
|
140
|
-
if (parts.length >= prefix.length && prefix.every((p, i) => parts[i] === p)) {
|
|
141
|
-
return parts.slice(prefix.length);
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
// guava layout: guava/src/com/...
|
|
145
|
-
const comAt = parts.indexOf("com");
|
|
146
|
-
const orgAt = parts.indexOf("org");
|
|
147
|
-
const netAt = parts.indexOf("net");
|
|
148
|
-
const idx = [comAt, orgAt, netAt].filter((v) => v >= 0).sort((a, b) => a - b)[0];
|
|
149
|
-
if (idx !== undefined) return parts.slice(idx);
|
|
150
|
-
return parts;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
function createJvmFileDepResolver(options: JvmFileDepOptions): FileDepResolver {
|
|
154
|
-
return async (fromPath, specifier, host, signal) => {
|
|
155
|
-
signal.throwIfAborted();
|
|
156
|
-
const raw = specifier
|
|
157
|
-
.trim()
|
|
158
|
-
.replace(/;$/u, "")
|
|
159
|
-
.replace(/^static\s+/u, "");
|
|
160
|
-
const { path, wildcard } = stripImportNoise(specifier);
|
|
161
|
-
if (path.length === 0) return { kind: "unresolved" };
|
|
162
|
-
if (isExternalPackage(path, options.externalPrefixes)) return externalId(path);
|
|
163
|
-
|
|
164
|
-
// Wildcards / bare packages are package ids — never dump every file in the dir.
|
|
165
|
-
if (wildcard) return externalId(path);
|
|
166
|
-
|
|
167
|
-
const segments = segmentsOf(path);
|
|
168
|
-
const packageOnly =
|
|
169
|
-
segments.length > 0 &&
|
|
170
|
-
segments.every((seg) => {
|
|
171
|
-
const c = seg.charAt(0);
|
|
172
|
-
return c === c.toLowerCase() && c !== c.toUpperCase();
|
|
173
|
-
});
|
|
174
|
-
// Top-level function/property import (okio.buffer) peels to package-only segments.
|
|
175
|
-
const rawParts = raw
|
|
176
|
-
.replace(/\.\*$/u, "")
|
|
177
|
-
.split(".")
|
|
178
|
-
.filter((part) => part.length > 0);
|
|
179
|
-
const lastRaw = rawParts[rawParts.length - 1];
|
|
180
|
-
const memberLike =
|
|
181
|
-
lastRaw !== undefined &&
|
|
182
|
-
lastRaw !== "Companion" &&
|
|
183
|
-
lastRaw.charAt(0) === lastRaw.charAt(0).toLowerCase() &&
|
|
184
|
-
lastRaw.charAt(0) !== lastRaw.charAt(0).toUpperCase();
|
|
185
|
-
if (packageOnly) {
|
|
186
|
-
return externalId(memberLike ? raw.replace(/\.\*$/u, "") : path);
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
const roots = sourceRootsFor(host, fromPath, segments, options.sourceRootSuffixes);
|
|
190
|
-
for (const root of roots) {
|
|
191
|
-
signal.throwIfAborted();
|
|
192
|
-
const file = await resolveTypeAgainstRoot(host, root, segments, options.extensions);
|
|
193
|
-
if (file !== undefined) return internalPaths([file]);
|
|
194
|
-
}
|
|
195
|
-
return externalId(path);
|
|
196
|
-
};
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
export const resolveJavaFileDep = createJvmFileDepResolver({
|
|
200
|
-
extensions: [".java"],
|
|
201
|
-
externalPrefixes: ["java", "javax", "jakarta", "jdk", "sun"],
|
|
202
|
-
sourceRootSuffixes: ["src/main/java", "src"],
|
|
203
|
-
});
|
|
204
|
-
|
|
205
|
-
export const resolveKotlinFileDep = createJvmFileDepResolver({
|
|
206
|
-
// .kts is often Gradle — not a package type file.
|
|
207
|
-
extensions: [".kt", ".java"],
|
|
208
|
-
externalPrefixes: ["java", "javax", "jakarta", "kotlin", "kotlinx"],
|
|
209
|
-
sourceRootSuffixes: [
|
|
210
|
-
"src/main/kotlin",
|
|
211
|
-
"src/main/java",
|
|
212
|
-
"src/commonMain/kotlin",
|
|
213
|
-
"src/commonMain/java",
|
|
214
|
-
"src/jvmMain/kotlin",
|
|
215
|
-
"src",
|
|
216
|
-
],
|
|
217
|
-
});
|
|
@@ -1,175 +0,0 @@
|
|
|
1
|
-
import { dirname, join, resolve } from "node:path";
|
|
2
|
-
import type { FileDepHost, FileDepResolver } from "../adapter.ts";
|
|
3
|
-
import { boundedInternalPaths, externalId, firstExistingFile, internalPaths, isWithin } from "./file-dep-util.ts";
|
|
4
|
-
|
|
5
|
-
/** `collection:pkg/path` — Odin collection import. */
|
|
6
|
-
const COLLECTION_SPEC = /^([A-Za-z_][A-Za-z0-9_]*):(.+)$/u;
|
|
7
|
-
|
|
8
|
-
async function isDir(host: FileDepHost, path: string): Promise<boolean> {
|
|
9
|
-
if (!(await host.pathExists(path))) return false;
|
|
10
|
-
if (await host.isFile(path)) return false;
|
|
11
|
-
return true;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/** Direct `.odin` package files in a directory (not nested packages). */
|
|
15
|
-
async function listOdinPackageFiles(host: FileDepHost, dir: string): Promise<string[]> {
|
|
16
|
-
if (!(await isDir(host, dir))) return [];
|
|
17
|
-
const names = await host.readDir(dir);
|
|
18
|
-
const out: string[] = [];
|
|
19
|
-
for (const name of names) {
|
|
20
|
-
if (!name.endsWith(".odin")) continue;
|
|
21
|
-
const full = join(dir, name);
|
|
22
|
-
if ((await host.isFile(full)) && host.ownsPath(full)) out.push(full);
|
|
23
|
-
}
|
|
24
|
-
return out;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Odin root under scope: directory that has both `core` and `base` collections.
|
|
29
|
-
* Also accepts ODIN_ROOT when that path sits inside the scope.
|
|
30
|
-
*/
|
|
31
|
-
async function findOdinRoot(host: FileDepHost, fromPath: string): Promise<string | undefined> {
|
|
32
|
-
const key = `odin-root:${host.scopeRoot}`;
|
|
33
|
-
if (host.memo.has(key)) return host.memo.get(key) as string | undefined;
|
|
34
|
-
|
|
35
|
-
const envRoot = process.env.ODIN_ROOT?.trim();
|
|
36
|
-
if (envRoot !== undefined && envRoot.length > 0) {
|
|
37
|
-
const absolute = resolve(envRoot);
|
|
38
|
-
if (isWithin(host.scopeRoot, absolute) || absolute === resolve(host.scopeRoot)) {
|
|
39
|
-
if ((await isDir(host, join(absolute, "core"))) && (await isDir(host, join(absolute, "base")))) {
|
|
40
|
-
host.memo.set(key, absolute);
|
|
41
|
-
return absolute;
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
let dir = resolve(dirname(fromPath));
|
|
47
|
-
const stop = resolve(host.scopeRoot);
|
|
48
|
-
for (;;) {
|
|
49
|
-
if (isWithin(stop, dir) || dir === stop) {
|
|
50
|
-
const core = join(dir, "core");
|
|
51
|
-
const base = join(dir, "base");
|
|
52
|
-
if ((await isDir(host, core)) && (await isDir(host, base))) {
|
|
53
|
-
host.memo.set(key, dir);
|
|
54
|
-
return dir;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
if (dir === stop) break;
|
|
58
|
-
const parent = dirname(dir);
|
|
59
|
-
if (parent === dir) break;
|
|
60
|
-
if (!isWithin(stop, parent) && parent !== stop) break;
|
|
61
|
-
dir = parent;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// Scope itself may be the Odin tree root even when fromPath walk missed.
|
|
65
|
-
const scopeCore = join(stop, "core");
|
|
66
|
-
const scopeBase = join(stop, "base");
|
|
67
|
-
if ((await isDir(host, scopeCore)) && (await isDir(host, scopeBase))) {
|
|
68
|
-
host.memo.set(key, stop);
|
|
69
|
-
return stop;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
host.memo.set(key, undefined);
|
|
73
|
-
return undefined;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Candidate roots for a named collection, nearest-first.
|
|
78
|
-
* Project `vendor`/`shared` beside sources beat the compiler tree when both exist.
|
|
79
|
-
*/
|
|
80
|
-
async function collectionRoots(host: FileDepHost, collection: string, fromPath: string): Promise<string[]> {
|
|
81
|
-
const key = `odin-collection:${host.scopeRoot}:${collection}`;
|
|
82
|
-
const cached = host.memo.get(key);
|
|
83
|
-
if (cached !== undefined) return cached as string[];
|
|
84
|
-
|
|
85
|
-
const found: string[] = [];
|
|
86
|
-
const seen = new Set<string>();
|
|
87
|
-
const add = async (candidate: string) => {
|
|
88
|
-
const absolute = resolve(candidate);
|
|
89
|
-
if (seen.has(absolute)) return;
|
|
90
|
-
if (!isWithin(host.scopeRoot, absolute) && absolute !== resolve(host.scopeRoot)) return;
|
|
91
|
-
if (!(await isDir(host, absolute))) return;
|
|
92
|
-
seen.add(absolute);
|
|
93
|
-
found.push(absolute);
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
// Ancestors from the importing file up to scope (project-local collections).
|
|
97
|
-
let dir = resolve(dirname(fromPath));
|
|
98
|
-
const stop = resolve(host.scopeRoot);
|
|
99
|
-
for (;;) {
|
|
100
|
-
await add(join(dir, collection));
|
|
101
|
-
if (dir === stop) break;
|
|
102
|
-
const parent = dirname(dir);
|
|
103
|
-
if (parent === dir) break;
|
|
104
|
-
if (!isWithin(stop, parent) && parent !== stop) break;
|
|
105
|
-
dir = parent;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
await add(join(stop, collection));
|
|
109
|
-
|
|
110
|
-
const odinRoot = await findOdinRoot(host, fromPath);
|
|
111
|
-
if (odinRoot !== undefined) await add(join(odinRoot, collection));
|
|
112
|
-
|
|
113
|
-
host.memo.set(key, found);
|
|
114
|
-
return found;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
async function resolveCollectionImport(
|
|
118
|
-
host: FileDepHost,
|
|
119
|
-
collection: string,
|
|
120
|
-
pkgPath: string,
|
|
121
|
-
fromPath: string,
|
|
122
|
-
signal: AbortSignal,
|
|
123
|
-
): Promise<ReturnType<typeof internalPaths> | ReturnType<typeof externalId>> {
|
|
124
|
-
const id = `${collection}:${pkgPath}`;
|
|
125
|
-
const segments = pkgPath.split(/[/\\]/u).filter((part) => part.length > 0 && part !== "." && part !== "..");
|
|
126
|
-
if (segments.length === 0) return externalId(id);
|
|
127
|
-
|
|
128
|
-
const roots = await collectionRoots(host, collection, fromPath);
|
|
129
|
-
for (const root of roots) {
|
|
130
|
-
signal.throwIfAborted();
|
|
131
|
-
const dir = join(root, ...segments);
|
|
132
|
-
if (!isWithin(host.scopeRoot, dir) && dir !== resolve(host.scopeRoot)) continue;
|
|
133
|
-
const files = await listOdinPackageFiles(host, dir);
|
|
134
|
-
// Fat packages (e.g. base:runtime) stay package-id edges, not 40+ file dumps.
|
|
135
|
-
if (files.length > 0) return boundedInternalPaths(files, id);
|
|
136
|
-
}
|
|
137
|
-
return externalId(id);
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
async function resolveRelativeImport(
|
|
141
|
-
host: FileDepHost,
|
|
142
|
-
fromPath: string,
|
|
143
|
-
trimmed: string,
|
|
144
|
-
): Promise<ReturnType<typeof internalPaths> | { kind: "unresolved" }> {
|
|
145
|
-
const absolute = resolve(dirname(fromPath), trimmed);
|
|
146
|
-
const candidates = [absolute, `${absolute}.odin`, join(absolute, "main.odin")];
|
|
147
|
-
const file = await firstExistingFile(host, candidates);
|
|
148
|
-
if (file !== undefined && isWithin(host.scopeRoot, file)) return internalPaths([file]);
|
|
149
|
-
|
|
150
|
-
const files = (await listOdinPackageFiles(host, absolute)).filter((path) => isWithin(host.scopeRoot, path));
|
|
151
|
-
if (files.length > 0) return boundedInternalPaths(files, trimmed);
|
|
152
|
-
return { kind: "unresolved" };
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
export const resolveOdinFileDep: FileDepResolver = async (fromPath, specifier, host, signal) => {
|
|
156
|
-
signal.throwIfAborted();
|
|
157
|
-
const trimmed = specifier.trim().replace(/^"+|"+$/gu, "");
|
|
158
|
-
if (trimmed.length === 0) return { kind: "unresolved" };
|
|
159
|
-
|
|
160
|
-
const collection = COLLECTION_SPEC.exec(trimmed);
|
|
161
|
-
if (collection !== null) {
|
|
162
|
-
const name = collection[1];
|
|
163
|
-
const pkg = collection[2];
|
|
164
|
-
if (name === undefined || pkg === undefined) return { kind: "unresolved" };
|
|
165
|
-
return resolveCollectionImport(host, name, pkg, fromPath, signal);
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
// Relative / path-like package path
|
|
169
|
-
if (trimmed.startsWith("./") || trimmed.startsWith("../") || trimmed.includes("/") || trimmed.includes("\\")) {
|
|
170
|
-
return resolveRelativeImport(host, fromPath, trimmed);
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
// Bare name with no collection — cannot resolve without collection config.
|
|
174
|
-
return externalId(trimmed);
|
|
175
|
-
};
|