@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
|
@@ -11,16 +11,14 @@ import {
|
|
|
11
11
|
extensionPathsForTools,
|
|
12
12
|
runSubagentTurn,
|
|
13
13
|
} from "../subagent/run.ts";
|
|
14
|
-
import { collectSyncEvidence, CONTEXT_SYNC_EVIDENCE_TOOL } from "./evidence.ts";
|
|
15
14
|
import { pathExists } from "./definitions.ts";
|
|
16
15
|
import contextSettings from "./settings.ts";
|
|
17
|
-
import { formatContextValidationFailure, validateContextCatalog } from "./validation.ts";
|
|
18
|
-
import { restoreOutsideContextMutations, snapshotOutsideContext } from "./write-scope.ts";
|
|
16
|
+
import { formatContextValidationFailure, listEligibleDirtyPaths, validateContextCatalog } from "./validation.ts";
|
|
19
17
|
|
|
20
18
|
const CONTEXT_SYNC_AGENT = "context-sync";
|
|
21
19
|
|
|
22
20
|
export interface ContextSyncDetails {
|
|
23
|
-
outcome: "applied" | "no-change" | "failed";
|
|
21
|
+
outcome: "applied" | "no-change" | "failed" | "cancelled";
|
|
24
22
|
summary: string;
|
|
25
23
|
reason: string;
|
|
26
24
|
changedContextFiles: string[];
|
|
@@ -28,53 +26,80 @@ export interface ContextSyncDetails {
|
|
|
28
26
|
validationFailure?: string;
|
|
29
27
|
}
|
|
30
28
|
|
|
29
|
+
type SyncStatus = (status: string) => void | Promise<void>;
|
|
30
|
+
type GitRunner = ReturnType<typeof createGitRunner>;
|
|
31
|
+
|
|
31
32
|
let syncQueue = Promise.resolve();
|
|
32
33
|
|
|
33
34
|
export async function runContextSync(
|
|
34
35
|
pi: ExtensionAPI,
|
|
35
36
|
ctx: ExtensionContext,
|
|
36
|
-
options: { nudge?: string; signal?: AbortSignal; onStatus?:
|
|
37
|
+
options: { nudge?: string; signal?: AbortSignal; onStatus?: SyncStatus } = {},
|
|
37
38
|
): Promise<ContextSyncDetails> {
|
|
38
39
|
return withSyncLock(() => runContextSyncLocked(pi, ctx, options));
|
|
39
40
|
}
|
|
40
41
|
|
|
41
|
-
|
|
42
|
+
function noChangeResult(reason: string, agentResponse?: string): ContextSyncDetails {
|
|
43
|
+
return {
|
|
44
|
+
outcome: "no-change",
|
|
45
|
+
summary: "Existing context mappings already fit the changed scope.",
|
|
46
|
+
reason,
|
|
47
|
+
changedContextFiles: [],
|
|
48
|
+
agentResponse,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async function inspectSyncNeed(
|
|
42
53
|
pi: ExtensionAPI,
|
|
43
54
|
ctx: ExtensionContext,
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
55
|
+
nudge: string | undefined,
|
|
56
|
+
onStatus?: SyncStatus,
|
|
57
|
+
): Promise<
|
|
58
|
+
| { kind: "done"; result: ContextSyncDetails }
|
|
59
|
+
| { kind: "run"; git: GitRunner; root: string; ignoreGlobs: readonly string[] }
|
|
60
|
+
> {
|
|
61
|
+
await onStatus?.("Inspecting repository context");
|
|
48
62
|
const git = createGitRunner(pi, ctx);
|
|
49
63
|
const status = await loadRepoStatus(git);
|
|
50
64
|
if (!status) throw new Error("No Git repository found");
|
|
51
65
|
const settings = await loadTauExtensionSettings(ctx, contextSettings);
|
|
52
|
-
const
|
|
53
|
-
|
|
66
|
+
const ignoreGlobs = settings.validation.ignoreGlobs;
|
|
67
|
+
const validation = await validateContextCatalog(git, status.root, ignoreGlobs);
|
|
68
|
+
const dirtyEligible = await listEligibleDirtyPaths(git, status.root, ignoreGlobs);
|
|
69
|
+
const force = Boolean(nudge?.trim());
|
|
70
|
+
if (!force && validation.stale.length === 0 && validation.uncovered.length === 0 && dirtyEligible.length === 0) {
|
|
54
71
|
return {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
reason: "Changed files are outside context catalog scope.",
|
|
58
|
-
changedContextFiles: [],
|
|
72
|
+
kind: "done",
|
|
73
|
+
result: noChangeResult("No eligible dirty files, stale catalog paths, or sync nudge."),
|
|
59
74
|
};
|
|
75
|
+
}
|
|
76
|
+
return { kind: "run", git, root: status.root, ignoreGlobs };
|
|
77
|
+
}
|
|
60
78
|
|
|
61
|
-
|
|
62
|
-
const outsideBefore = await snapshotOutsideContext(git, status.root);
|
|
79
|
+
async function resolveContextSyncDefinition(ctx: ExtensionContext) {
|
|
63
80
|
const discovery = await discoverAgents(ctx.cwd, ctx.isProjectTrusted());
|
|
64
81
|
const definition = discovery.agents.get(CONTEXT_SYNC_AGENT);
|
|
65
|
-
if (
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
const task = buildContextSyncTask(status.root, options.nudge);
|
|
75
|
-
const signal = options.signal ?? ctx.signal ?? new AbortController().signal;
|
|
82
|
+
if (definition) return definition;
|
|
83
|
+
const reason =
|
|
84
|
+
discovery.invalid
|
|
85
|
+
.get(CONTEXT_SYNC_AGENT)
|
|
86
|
+
?.map((item) => item.reason)
|
|
87
|
+
.join("; ") ?? "unknown agent";
|
|
88
|
+
throw new Error(`Context sync agent unavailable: ${reason}`);
|
|
89
|
+
}
|
|
76
90
|
|
|
77
|
-
|
|
91
|
+
async function runContextSyncAgent(
|
|
92
|
+
pi: ExtensionAPI,
|
|
93
|
+
ctx: ExtensionContext,
|
|
94
|
+
root: string,
|
|
95
|
+
nudge: string | undefined,
|
|
96
|
+
signal: AbortSignal,
|
|
97
|
+
onStatus: SyncStatus | undefined,
|
|
98
|
+
beforeCatalog: Map<string, string>,
|
|
99
|
+
): Promise<ContextSyncDetails | { agentResponse: string }> {
|
|
100
|
+
const definition = await resolveContextSyncDefinition(ctx);
|
|
101
|
+
const task = buildContextSyncTask(root, nudge);
|
|
102
|
+
await onStatus?.("Running context-sync subagent");
|
|
78
103
|
const thread = await createSubagentThread({
|
|
79
104
|
id: `context-sync-${Date.now()}`,
|
|
80
105
|
displayName: definition.names[0] ?? definition.name,
|
|
@@ -88,7 +113,6 @@ async function runContextSyncLocked(
|
|
|
88
113
|
ctx.ui.notify(`Context sync agent: ${warning}`, "warning");
|
|
89
114
|
},
|
|
90
115
|
});
|
|
91
|
-
let agentResponse = "";
|
|
92
116
|
try {
|
|
93
117
|
const result = await runSubagentTurn({
|
|
94
118
|
thread,
|
|
@@ -97,49 +121,36 @@ async function runContextSyncLocked(
|
|
|
97
121
|
signal,
|
|
98
122
|
onUpdate: async (details) => {
|
|
99
123
|
const activity = details.currentActivity ?? details.response;
|
|
100
|
-
if (activity) await
|
|
124
|
+
if (activity) await onStatus?.(activity.slice(0, 160));
|
|
101
125
|
},
|
|
102
126
|
});
|
|
103
|
-
|
|
127
|
+
if (signal.aborted || result.details.status === "aborted") return cancelledResult(result.content);
|
|
104
128
|
if (result.details.status !== "completed") {
|
|
105
|
-
const writeScopeViolations = await restoreOutsideContextMutations(git, status.root, outsideBefore);
|
|
106
129
|
return {
|
|
107
130
|
outcome: "failed",
|
|
108
131
|
summary: "Context sync subagent failed.",
|
|
109
|
-
reason:
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
? `Restored out-of-scope writes:\n${writeScopeViolations.map((path) => `- ${path}`).join("\n")}`
|
|
113
|
-
: undefined,
|
|
114
|
-
]
|
|
115
|
-
.filter((line): line is string => Boolean(line))
|
|
116
|
-
.join("\n\n"),
|
|
117
|
-
changedContextFiles: changedCatalogPaths(beforeCatalog, await catalogFileSnapshot(status.root)),
|
|
118
|
-
agentResponse,
|
|
132
|
+
reason: result.details.error ?? result.content,
|
|
133
|
+
changedContextFiles: changedCatalogPaths(beforeCatalog, await catalogFileSnapshot(root)),
|
|
134
|
+
agentResponse: result.content,
|
|
119
135
|
};
|
|
120
136
|
}
|
|
137
|
+
return { agentResponse: result.content };
|
|
121
138
|
} finally {
|
|
122
139
|
await disposeSubagentThread(thread);
|
|
123
140
|
}
|
|
141
|
+
}
|
|
124
142
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
};
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
await options.onStatus?.("Verifying context catalog");
|
|
140
|
-
const validationFailure = formatContextValidationFailure(
|
|
141
|
-
await validateContextCatalog(git, status.root, settings.validation.ignoreGlobs),
|
|
142
|
-
);
|
|
143
|
+
async function finalizeContextSync(
|
|
144
|
+
git: GitRunner,
|
|
145
|
+
root: string,
|
|
146
|
+
ignoreGlobs: readonly string[],
|
|
147
|
+
beforeCatalog: Map<string, string>,
|
|
148
|
+
agentResponse: string,
|
|
149
|
+
onStatus?: SyncStatus,
|
|
150
|
+
): Promise<ContextSyncDetails> {
|
|
151
|
+
const changedContextFiles = changedCatalogPaths(beforeCatalog, await catalogFileSnapshot(root));
|
|
152
|
+
await onStatus?.("Verifying context catalog");
|
|
153
|
+
const validationFailure = formatContextValidationFailure(await validateContextCatalog(git, root, ignoreGlobs));
|
|
143
154
|
if (validationFailure) {
|
|
144
155
|
return {
|
|
145
156
|
outcome: "failed",
|
|
@@ -151,13 +162,7 @@ async function runContextSyncLocked(
|
|
|
151
162
|
};
|
|
152
163
|
}
|
|
153
164
|
if (changedContextFiles.length === 0) {
|
|
154
|
-
return
|
|
155
|
-
outcome: "no-change",
|
|
156
|
-
summary: "Existing context mappings already fit the changed scope.",
|
|
157
|
-
reason: agentResponse.trim() || "Context-sync subagent made no catalog edits.",
|
|
158
|
-
changedContextFiles: [],
|
|
159
|
-
agentResponse,
|
|
160
|
-
};
|
|
165
|
+
return noChangeResult(agentResponse.trim() || "Context-sync subagent made no catalog edits.", agentResponse);
|
|
161
166
|
}
|
|
162
167
|
return {
|
|
163
168
|
outcome: "applied",
|
|
@@ -168,13 +173,59 @@ async function runContextSyncLocked(
|
|
|
168
173
|
};
|
|
169
174
|
}
|
|
170
175
|
|
|
171
|
-
|
|
176
|
+
async function runContextSyncLocked(
|
|
177
|
+
pi: ExtensionAPI,
|
|
178
|
+
ctx: ExtensionContext,
|
|
179
|
+
options: { nudge?: string; signal?: AbortSignal; onStatus?: SyncStatus },
|
|
180
|
+
): Promise<ContextSyncDetails> {
|
|
181
|
+
if (!ctx.isProjectTrusted()) throw new Error("Context sync requires a trusted project");
|
|
182
|
+
const signal = options.signal ?? ctx.signal ?? new AbortController().signal;
|
|
183
|
+
if (signal.aborted) return cancelledResult();
|
|
184
|
+
|
|
185
|
+
const need = await inspectSyncNeed(pi, ctx, options.nudge, options.onStatus);
|
|
186
|
+
if (need.kind === "done") return need.result;
|
|
187
|
+
if (signal.aborted) return cancelledResult();
|
|
188
|
+
|
|
189
|
+
const beforeCatalog = await catalogFileSnapshot(need.root);
|
|
190
|
+
const agentResult = await runContextSyncAgent(
|
|
191
|
+
pi,
|
|
192
|
+
ctx,
|
|
193
|
+
need.root,
|
|
194
|
+
options.nudge,
|
|
195
|
+
signal,
|
|
196
|
+
options.onStatus,
|
|
197
|
+
beforeCatalog,
|
|
198
|
+
);
|
|
199
|
+
if ("outcome" in agentResult) return agentResult;
|
|
200
|
+
if (signal.aborted) return cancelledResult(agentResult.agentResponse);
|
|
201
|
+
return finalizeContextSync(
|
|
202
|
+
need.git,
|
|
203
|
+
need.root,
|
|
204
|
+
need.ignoreGlobs,
|
|
205
|
+
beforeCatalog,
|
|
206
|
+
agentResult.agentResponse,
|
|
207
|
+
options.onStatus,
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function cancelledResult(agentResponse?: string): ContextSyncDetails {
|
|
212
|
+
return {
|
|
213
|
+
outcome: "cancelled",
|
|
214
|
+
summary: "Context sync cancelled",
|
|
215
|
+
reason: agentResponse?.trim() || "Cancelled by user.",
|
|
216
|
+
changedContextFiles: [],
|
|
217
|
+
agentResponse,
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function buildContextSyncTask(root: string, nudge?: string): string {
|
|
172
222
|
const trimmed = nudge?.trim();
|
|
173
223
|
return [
|
|
174
|
-
`
|
|
175
|
-
"
|
|
176
|
-
"
|
|
177
|
-
|
|
224
|
+
`Keep the repository context catalog at ${root} honest.`,
|
|
225
|
+
"Job: update `.pi/contexts` so work packs match the codebase.",
|
|
226
|
+
"Use normal repo tools. Prefer patch for catalog edits under `.pi/contexts`.",
|
|
227
|
+
"Walk domain → concept → job entry before placing paths. Harness verifies catalog coverage after you finish.",
|
|
228
|
+
trimmed ? `Human nudge (soft steer, does not skip the ladder or coverage):\n${trimmed}` : undefined,
|
|
178
229
|
]
|
|
179
230
|
.filter((line): line is string => Boolean(line))
|
|
180
231
|
.join("\n\n");
|
|
@@ -198,18 +249,20 @@ async function catalogFileSnapshot(root: string): Promise<Map<string, string>> {
|
|
|
198
249
|
const snapshot = new Map<string, string>();
|
|
199
250
|
const base = join(root, ".pi", "contexts");
|
|
200
251
|
if (!(await pathExists(base))) return snapshot;
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
252
|
+
const stack = [base];
|
|
253
|
+
while (stack.length > 0) {
|
|
254
|
+
const directory = stack.pop();
|
|
255
|
+
if (!directory) continue;
|
|
256
|
+
for (const entry of await readdir(directory, { withFileTypes: true })) {
|
|
257
|
+
const fullPath = join(directory, entry.name);
|
|
258
|
+
if (entry.isDirectory()) {
|
|
259
|
+
stack.push(fullPath);
|
|
260
|
+
continue;
|
|
261
|
+
}
|
|
262
|
+
if (!entry.isFile() || extname(entry.name) !== ".toml") continue;
|
|
263
|
+
const relativePath = relative(root, fullPath).split(sep).join("/");
|
|
264
|
+
const content = await readFile(fullPath, "utf8");
|
|
265
|
+
snapshot.set(relativePath, createHash("sha256").update(content).digest("hex"));
|
|
213
266
|
}
|
|
214
267
|
}
|
|
215
268
|
return snapshot;
|
|
@@ -221,6 +274,3 @@ function changedCatalogPaths(before: Map<string, string>, after: Map<string, str
|
|
|
221
274
|
.filter((path) => before.get(path) !== after.get(path))
|
|
222
275
|
.sort((left, right) => left.localeCompare(right));
|
|
223
276
|
}
|
|
224
|
-
|
|
225
|
-
// Ensure evidence tool name stays aligned with agent definition tooling.
|
|
226
|
-
export const CONTEXT_SYNC_REQUIRED_TOOLS = ["read", "bash", "patch", CONTEXT_SYNC_EVIDENCE_TOOL] as const;
|
|
@@ -8,6 +8,17 @@ export interface ContextValidationResult {
|
|
|
8
8
|
uncovered: string[];
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
+
export async function listEligibleDirtyPaths(
|
|
12
|
+
git: GitRunner,
|
|
13
|
+
root: string,
|
|
14
|
+
ignoreGlobs: readonly string[],
|
|
15
|
+
): Promise<string[]> {
|
|
16
|
+
const status = await git.run(["status", "--porcelain=v2", "-z", "--untracked-files=all"], { cwd: root });
|
|
17
|
+
return [...parseDirtyPaths(status)]
|
|
18
|
+
.filter((path) => !isSensitiveContextPath(path) && isContextEligiblePath(path, ignoreGlobs))
|
|
19
|
+
.sort((left, right) => left.localeCompare(right));
|
|
20
|
+
}
|
|
21
|
+
|
|
11
22
|
export async function validateContextCatalog(
|
|
12
23
|
git: GitRunner,
|
|
13
24
|
root: string,
|
|
@@ -28,31 +39,8 @@ export async function validateContextCatalog(
|
|
|
28
39
|
}
|
|
29
40
|
}
|
|
30
41
|
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
const dirty = new Set<string>();
|
|
34
|
-
for (let index = 0; index < records.length; index++) {
|
|
35
|
-
const record = records[index];
|
|
36
|
-
if (!record) continue;
|
|
37
|
-
if (record.startsWith("? ")) {
|
|
38
|
-
dirty.add(record.slice(2).replaceAll("\\", "/"));
|
|
39
|
-
continue;
|
|
40
|
-
}
|
|
41
|
-
const fields = record.split(" ");
|
|
42
|
-
const state = fields[1] ?? "..";
|
|
43
|
-
if (record.startsWith("1 ") && !state.includes("D")) {
|
|
44
|
-
dirty.add(fields.slice(8).join(" ").replaceAll("\\", "/"));
|
|
45
|
-
} else if (record.startsWith("2 ")) {
|
|
46
|
-
if (!state.includes("D")) dirty.add(fields.slice(9).join(" ").replaceAll("\\", "/"));
|
|
47
|
-
index += 1;
|
|
48
|
-
} else if (record.startsWith("u ")) {
|
|
49
|
-
dirty.add(fields.slice(10).join(" ").replaceAll("\\", "/"));
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
const uncovered = [...dirty]
|
|
53
|
-
.filter(
|
|
54
|
-
(path) => !isSensitiveContextPath(path) && isContextEligiblePath(path, ignoreGlobs) && !memberships.has(path),
|
|
55
|
-
)
|
|
42
|
+
const uncovered = (await listEligibleDirtyPaths(git, root, ignoreGlobs))
|
|
43
|
+
.filter((path) => !memberships.has(path))
|
|
56
44
|
.sort((left, right) => left.localeCompare(right));
|
|
57
45
|
return { stale: stale.sort((left, right) => left.path.localeCompare(right.path)), uncovered };
|
|
58
46
|
}
|
|
@@ -74,3 +62,27 @@ export function formatContextValidationFailure(result: ContextValidationResult):
|
|
|
74
62
|
);
|
|
75
63
|
return output.join("\n");
|
|
76
64
|
}
|
|
65
|
+
|
|
66
|
+
function parseDirtyPaths(status: string): Set<string> {
|
|
67
|
+
const records = status.split("\0");
|
|
68
|
+
const dirty = new Set<string>();
|
|
69
|
+
for (let index = 0; index < records.length; index++) {
|
|
70
|
+
const record = records[index];
|
|
71
|
+
if (!record) continue;
|
|
72
|
+
if (record.startsWith("? ")) {
|
|
73
|
+
dirty.add(record.slice(2).replaceAll("\\", "/"));
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
const fields = record.split(" ");
|
|
77
|
+
const state = fields[1] ?? "..";
|
|
78
|
+
if (record.startsWith("1 ") && !state.includes("D")) {
|
|
79
|
+
dirty.add(fields.slice(8).join(" ").replaceAll("\\", "/"));
|
|
80
|
+
} else if (record.startsWith("2 ")) {
|
|
81
|
+
if (!state.includes("D")) dirty.add(fields.slice(9).join(" ").replaceAll("\\", "/"));
|
|
82
|
+
index += 1;
|
|
83
|
+
} else if (record.startsWith("u ")) {
|
|
84
|
+
dirty.add(fields.slice(10).join(" ").replaceAll("\\", "/"));
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return dirty;
|
|
88
|
+
}
|
|
@@ -2,6 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
Switches active chat model and thinking level as one effort tier. Available providers come from current logins, and each provider can fall back through its ranked models.
|
|
4
4
|
|
|
5
|
-
Run `/effort` to choose a tier and provider. `/effort
|
|
5
|
+
Run `/effort` to choose a tier and provider. `/effort quick`, `/effort standard`, and `/effort deep` skip the tier prompt. Press `Ctrl+Shift+E` to cycle tiers on current provider.
|
|
6
6
|
|
|
7
7
|
If selected provider has no usable model for tier, current model stays active. Footer shows an effort label whenever current provider, model, and thinking level match a configured tier; otherwise it shows no label.
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
} from "../../shared/model-effort.ts";
|
|
10
10
|
import { EFFORT_STATE_TYPE, effortState, nextEffort } from "./state.ts";
|
|
11
11
|
|
|
12
|
-
const EFFORTS: readonly ModelEffort[] = ["
|
|
12
|
+
const EFFORTS: readonly ModelEffort[] = ["quick", "standard", "deep"];
|
|
13
13
|
|
|
14
14
|
export default function effortExtension(pi: ExtensionAPI): void {
|
|
15
15
|
let activeEffort: ModelEffort | undefined;
|
|
@@ -81,7 +81,7 @@ export default function effortExtension(pi: ExtensionAPI): void {
|
|
|
81
81
|
}
|
|
82
82
|
const arg = args.trim().toLowerCase();
|
|
83
83
|
if (arg && !isModelEffort(arg)) {
|
|
84
|
-
ctx.ui.notify("Usage: /effort [
|
|
84
|
+
ctx.ui.notify("Usage: /effort [quick|standard|deep]", "error");
|
|
85
85
|
return;
|
|
86
86
|
}
|
|
87
87
|
let effort: ModelEffort | undefined = isModelEffort(arg) ? arg : undefined;
|
|
@@ -130,5 +130,5 @@ export default function effortExtension(pi: ExtensionAPI): void {
|
|
|
130
130
|
}
|
|
131
131
|
|
|
132
132
|
function isModelEffort(value: string): value is ModelEffort {
|
|
133
|
-
return value === "
|
|
133
|
+
return value === "quick" || value === "standard" || value === "deep";
|
|
134
134
|
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { SessionEntry } from "@earendil-works/pi-coding-agent";
|
|
2
1
|
import type { ModelEffort } from "../../shared/model-effort.ts";
|
|
3
2
|
|
|
4
3
|
export const EFFORT_STATE_TYPE = "tau.model-effort.state";
|
|
@@ -12,22 +11,8 @@ export function effortState(effort: ModelEffort | undefined): EffortStateV1 {
|
|
|
12
11
|
return { v: 1, effort: effort ?? null };
|
|
13
12
|
}
|
|
14
13
|
|
|
15
|
-
export function replayEffortState(branch: readonly SessionEntry[]): ModelEffort | undefined {
|
|
16
|
-
for (let index = branch.length - 1; index >= 0; index -= 1) {
|
|
17
|
-
const entry = branch[index];
|
|
18
|
-
if (entry?.type !== "custom" || entry.customType !== EFFORT_STATE_TYPE) continue;
|
|
19
|
-
const data = entry.data;
|
|
20
|
-
if (!data || typeof data !== "object") continue;
|
|
21
|
-
const value = data as Record<string, unknown>;
|
|
22
|
-
if (value.v !== 1) continue;
|
|
23
|
-
if (value.effort === null) return undefined;
|
|
24
|
-
if (value.effort === "low" || value.effort === "medium" || value.effort === "high") return value.effort;
|
|
25
|
-
}
|
|
26
|
-
return undefined;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
14
|
export function nextEffort(effort: ModelEffort | undefined): ModelEffort {
|
|
30
|
-
if (effort === "
|
|
31
|
-
if (effort === "
|
|
32
|
-
return "
|
|
15
|
+
if (effort === "quick") return "standard";
|
|
16
|
+
if (effort === "standard") return "deep";
|
|
17
|
+
return "quick";
|
|
33
18
|
}
|
|
@@ -1,52 +1,11 @@
|
|
|
1
1
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
|
-
import type { ExploreEngine } from "./ast/engine.ts";
|
|
3
|
-
import { walkPaths } from "./traverse.ts";
|
|
4
2
|
|
|
5
|
-
const
|
|
3
|
+
const GUIDANCE = `## Explore
|
|
4
|
+
Shape-backed languages: \`markdown\`, \`typescript\`, \`tsx\`, \`go\`, \`rust\`, \`c_sharp\`, \`java\`, \`kotlin\`, \`swift\`.
|
|
6
5
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
try {
|
|
10
|
-
const engine = engineFor(ctx.cwd);
|
|
11
|
-
const advertised = engine.registry
|
|
12
|
-
.registeredLanguages()
|
|
13
|
-
.filter((language) => language.capabilities.shape)
|
|
14
|
-
.map((language) => language.id);
|
|
15
|
-
const advertisedSet = new Set(advertised);
|
|
16
|
-
const detected = new Set<string>();
|
|
6
|
+
Structural tools (\`outline\`, \`show\`, \`discover\`, \`ast_search\`, deps/relationships, \`impact\`, \`context\`) apply to those languages. Other files: harness \`read\` / \`grep\` / \`find\` / \`ls\`.
|
|
7
|
+
Full \`read\` of a large registered source returns outline + follow-up hint, not the body — use ranged \`read\` or \`show\`.`;
|
|
17
8
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
root: ".",
|
|
21
|
-
filesOnly: true,
|
|
22
|
-
budgets: { maxFiles: GUIDANCE_SCAN_MAX_ENTRIES },
|
|
23
|
-
onFile(hit) {
|
|
24
|
-
const language = engine.registry.adapterForPath(hit.absolutePath)?.id;
|
|
25
|
-
if (language !== undefined && advertisedSet.has(language)) detected.add(language);
|
|
26
|
-
return detected.size < advertisedSet.size;
|
|
27
|
-
},
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
const backed = advertised.filter((language) => detected.has(language));
|
|
31
|
-
if (backed.length === 0) return undefined;
|
|
32
|
-
|
|
33
|
-
const guidance = `## Explore source policy
|
|
34
|
-
Structurally backed languages detected in this workspace: ${backed.map((language) => `\`${language}\``).join(", ")}.
|
|
35
|
-
|
|
36
|
-
- Use the same workflow for every backed language. Identify the job type before loading deeply: locate, reuse, edit, explain, or debug.
|
|
37
|
-
- Ask the smallest sufficient query and stop when it answers the question.
|
|
38
|
-
- Prefer \`outline\` for unfamiliar trees and known packages. A large full harness \`read\` returns an outline; use ranged \`read\` or \`show\` for bodies.
|
|
39
|
-
- Prefer \`discover\` when reuse is the goal and the path is unknown. Search package/public surfaces first.
|
|
40
|
-
- Bind targets with path + name (+ line when needed). Use \`show\` views cheapest to richest: \`signature\`, \`signatureWithDocs\`, \`declaration\`, \`declarationWithImports\`.
|
|
41
|
-
- Run \`impact\` before a non-trivial symbol change. Use \`context\` to understand one symbol in one bounded pack.
|
|
42
|
-
- After selecting a target, use \`callers\`, \`callees\`, \`references\`, or \`implementations\` for focused relationship questions.
|
|
43
|
-
- Use \`ast_search\` for code shapes and harness \`grep\` for literals. Use harness \`read\` for formatting, comments, gaps, and unsupported files.
|
|
44
|
-
- List and search paths with harness \`ls\`, \`find\`, and \`grep\`. Edit only with harness patch/edit/write tools.
|
|
45
|
-
- Trust structural hits as exact path, line, and source text. Do not ask for scores or engine metadata.`;
|
|
46
|
-
|
|
47
|
-
return { systemPrompt: `${event.systemPrompt}\n\n${guidance}` };
|
|
48
|
-
} catch {
|
|
49
|
-
return undefined;
|
|
50
|
-
}
|
|
51
|
-
});
|
|
9
|
+
export function registerExploreGuidance(pi: ExtensionAPI): void {
|
|
10
|
+
pi.on("before_agent_start", (event) => ({ systemPrompt: `${event.systemPrompt}\n\n${GUIDANCE}` }));
|
|
52
11
|
}
|
|
@@ -3,50 +3,42 @@ import { resolve } from "node:path";
|
|
|
3
3
|
import { onTauEvent } from "../../shared/events.ts";
|
|
4
4
|
import { createTemporaryOutputStore } from "../../shared/temporary-output-store.ts";
|
|
5
5
|
import { createToolRowStateStore } from "../../shared/tool-row-state.ts";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
6
|
+
import type { ExploreEngine } from "../../src/ast/engine.ts";
|
|
7
|
+
import type { ExploreFileGraph } from "../../src/ast/graph/file-graph.ts";
|
|
8
|
+
import {
|
|
9
|
+
astEngineFor,
|
|
10
|
+
astGraphFor,
|
|
11
|
+
clearAstSession,
|
|
12
|
+
invalidateAstPaths,
|
|
13
|
+
restartAstSession,
|
|
14
|
+
shutdownAstSession,
|
|
15
|
+
} from "../../src/ast/session.ts";
|
|
16
|
+
import { registerFileInjection } from "../../src/file-injection/index.ts";
|
|
17
|
+
import { loadTauExtensionSettings } from "../../shared/settings/load.ts";
|
|
18
|
+
import { registerReadOutlineHook } from "./read/hook.ts";
|
|
19
|
+
import { createAstSearchTool } from "./tools/ast-search.ts";
|
|
20
|
+
import { createContextTool } from "./tools/context.ts";
|
|
21
|
+
import { createDepsTool } from "./tools/deps.ts";
|
|
22
|
+
import { createDiscoverTool } from "./tools/discover.ts";
|
|
23
|
+
import { createImpactTool } from "./tools/impact.ts";
|
|
24
|
+
import { createOutlineTool } from "./tools/outline.ts";
|
|
15
25
|
import {
|
|
16
26
|
createCalleesTool,
|
|
17
27
|
createCallersTool,
|
|
18
28
|
createImplementationsTool,
|
|
19
29
|
createReferencesTool,
|
|
20
|
-
} from "./
|
|
21
|
-
import { createReverseDepsTool } from "./
|
|
22
|
-
import { createShowTool } from "./
|
|
30
|
+
} from "./tools/relationships.ts";
|
|
31
|
+
import { createReverseDepsTool } from "./tools/reverse-deps.ts";
|
|
32
|
+
import { createShowTool } from "./tools/show.ts";
|
|
23
33
|
import { registerExploreGuidance } from "./guidance.ts";
|
|
24
|
-
import
|
|
25
|
-
import { registerExploreAutoread } from "./read/autoread.ts";
|
|
34
|
+
import exploreSettings from "./settings.ts";
|
|
26
35
|
|
|
27
36
|
export default function exploreExtension(pi: ExtensionAPI): void {
|
|
28
37
|
const rowState = createToolRowStateStore(pi, "explore.tool-row-state");
|
|
29
38
|
const temporaryOutput = createTemporaryOutputStore();
|
|
30
|
-
let
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
const engineFor = (cwd: string): ExploreEngine => {
|
|
34
|
-
const absoluteCwd = resolve(cwd);
|
|
35
|
-
if (engine !== undefined && engine.cwd === absoluteCwd) return engine;
|
|
36
|
-
engine?.shutdown();
|
|
37
|
-
graph?.clear();
|
|
38
|
-
engine = createExploreEngine({ cwd: absoluteCwd });
|
|
39
|
-
graph = createFileGraph(engine);
|
|
40
|
-
return engine;
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
const graphFor = (cwd: string): ExploreFileGraph => {
|
|
44
|
-
engineFor(cwd);
|
|
45
|
-
if (graph === undefined) {
|
|
46
|
-
throw new Error("Explore file graph failed to initialize");
|
|
47
|
-
}
|
|
48
|
-
return graph;
|
|
49
|
-
};
|
|
39
|
+
let readSettings = exploreSettings.defaults.read;
|
|
40
|
+
const engineFor = (cwd: string): ExploreEngine => astEngineFor(cwd);
|
|
41
|
+
const graphFor = (cwd: string): ExploreFileGraph => astGraphFor(cwd);
|
|
50
42
|
|
|
51
43
|
pi.registerTool(createOutlineTool(rowState, temporaryOutput, engineFor));
|
|
52
44
|
pi.registerTool(createShowTool(rowState, engineFor));
|
|
@@ -61,45 +53,38 @@ export default function exploreExtension(pi: ExtensionAPI): void {
|
|
|
61
53
|
pi.registerTool(createImpactTool(rowState, temporaryOutput, engineFor, graphFor));
|
|
62
54
|
pi.registerTool(createContextTool(rowState, temporaryOutput, engineFor, graphFor));
|
|
63
55
|
registerReadOutlineHook(pi, engineFor);
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
56
|
+
registerFileInjection(pi, rowState, {
|
|
57
|
+
temporaryOutput,
|
|
58
|
+
autoOutline: () => ({ enabled: readSettings.enabled, thresholdLines: readSettings.structureThresholdLines }),
|
|
59
|
+
});
|
|
60
|
+
registerExploreGuidance(pi);
|
|
67
61
|
|
|
68
62
|
pi.on("session_start", async (_event, ctx) => {
|
|
63
|
+
readSettings = (await loadTauExtensionSettings(ctx, exploreSettings)).read;
|
|
69
64
|
await temporaryOutput.shutdown();
|
|
70
65
|
await temporaryOutput.start();
|
|
71
66
|
rowState.clear();
|
|
72
|
-
|
|
73
|
-
graph?.clear();
|
|
74
|
-
engine = createExploreEngine({ cwd: ctx.cwd });
|
|
75
|
-
graph = createFileGraph(engine);
|
|
67
|
+
restartAstSession(ctx.cwd);
|
|
76
68
|
});
|
|
77
69
|
|
|
78
70
|
onTauEvent(pi, "explore.ast", "tau:file-mutation.applied", (event) => {
|
|
79
|
-
if (engine === undefined) return;
|
|
80
71
|
const paths = event.changes.flatMap((change) => {
|
|
81
72
|
const main = resolve(event.cwd, change.path);
|
|
82
73
|
if (change.move === undefined) return [main];
|
|
83
74
|
return [main, resolve(event.cwd, change.move.from), resolve(event.cwd, change.move.to)];
|
|
84
75
|
});
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
(change) => change.kind === "add" || change.kind === "delete" || change.move !== undefined,
|
|
76
|
+
invalidateAstPaths(
|
|
77
|
+
paths,
|
|
78
|
+
event.changes.some((change) => change.kind === "add" || change.kind === "delete" || change.move !== undefined),
|
|
88
79
|
);
|
|
89
|
-
if (topologyChanged) graph?.clear();
|
|
90
|
-
else graph?.invalidate(paths);
|
|
91
80
|
});
|
|
92
81
|
|
|
93
82
|
pi.on("session_tree", () => {
|
|
94
|
-
|
|
95
|
-
graph?.clear();
|
|
83
|
+
clearAstSession();
|
|
96
84
|
});
|
|
97
85
|
|
|
98
86
|
pi.on("session_shutdown", async () => {
|
|
99
|
-
|
|
100
|
-
engine = undefined;
|
|
101
|
-
graph?.clear();
|
|
102
|
-
graph = undefined;
|
|
87
|
+
shutdownAstSession();
|
|
103
88
|
await temporaryOutput.shutdown();
|
|
104
89
|
});
|
|
105
90
|
}
|