@shanepadgett/tau-agent 0.33.1 → 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.
Files changed (190) hide show
  1. package/docs/context.md +29 -20
  2. package/docs/extending-tau-agent.md +63 -24
  3. package/extensions/appshot/index.ts +112 -111
  4. package/extensions/aside/README.md +7 -0
  5. package/extensions/aside/index.ts +168 -0
  6. package/extensions/aside/panel.ts +83 -0
  7. package/extensions/branch/index.ts +38 -21
  8. package/extensions/cache-diagnostics/index.ts +70 -58
  9. package/extensions/checkpoint/README.md +9 -0
  10. package/extensions/checkpoint/checkpoint-budget.ts +79 -0
  11. package/extensions/checkpoint/checkpoint.ts +277 -0
  12. package/extensions/checkpoint/index.ts +102 -0
  13. package/extensions/checkpoint/messages.ts +169 -0
  14. package/extensions/checkpoint/prompt.ts +24 -0
  15. package/extensions/checkpoint/settings.ts +27 -0
  16. package/extensions/commit/commit-effort.ts +3 -3
  17. package/extensions/context/README.md +12 -8
  18. package/extensions/context/definitions.ts +178 -55
  19. package/extensions/context/index.ts +192 -160
  20. package/extensions/context/panel.ts +57 -37
  21. package/extensions/context/sync.ts +139 -89
  22. package/extensions/context/validation.ts +37 -25
  23. package/extensions/effort/README.md +1 -1
  24. package/extensions/effort/index.ts +3 -3
  25. package/extensions/effort/state.ts +3 -18
  26. package/extensions/explore/guidance.ts +6 -47
  27. package/extensions/explore/index.ts +38 -53
  28. package/extensions/explore/{ast/read → read}/hook.ts +12 -7
  29. package/extensions/explore/{ast/tools → tools}/ast-search.ts +15 -19
  30. package/extensions/explore/{ast/tools → tools}/context.ts +16 -23
  31. package/extensions/explore/{ast/tools → tools}/deps.ts +11 -15
  32. package/extensions/explore/{ast/tools → tools}/discover.ts +15 -17
  33. package/extensions/explore/{ast/tools → tools}/impact.ts +20 -23
  34. package/extensions/explore/{ast/tools → tools}/outline.ts +21 -24
  35. package/extensions/explore/{ast/tools → tools}/relationships.ts +23 -32
  36. package/extensions/explore/{ast/tools → tools}/render.ts +1 -1
  37. package/extensions/explore/{ast/tools → tools}/reverse-deps.ts +11 -15
  38. package/extensions/explore/{ast/tools → tools}/show.ts +16 -19
  39. package/extensions/explore/tools/target-params.ts +9 -0
  40. package/extensions/footer/index.ts +75 -56
  41. package/extensions/handoff/index.ts +8 -30
  42. package/extensions/image-gen/index.ts +46 -40
  43. package/extensions/patch/executor.ts +190 -135
  44. package/extensions/patch/matcher.ts +82 -48
  45. package/extensions/patch/parser.ts +149 -105
  46. package/extensions/patch/render.ts +32 -39
  47. package/extensions/patch/summary.ts +30 -26
  48. package/extensions/ready/README.md +7 -0
  49. package/extensions/ready/index.ts +58 -0
  50. package/extensions/ready/model.ts +128 -0
  51. package/extensions/ready/packs.ts +272 -0
  52. package/extensions/ready/render.ts +445 -0
  53. package/extensions/ready/scan.ts +760 -0
  54. package/extensions/reference/panel.ts +28 -16
  55. package/extensions/review/index.ts +63 -51
  56. package/extensions/review/panel.ts +5 -34
  57. package/extensions/runtime-context/context.ts +30 -26
  58. package/extensions/script-runner/README.md +2 -2
  59. package/extensions/script-runner/index.ts +122 -82
  60. package/extensions/silent-command-runner/index.ts +38 -23
  61. package/extensions/subagent/agents/context-sync.md +185 -33
  62. package/extensions/subagent/agents/scout.md +2 -2
  63. package/extensions/subagent/agents.ts +137 -84
  64. package/extensions/subagent/cmux-dashboard.ts +39 -30
  65. package/extensions/subagent/index.ts +79 -59
  66. package/extensions/subagent/resume.ts +1 -1
  67. package/extensions/subagent/run.ts +304 -193
  68. package/extensions/subagent/runtime.ts +781 -343
  69. package/extensions/tau/index.ts +47 -38
  70. package/extensions/tau-help/help.md +14 -6
  71. package/extensions/tool-loader/README.md +4 -2
  72. package/extensions/tool-loader/index.ts +77 -52
  73. package/extensions/web/index.ts +6 -3
  74. package/extensions/web/webfetch.ts +114 -86
  75. package/package.json +3 -3
  76. package/schemas/tau.schema.json +20 -33
  77. package/shared/bounded-text-result.ts +1 -0
  78. package/shared/checkpoint-visibility.ts +9 -0
  79. package/shared/events.ts +28 -10
  80. package/shared/isolated-session.ts +71 -29
  81. package/shared/model-effort.ts +9 -19
  82. package/shared/model-fallback/index.ts +66 -41
  83. package/shared/settings/load.ts +45 -38
  84. package/shared/temporary-output-store.ts +16 -10
  85. package/{extensions/explore → src}/ast/adapter.ts +11 -2
  86. package/{extensions/explore → src}/ast/engine.ts +54 -38
  87. package/{extensions/explore → src}/ast/format/ast-search.ts +15 -6
  88. package/{extensions/explore → src}/ast/format/context.ts +3 -6
  89. package/src/ast/format/deps.ts +94 -0
  90. package/{extensions/explore → src}/ast/format/discover.ts +2 -2
  91. package/{extensions/explore → src}/ast/format/impact.ts +9 -11
  92. package/{extensions/explore → src}/ast/format/outline.ts +2 -2
  93. package/{extensions/explore → src}/ast/format/relationships.ts +11 -7
  94. package/{extensions/explore → src}/ast/format/show.ts +1 -1
  95. package/src/ast/format/targeted-result.ts +20 -0
  96. package/src/ast/grammars/c_sharp.wasm +0 -0
  97. package/{extensions/explore → src}/ast/grammars/manifest.json +9 -12
  98. package/{extensions/explore → src}/ast/grammars/manifest.ts +0 -4
  99. package/{extensions/explore → src}/ast/graph/file-graph.ts +210 -98
  100. package/{extensions/explore → src}/ast/graph/relationships.ts +285 -72
  101. package/{extensions/explore → src}/ast/ir.ts +6 -0
  102. package/src/ast/languages/csharp-file-deps.ts +90 -0
  103. package/{extensions/explore → src}/ast/languages/csharp.ts +9 -40
  104. package/src/ast/languages/dotted-index.ts +131 -0
  105. package/{extensions/explore → src}/ast/languages/file-dep-util.ts +22 -13
  106. package/src/ast/languages/fixtures/sample.cs +53 -0
  107. package/src/ast/languages/fixtures/sample.cs.expected.json +156 -0
  108. package/src/ast/languages/fixtures/sample.go +37 -0
  109. package/src/ast/languages/fixtures/sample.go.expected.json +96 -0
  110. package/src/ast/languages/fixtures/sample.java +40 -0
  111. package/src/ast/languages/fixtures/sample.java.expected.json +126 -0
  112. package/src/ast/languages/fixtures/sample.kt +31 -0
  113. package/src/ast/languages/fixtures/sample.kt.expected.json +134 -0
  114. package/src/ast/languages/fixtures/sample.md +23 -0
  115. package/src/ast/languages/fixtures/sample.md.expected.json +44 -0
  116. package/src/ast/languages/fixtures/sample.rs +54 -0
  117. package/src/ast/languages/fixtures/sample.rs.expected.json +178 -0
  118. package/src/ast/languages/fixtures/sample.swift +45 -0
  119. package/src/ast/languages/fixtures/sample.swift.expected.json +147 -0
  120. package/src/ast/languages/fixtures/sample.ts +45 -0
  121. package/src/ast/languages/fixtures/sample.ts.expected.json +141 -0
  122. package/src/ast/languages/fixtures/sample.tsx +36 -0
  123. package/src/ast/languages/fixtures/sample.tsx.expected.json +59 -0
  124. package/{extensions/explore → src}/ast/languages/go-file-deps.ts +6 -0
  125. package/{extensions/explore → src}/ast/languages/go.ts +21 -3
  126. package/{extensions/explore → src}/ast/languages/java.ts +5 -38
  127. package/src/ast/languages/jvm-file-deps.ts +135 -0
  128. package/{extensions/explore → src}/ast/languages/kotlin.ts +5 -39
  129. package/{extensions/explore → src}/ast/languages/rust-file-deps.ts +3 -3
  130. package/{extensions/explore → src}/ast/languages/rust.ts +21 -13
  131. package/{extensions/explore → src}/ast/languages/swift-file-deps.ts +22 -15
  132. package/{extensions/explore → src}/ast/languages/swift.ts +9 -39
  133. package/{extensions/explore → src}/ast/languages/tree.ts +47 -0
  134. package/{extensions/explore → src}/ast/languages/typescript-file-deps.ts +108 -0
  135. package/{extensions/explore → src}/ast/languages/typescript-package-surface.ts +4 -3
  136. package/{extensions/explore → src}/ast/languages/typescript.ts +3 -5
  137. package/{extensions/explore → src}/ast/markdown.ts +72 -47
  138. package/{extensions/explore → src}/ast/queries/ast-search.ts +164 -146
  139. package/{extensions/explore → src}/ast/queries/composite-target.ts +1 -1
  140. package/{extensions/explore → src}/ast/queries/context.ts +116 -80
  141. package/{extensions/explore → src}/ast/queries/discover.ts +154 -105
  142. package/src/ast/queries/impact.ts +291 -0
  143. package/{extensions/explore → src}/ast/queries/outline.ts +4 -2
  144. package/{extensions/explore → src}/ast/queries/show.ts +46 -42
  145. package/{extensions/explore → src}/ast/query.ts +1 -1
  146. package/{extensions/explore → src}/ast/registry.ts +1 -2
  147. package/{extensions/explore → src}/ast/scan.ts +1 -1
  148. package/src/ast/session.ts +50 -0
  149. package/{extensions/explore → src}/ast/slice.ts +8 -0
  150. package/{extensions/explore → src/ast}/traverse.ts +32 -20
  151. package/src/file-injection/index.ts +547 -0
  152. package/src/image-generation/client.ts +50 -31
  153. package/src/image-generation/index.ts +177 -134
  154. package/src/index.ts +2 -0
  155. package/src/tool-loading/index.ts +58 -0
  156. package/extensions/context/evidence.ts +0 -517
  157. package/extensions/context/projection.ts +0 -141
  158. package/extensions/context/state.ts +0 -30
  159. package/extensions/context/write-scope.ts +0 -109
  160. package/extensions/explore/ast/format/deps.ts +0 -86
  161. package/extensions/explore/ast/grammars/odin.wasm +0 -0
  162. package/extensions/explore/ast/languages/csharp-file-deps.ts +0 -189
  163. package/extensions/explore/ast/languages/fixtures/sample.cs +0 -17
  164. package/extensions/explore/ast/languages/fixtures/sample.java +0 -21
  165. package/extensions/explore/ast/languages/fixtures/sample.kt +0 -15
  166. package/extensions/explore/ast/languages/fixtures/sample.odin +0 -28
  167. package/extensions/explore/ast/languages/fixtures/sample.rs +0 -31
  168. package/extensions/explore/ast/languages/fixtures/sample.swift +0 -25
  169. package/extensions/explore/ast/languages/jvm-file-deps.ts +0 -217
  170. package/extensions/explore/ast/languages/odin-file-deps.ts +0 -175
  171. package/extensions/explore/ast/languages/odin.ts +0 -407
  172. package/extensions/explore/ast/queries/impact.ts +0 -250
  173. package/extensions/explore/outline-injection.ts +0 -151
  174. package/extensions/explore/read/autoread.ts +0 -118
  175. package/extensions/working-memory/README.md +0 -17
  176. package/extensions/working-memory/checkpoint.ts +0 -242
  177. package/extensions/working-memory/index.ts +0 -341
  178. package/extensions/working-memory/memory.ts +0 -235
  179. package/extensions/working-memory/render.ts +0 -185
  180. package/extensions/working-memory/settings.ts +0 -38
  181. package/extensions/working-memory/state.ts +0 -158
  182. package/shared/autoread.ts +0 -234
  183. package/shared/context-messages.ts +0 -19
  184. package/shared/outline-injection.ts +0 -56
  185. /package/{extensions/explore → src}/ast/format/composite.ts +0 -0
  186. /package/{extensions/explore → src}/ast/grammars/kotlin.wasm +0 -0
  187. /package/{extensions/explore → src}/ast/grammars/swift.wasm +0 -0
  188. /package/{extensions/explore → src}/ast/identity.ts +0 -0
  189. /package/{extensions/explore → src}/ast/package-surface.ts +0 -0
  190. /package/{extensions/explore/ast/read/policy.ts → src/ast/read-policy.ts} +0 -0
@@ -1,109 +0,0 @@
1
- import { mkdir, readFile, unlink, writeFile } from "node:fs/promises";
2
- import { dirname, join } from "node:path";
3
- import type { GitRunner } from "../../shared/git.ts";
4
-
5
- export type OutsidePathSnapshot = Map<string, Buffer | null>;
6
-
7
- export function isContextCatalogPath(path: string): boolean {
8
- const normalized = path.replaceAll("\\", "/");
9
- return normalized === ".pi/contexts" || normalized.startsWith(".pi/contexts/");
10
- }
11
-
12
- export async function snapshotOutsideContext(git: GitRunner, root: string): Promise<OutsidePathSnapshot> {
13
- const snapshot: OutsidePathSnapshot = new Map();
14
- for (const path of await listDirtyPaths(git, root)) {
15
- if (isContextCatalogPath(path)) continue;
16
- snapshot.set(path, await readPathContent(root, path));
17
- }
18
- return snapshot;
19
- }
20
-
21
- export async function restoreOutsideContextMutations(
22
- git: GitRunner,
23
- root: string,
24
- before: OutsidePathSnapshot,
25
- ): Promise<string[]> {
26
- const afterDirty = await listDirtyPaths(git, root);
27
- const candidates = new Set([...before.keys(), ...afterDirty.filter((path) => !isContextCatalogPath(path))]);
28
- const violations: string[] = [];
29
- for (const path of [...candidates].sort((left, right) => left.localeCompare(right))) {
30
- if (isContextCatalogPath(path)) continue;
31
- const prior = before.has(path) ? before.get(path) : undefined;
32
- const current = await readPathContent(root, path);
33
- if (sameContent(prior, current)) continue;
34
- violations.push(path);
35
- await restorePath(git, root, path, prior);
36
- }
37
- return violations;
38
- }
39
-
40
- async function listDirtyPaths(git: GitRunner, root: string): Promise<string[]> {
41
- const raw = await git.run(["status", "--porcelain=v2", "-z", "--untracked-files=all"], { cwd: root });
42
- const parts = raw.split("\0").filter(Boolean);
43
- const paths = new Set<string>();
44
- for (let index = 0; index < parts.length; index++) {
45
- const record = parts[index];
46
- if (!record) continue;
47
- if (record[0] === "?") {
48
- paths.add(record.slice(2).replaceAll("\\", "/"));
49
- continue;
50
- }
51
- const fields = record.split(" ");
52
- if (record[0] === "1") {
53
- const path = fields.slice(8).join(" ").replaceAll("\\", "/");
54
- if (path) paths.add(path);
55
- } else if (record[0] === "2") {
56
- const path = fields.slice(9).join(" ").replaceAll("\\", "/");
57
- const oldPath = parts[index + 1]?.replaceAll("\\", "/");
58
- if (path) paths.add(path);
59
- if (oldPath) paths.add(oldPath);
60
- index++;
61
- } else if (record[0] === "u") {
62
- const path = fields.slice(10).join(" ").replaceAll("\\", "/");
63
- if (path) paths.add(path);
64
- }
65
- }
66
- return [...paths].sort((left, right) => left.localeCompare(right));
67
- }
68
-
69
- async function readPathContent(root: string, path: string): Promise<Buffer | null> {
70
- try {
71
- return await readFile(join(root, path));
72
- } catch {
73
- return null;
74
- }
75
- }
76
-
77
- function sameContent(left: Buffer | null | undefined, right: Buffer | null): boolean {
78
- if (left === undefined) return false;
79
- if (left === null || right === null) return left === right;
80
- return left.equals(right);
81
- }
82
-
83
- async function restorePath(
84
- git: GitRunner,
85
- root: string,
86
- path: string,
87
- prior: Buffer | null | undefined,
88
- ): Promise<void> {
89
- const absolute = join(root, path);
90
- if (prior === undefined) {
91
- // Was clean before the run: restore tracked paths from HEAD, delete new untracked paths.
92
- const tracked = await git.run(["ls-files", "--error-unmatch", "--", path], {
93
- cwd: root,
94
- optional: true,
95
- });
96
- if (tracked) {
97
- await git.run(["restore", "--worktree", "--source=HEAD", "--", path], { cwd: root });
98
- return;
99
- }
100
- await unlink(absolute).catch(() => {});
101
- return;
102
- }
103
- if (prior === null) {
104
- await unlink(absolute).catch(() => {});
105
- return;
106
- }
107
- await mkdir(dirname(absolute), { recursive: true });
108
- await writeFile(absolute, prior);
109
- }
@@ -1,86 +0,0 @@
1
- import type { FileDepHit, FileDepQueryResult } from "../graph/file-graph.ts";
2
- import { formatPathForDisplay } from "../../traverse.ts";
3
-
4
- function depthPrefix(depth: number, showDepth: boolean): string {
5
- return showDepth ? `d${depth} ` : "";
6
- }
7
-
8
- /** Factor shared directory prefixes into a small indent tree. */
9
- function formatInternalTree(
10
- paths: readonly { depth: number; path: string }[],
11
- cwd: string,
12
- showDepth: boolean,
13
- ): string[] {
14
- if (paths.length === 0) return [];
15
- const rows = paths.map((hit) => ({
16
- depth: hit.depth,
17
- display: formatPathForDisplay(hit.path, cwd),
18
- }));
19
-
20
- // Common directory prefix across displays (posix-ish split on /).
21
- const split = rows.map((row) => row.display.split("/"));
22
- let common = 0;
23
- const first = split[0];
24
- if (first !== undefined && split.length > 1) {
25
- while (common < first.length - 1) {
26
- const part = first[common];
27
- if (part === undefined) break;
28
- if (!split.every((parts) => parts[common] === part)) break;
29
- common += 1;
30
- }
31
- }
32
-
33
- const lines: string[] = [];
34
- if (common > 0 && first !== undefined) {
35
- lines.push(first.slice(0, common).join("/"));
36
- }
37
-
38
- for (let i = 0; i < rows.length; i += 1) {
39
- const row = rows[i];
40
- const parts = split[i];
41
- if (row === undefined || parts === undefined) continue;
42
- const leaf = common > 0 ? parts.slice(common).join("/") : row.display;
43
- const indent = common > 0 ? " " : "";
44
- lines.push(`${indent}${depthPrefix(row.depth, showDepth)}${leaf}`);
45
- }
46
- return lines;
47
- }
48
-
49
- export function formatDepsResult(result: FileDepQueryResult, cwd: string, emptyLabel: string): string {
50
- const showDepth = result.depth > 1;
51
- const internal = result.hits
52
- .filter((hit): hit is FileDepHit & { path: string } => hit.path !== undefined)
53
- .map((hit) => ({ depth: hit.depth, path: hit.path }));
54
- const external = result.hits
55
- .filter((hit): hit is FileDepHit & { externalId: string } => hit.externalId !== undefined)
56
- .map((hit) => ({ depth: hit.depth, id: hit.externalId }));
57
-
58
- if (internal.length === 0 && external.length === 0) {
59
- return emptyLabel;
60
- }
61
-
62
- const lines: string[] = [];
63
- lines.push(...formatInternalTree(internal, cwd, showDepth));
64
-
65
- if (external.length > 0) {
66
- if (lines.length > 0) lines.push("external:");
67
- else lines.push("external:");
68
- for (const hit of external) {
69
- lines.push(` ${depthPrefix(hit.depth, showDepth)}${hit.id}`);
70
- }
71
- }
72
-
73
- if (result.resultLimitReached) {
74
- lines.push(`omitted (result limit ${result.resultLimit})`);
75
- }
76
-
77
- return lines.join("\n");
78
- }
79
-
80
- export function formatDepsEmpty(): string {
81
- return "No file dependencies";
82
- }
83
-
84
- export function formatReverseDepsEmpty(): string {
85
- return "No reverse file dependencies";
86
- }
@@ -1,189 +0,0 @@
1
- import { readFile } from "node:fs/promises";
2
- import { relative, resolve, sep } from "node:path";
3
- import type { FileDepHost, FileDepResolver } from "../adapter.ts";
4
- import { collectFilesWithExtensions, externalId, internalPaths, isWithin } from "./file-dep-util.ts";
5
-
6
- /**
7
- * C# file deps — ast-bro style.
8
- *
9
- * Index path suffixes + `namespace.Type` FQNs. Each `using` resolves to at most
10
- * one internal file (`pickClosest` on ties). Bare namespace usings that don't
11
- * match a type/path become external ids — never "every file in the namespace".
12
- */
13
-
14
- type CsharpIndex = {
15
- /** slash-key → absolute paths */
16
- bySuffix: Map<string, string[]>;
17
- };
18
-
19
- /** BCL / engine — never treat polyfills as project file edges. */
20
- function isFrameworkNamespace(ns: string): boolean {
21
- if (ns === "System" || ns.startsWith("System.")) return true;
22
- if (ns === "Microsoft" || ns.startsWith("Microsoft.")) return true;
23
- if (ns === "Windows" || ns.startsWith("Windows.")) return true;
24
- if (ns === "UnityEngine" || ns.startsWith("UnityEngine.")) return true;
25
- if (ns === "UnityEditor" || ns.startsWith("UnityEditor.")) return true;
26
- if (ns === "Godot" || ns.startsWith("Godot.")) return true;
27
- return false;
28
- }
29
-
30
- function normalizeUsing(specifier: string): string {
31
- let s = specifier.trim().replace(/;$/u, "").trim();
32
- s = s.replace(/^global\s+/iu, "").trim();
33
- s = s.replace(/^static\s+/iu, "").trim();
34
- // Alias: using A = X.Y.Z → resolve the target.
35
- const eq = s.indexOf("=");
36
- if (eq !== -1) s = s.slice(eq + 1).trim();
37
- return s;
38
- }
39
-
40
- const TYPE_KEYWORDS = ["class ", "struct ", "interface ", "record ", "enum ", "delegate "] as const;
41
-
42
- /**
43
- * Scan source for namespace + type pairs (sequential — types bind to the
44
- * nearest preceding namespace declaration).
45
- */
46
- function typesByNamespace(head: string): { ns: string; typeName: string }[] {
47
- const out: { ns: string; typeName: string }[] = [];
48
- let currentNs: string | undefined;
49
- for (const raw of head.split("\n")) {
50
- const line = raw.trim();
51
- if (line.length === 0 || line.startsWith("//")) continue;
52
-
53
- // file-scoped `namespace X;`, block `namespace X {`, or `namespace X` + brace next line
54
- const nsMatch =
55
- /^(?:internal\s+)?namespace\s+([A-Za-z_][\w.]*)\s*[;{]?\s*$/u.exec(line) ??
56
- /^(?:internal\s+)?namespace\s+([A-Za-z_][\w.]*)\s*[{/]/u.exec(line);
57
- if (nsMatch?.[1] !== undefined) {
58
- currentNs = nsMatch[1];
59
- continue;
60
- }
61
- if (currentNs === undefined) continue;
62
-
63
- for (const kw of TYPE_KEYWORDS) {
64
- const idx = line.indexOf(kw);
65
- if (idx === -1) continue;
66
- if (idx > 0 && /[A-Za-z0-9_]/u.test(line.charAt(idx - 1))) continue;
67
- const rest = line.slice(idx + kw.length).trimStart();
68
- const name = /^([A-Za-z_][\w]*)/u.exec(rest)?.[1];
69
- if (name !== undefined) out.push({ ns: currentNs, typeName: name });
70
- break;
71
- }
72
- }
73
- return out;
74
- }
75
-
76
- function pushSuffix(map: Map<string, string[]>, key: string, file: string): void {
77
- if (key.length === 0) return;
78
- const list = map.get(key);
79
- if (list === undefined) map.set(key, [file]);
80
- else if (!list.includes(file)) list.push(file);
81
- }
82
-
83
- /** Index every slash-suffix of a stem path: a/b/c → a/b/c, b/c, c */
84
- function indexPathSuffixes(map: Map<string, string[]>, file: string, stemRelPosix: string): void {
85
- const parts = stemRelPosix.split("/").filter((part) => part.length > 0);
86
- for (let i = 0; i < parts.length; i += 1) {
87
- pushSuffix(map, parts.slice(i).join("/"), file);
88
- }
89
- }
90
-
91
- function toPosixRel(scopeRoot: string, file: string): string | undefined {
92
- const rel = relative(resolve(scopeRoot), resolve(file));
93
- if (rel.startsWith(`..${sep}`) || rel === ".." || rel.length === 0) return undefined;
94
- return rel.split(sep).join("/");
95
- }
96
-
97
- async function buildIndex(host: FileDepHost, signal: AbortSignal): Promise<CsharpIndex> {
98
- const key = `csharp-suffix:${host.scopeRoot}`;
99
- const cached = host.memo.get(key);
100
- if (cached !== undefined) return cached as CsharpIndex;
101
-
102
- const bySuffix = new Map<string, string[]>();
103
- const files = await collectFilesWithExtensions(host, host.scopeRoot, [".cs"], signal);
104
-
105
- for (const file of files) {
106
- signal.throwIfAborted();
107
- const rel = toPosixRel(host.scopeRoot, file);
108
- if (rel === undefined) continue;
109
-
110
- const dot = rel.lastIndexOf(".");
111
- const stem = dot >= 0 ? rel.slice(0, dot) : rel;
112
- indexPathSuffixes(bySuffix, file, stem);
113
-
114
- let text: string;
115
- try {
116
- text = await readFile(file, "utf8");
117
- } catch {
118
- continue;
119
- }
120
- const head = text.slice(0, 12000);
121
- // FQN only — bare type names are too ambiguous (Platform, View, …).
122
- for (const { ns, typeName } of typesByNamespace(head)) {
123
- const fqn = `${ns}.${typeName}`.replace(/\./gu, "/");
124
- pushSuffix(bySuffix, fqn, file);
125
- }
126
- }
127
-
128
- for (const [, list] of bySuffix) list.sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));
129
- const index: CsharpIndex = { bySuffix };
130
- host.memo.set(key, index);
131
- return index;
132
- }
133
-
134
- /** Prefer the candidate sharing the most path prefix with the importer. */
135
- function pickClosest(candidates: readonly string[], fromPath: string): string | undefined {
136
- if (candidates.length === 0) return undefined;
137
- if (candidates.length === 1) return candidates[0];
138
-
139
- const fromSegs = resolve(fromPath).split(sep);
140
- let best: string | undefined;
141
- let bestCommon = -1;
142
- for (const candidate of candidates) {
143
- const segs = resolve(candidate).split(sep);
144
- let common = 0;
145
- const n = Math.min(fromSegs.length, segs.length);
146
- while (common < n && fromSegs[common] === segs[common]) common += 1;
147
- if (best === undefined || common > bestCommon || (common === bestCommon && candidate < best)) {
148
- best = candidate;
149
- bestCommon = common;
150
- }
151
- }
152
- return best;
153
- }
154
-
155
- function lookup(index: CsharpIndex, key: string): readonly string[] | undefined {
156
- const list = index.bySuffix.get(key);
157
- if (list === undefined || list.length === 0) return undefined;
158
- return list;
159
- }
160
-
161
- function resolveKey(index: CsharpIndex, fromPath: string, dotted: string): string | undefined {
162
- const key = dotted.replace(/\./gu, "/");
163
- const hit = pickClosest(lookup(index, key) ?? [], fromPath);
164
- if (hit !== undefined) return hit;
165
-
166
- // Nested type: Foo.Bar.Inner → try Foo.Bar file.
167
- const slash = key.lastIndexOf("/");
168
- if (slash <= 0) return undefined;
169
- const parent = key.slice(0, slash);
170
- return pickClosest(lookup(index, parent) ?? [], fromPath);
171
- }
172
-
173
- export const resolveCsharpFileDep: FileDepResolver = async (fromPath, specifier, host, signal) => {
174
- signal.throwIfAborted();
175
- const dotted = normalizeUsing(specifier);
176
- if (dotted.length === 0) return { kind: "unresolved" };
177
-
178
- if (isFrameworkNamespace(dotted)) return externalId(dotted);
179
-
180
- const index = await buildIndex(host, signal);
181
- const file = resolveKey(index, fromPath, dotted);
182
- if (file === undefined) return externalId(dotted);
183
- if (!isWithin(host.scopeRoot, file) && resolve(file) !== resolve(host.scopeRoot)) {
184
- return externalId(dotted);
185
- }
186
- if (!host.ownsPath(file)) return externalId(dotted);
187
- if (resolve(file) === resolve(fromPath)) return { kind: "unresolved" };
188
- return internalPaths([file]);
189
- };
@@ -1,17 +0,0 @@
1
- using System;
2
-
3
- namespace Demo;
4
-
5
- /// <summary>Greeter</summary>
6
- public class Greeter {
7
- private int count;
8
- public string Name { get; set; }
9
- public Greeter() {}
10
- public void Hello() {}
11
- public event EventHandler Done;
12
- }
13
-
14
- internal struct Box {}
15
- public interface IGreeter { void Hello(); }
16
- public enum Color { Red, Green }
17
- public record Person(string Name);
@@ -1,21 +0,0 @@
1
- package com.demo;
2
-
3
- import java.util.List;
4
-
5
- /** Greeter type. */
6
- public class Greeter {
7
- private int count;
8
- public Greeter() {}
9
- public void hello() {}
10
- public static final int MAX = 10;
11
- }
12
-
13
- interface Named {
14
- void name();
15
- }
16
-
17
- enum Color {
18
- RED,
19
- GREEN;
20
- void paint() {}
21
- }
@@ -1,15 +0,0 @@
1
- package com.demo
2
-
3
- /** Greeter */
4
- class Greeter(val name: String) {
5
- fun hello() {}
6
- companion object {
7
- fun create() = Greeter("x")
8
- }
9
- }
10
-
11
- internal fun top() {}
12
-
13
- object Singleton
14
-
15
- enum class Color { RED, GREEN }
@@ -1,28 +0,0 @@
1
- package demo
2
-
3
- import "core:fmt"
4
-
5
- // Add two ints
6
- add :: proc(a: int, b: int) -> int {
7
- return a + b
8
- }
9
-
10
- Point :: struct {
11
- x: int,
12
- y: int,
13
- }
14
-
15
- Color :: enum {
16
- Red,
17
- Green,
18
- }
19
-
20
- Value :: union {
21
- int,
22
- f32,
23
- }
24
-
25
- PI :: 3.14
26
-
27
- @(private)
28
- secret :: proc() {}
@@ -1,31 +0,0 @@
1
- /// Point in 2D.
2
- #[derive(Debug, Clone)]
3
- pub struct Point {
4
- pub x: i32,
5
- y: i32,
6
- }
7
-
8
- /// Drawable.
9
- pub trait Draw {
10
- fn draw(&self);
11
- }
12
-
13
- impl Point {
14
- /// Origin.
15
- pub fn origin() -> Self {
16
- Self { x: 0, y: 0 }
17
- }
18
- }
19
-
20
- impl Draw for Point {
21
- fn draw(&self) {}
22
- }
23
-
24
- pub mod nested {
25
- pub fn inner() {}
26
- }
27
-
28
- pub const MAX: i32 = 10;
29
- pub type Alias = i32;
30
-
31
- fn private_fn() {}
@@ -1,25 +0,0 @@
1
- import Foundation
2
-
3
- /// Greeter type
4
- public class Greeter {
5
- public var name: String = ""
6
- public init() {}
7
- public func hello() {}
8
- }
9
-
10
- extension Greeter {
11
- public func shout() {}
12
- }
13
-
14
- public protocol Named {
15
- func name()
16
- }
17
-
18
- internal struct Box {
19
- var value: Int
20
- }
21
-
22
- private enum Color {
23
- case red
24
- case green
25
- }