@loucompanion/forge-bridge 0.1.1-dev.242bb53ef13f → 0.1.1-dev.2ac58b6f94ce

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 (40) hide show
  1. package/README.md +1 -0
  2. package/dist/bridge/bridge.service.js +83 -11
  3. package/dist/bridge/bridge.types.d.ts +16 -0
  4. package/dist/cli/cli.service.d.ts +27 -2
  5. package/dist/cli/cli.service.js +406 -16
  6. package/dist/cli/local-config.d.ts +4 -1
  7. package/dist/cli/local-config.js +60 -8
  8. package/dist/session/index.d.ts +2 -0
  9. package/dist/session/index.js +1 -0
  10. package/dist/session/internals.d.ts +5 -3
  11. package/dist/session/internals.js +2 -1
  12. package/dist/session/phase-completion.d.ts +101 -0
  13. package/dist/session/phase-completion.js +1985 -0
  14. package/dist/session/provider-cli/index.d.ts +2 -2
  15. package/dist/session/provider-cli/index.js +1 -1
  16. package/dist/session/provider-cli/provider-cli.base.d.ts +2 -2
  17. package/dist/session/provider-cli/provider-cli.service.d.ts +48 -3
  18. package/dist/session/provider-cli/provider-cli.service.js +1778 -45
  19. package/dist/session/provider-cli/provider-cli.types.d.ts +2 -0
  20. package/dist/session/provider-cli/provider-usage.d.ts +25 -0
  21. package/dist/session/provider-cli/provider-usage.js +64 -0
  22. package/dist/session/qa-sidecar-validation.d.ts +3 -0
  23. package/dist/session/qa-sidecar-validation.js +44 -0
  24. package/dist/session/session.service.js +55 -12
  25. package/dist/session/session.types.d.ts +66 -0
  26. package/dist/session/session.types.js +165 -2
  27. package/dist/shared/cursor-agent/cursor-agent.launch.d.ts +9 -0
  28. package/dist/shared/cursor-agent/cursor-agent.launch.js +66 -0
  29. package/dist/version.d.ts +1 -1
  30. package/dist/version.js +1 -1
  31. package/dist/worktree/artifact-injector.d.ts +11 -0
  32. package/dist/worktree/artifact-injector.js +104 -0
  33. package/dist/worktree/dependency-warmer.d.ts +32 -0
  34. package/dist/worktree/dependency-warmer.js +297 -0
  35. package/dist/worktree/index.d.ts +2 -0
  36. package/dist/worktree/index.js +1 -0
  37. package/dist/worktree/worktree.service.d.ts +4 -0
  38. package/dist/worktree/worktree.service.js +173 -11
  39. package/dist/worktree/worktree.types.d.ts +5 -1
  40. package/package.json +2 -2
@@ -0,0 +1,11 @@
1
+ import type { DispatchResolvedArtifact } from "../session/session.types.js";
2
+ export declare function injectWorktreeArtifacts(worktreePath: string, artifacts: readonly DispatchResolvedArtifact[]): Promise<void>;
3
+ export declare function writeArtifact(worktreePath: string, artifact: DispatchResolvedArtifact): Promise<void>;
4
+ export declare function selectLaneCommand(artifacts: readonly DispatchResolvedArtifact[], lane: string, phase: string): DispatchResolvedArtifact | undefined;
5
+ export declare function resolveCommandSlug(lane: string, phase: string): string;
6
+ export declare function buildSpecBody(laneCommand: DispatchResolvedArtifact, workItem: {
7
+ readonly id: string;
8
+ readonly title: string;
9
+ readonly description: string;
10
+ readonly acceptanceCriteria: string;
11
+ }, phase?: string): string;
@@ -0,0 +1,104 @@
1
+ import crypto from "node:crypto";
2
+ import fs from "node:fs/promises";
3
+ import path from "node:path";
4
+ export async function injectWorktreeArtifacts(worktreePath, artifacts) {
5
+ for (const artifact of artifacts) {
6
+ await writeArtifact(worktreePath, artifact);
7
+ }
8
+ }
9
+ export async function writeArtifact(worktreePath, artifact) {
10
+ const writtenHash = sha256(artifact.body);
11
+ if (writtenHash !== artifact.contentHash.toLowerCase()) {
12
+ throw new Error(`Injected artifact '${artifact.slug}' content_hash mismatch (expected ${artifact.contentHash}, got ${writtenHash}).`);
13
+ }
14
+ const kindFolder = kindToFolder(artifact.kind);
15
+ const scope = safeArtifactSegment("scope", artifact.scope);
16
+ const slug = safeArtifactSegment("slug", artifact.slug);
17
+ const forgeRoot = path.resolve(worktreePath, ".forge");
18
+ const scopeDir = path.resolve(forgeRoot, kindFolder, scope);
19
+ assertInside(forgeRoot, scopeDir, "Artifact directory");
20
+ const filePath = normalizedArtifactKind(artifact.kind) === "skill"
21
+ ? path.resolve(scopeDir, slug, "SKILL.md")
22
+ : path.resolve(scopeDir, `${slug}.md`);
23
+ assertInside(forgeRoot, filePath, "Artifact file");
24
+ await fs.mkdir(path.dirname(filePath), { recursive: true });
25
+ await fs.writeFile(filePath, artifact.body, "utf8");
26
+ }
27
+ export function selectLaneCommand(artifacts, lane, phase) {
28
+ const slug = resolveCommandSlug(lane, phase).toLowerCase();
29
+ return artifacts.find((artifact) => normalizedArtifactKind(artifact.kind) === "command" && artifact.slug.trim().toLowerCase() === slug);
30
+ }
31
+ export function resolveCommandSlug(lane, phase) {
32
+ const normalizedPhase = phase.trim().toLowerCase().replace(/-/g, "_");
33
+ if (normalizedPhase === "rework") {
34
+ return "rework-prompt";
35
+ }
36
+ switch (lane.trim().toLowerCase()) {
37
+ case "architect":
38
+ return "architect-prompt";
39
+ case "qa":
40
+ return "qa-prompt";
41
+ default:
42
+ return "dev-prompt";
43
+ }
44
+ }
45
+ export function buildSpecBody(laneCommand, workItem, phase = "implementation") {
46
+ const prompt = laneCommand.body.replaceAll("{workItemId}", workItem.id);
47
+ return `---
48
+ work_item_id: ${workItem.id}
49
+ status: approved
50
+ lane: ${laneCommand.lane ?? "develop"}
51
+ phase: ${phase}
52
+ command_slug: ${laneCommand.slug}
53
+ ---
54
+
55
+ ${prompt}
56
+
57
+ ---
58
+
59
+ # Work item: ${workItem.title}
60
+
61
+ ${workItem.description}
62
+
63
+ ## Acceptance criteria
64
+
65
+ ${workItem.acceptanceCriteria}
66
+ `;
67
+ }
68
+ function kindToFolder(kind) {
69
+ switch (normalizedArtifactKind(kind)) {
70
+ case "command":
71
+ return "commands";
72
+ case "hook":
73
+ return "hooks";
74
+ case "skill":
75
+ return "skills";
76
+ default:
77
+ throw new Error(`Unsupported artifact kind '${kind}'.`);
78
+ }
79
+ }
80
+ function normalizedArtifactKind(kind) {
81
+ return kind.trim().toLowerCase();
82
+ }
83
+ function safeArtifactSegment(label, value) {
84
+ const trimmed = value.trim();
85
+ if (!trimmed
86
+ || trimmed === "."
87
+ || trimmed === ".."
88
+ || /[\\/]/.test(trimmed)
89
+ || path.isAbsolute(trimmed)
90
+ || !/^[A-Za-z0-9._-]+$/.test(trimmed)) {
91
+ throw new Error(`Artifact ${label} must be a safe path segment.`);
92
+ }
93
+ return trimmed;
94
+ }
95
+ function assertInside(root, candidate, label) {
96
+ const relative = path.relative(root, candidate);
97
+ if (relative === "" || (!relative.startsWith("..") && !path.isAbsolute(relative))) {
98
+ return;
99
+ }
100
+ throw new Error(`${label} must stay under ${root}.`);
101
+ }
102
+ function sha256(content) {
103
+ return crypto.createHash("sha256").update(content).digest("hex");
104
+ }
@@ -0,0 +1,32 @@
1
+ export type DependencyEcosystem = "node" | "dotnet" | "python";
2
+ export interface WarmPathEntry {
3
+ readonly relativePath: string;
4
+ readonly ecosystem: DependencyEcosystem;
5
+ readonly packageRoot: string;
6
+ }
7
+ export interface WarmDependenciesRequest {
8
+ readonly baseCheckoutPath: string;
9
+ readonly worktreePath: string;
10
+ readonly allowedRoot: string;
11
+ readonly warmPathOverrides?: readonly string[];
12
+ }
13
+ export interface InstallFallbackResult {
14
+ readonly ecosystem: DependencyEcosystem;
15
+ readonly packageRoot: string;
16
+ readonly reason: string;
17
+ readonly command: string;
18
+ }
19
+ export interface WarmDependenciesResult {
20
+ readonly linked: readonly string[];
21
+ readonly skipped: readonly string[];
22
+ readonly fallbacks: readonly InstallFallbackResult[];
23
+ }
24
+ export interface CommandExecutor {
25
+ run(cwd: string, command: string, args: readonly string[]): Promise<void>;
26
+ }
27
+ export declare function detectWarmPaths(repoRoot: string, warmPathOverrides?: readonly string[]): Promise<WarmPathEntry[]>;
28
+ export declare function warmWorktreeDependencies(request: WarmDependenciesRequest, executor?: CommandExecutor): Promise<WarmDependenciesResult>;
29
+ export declare function createDirectoryLink(sourcePath: string, targetPath: string, allowedRoot: string): Promise<void>;
30
+ export declare function lockfilesDiffer(baseCheckoutPath: string, worktreePath: string, packageRootRelative: string): Promise<boolean>;
31
+ export declare function requiresInstallFallback(entry: WarmPathEntry, baseCheckoutPath: string, worktreePath: string, sourceExists: boolean): Promise<string | null>;
32
+ export declare function declaresNativeModules(packageRootPath: string): Promise<boolean>;
@@ -0,0 +1,297 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { NodeProcessRunner } from "../shared/process/process.js";
4
+ import { isSubPath } from "../shared/path/path.js";
5
+ const SKIP_DIR_NAMES = new Set([
6
+ ".git",
7
+ "node_modules",
8
+ ".venv",
9
+ "bin",
10
+ "obj",
11
+ ".forge-worktrees",
12
+ ".forge"
13
+ ]);
14
+ const NATIVE_MODULE_HINTS = [
15
+ "node-gyp",
16
+ "bcrypt",
17
+ "sharp",
18
+ "esbuild",
19
+ "sqlite3",
20
+ "better-sqlite3",
21
+ "@swc/core"
22
+ ];
23
+ export async function detectWarmPaths(repoRoot, warmPathOverrides = []) {
24
+ const entries = new Map();
25
+ await collectDetectedPaths(repoRoot, repoRoot, entries, 0);
26
+ for (const override of warmPathOverrides) {
27
+ const normalized = normalizeRelativePath(override);
28
+ if (!normalized) {
29
+ continue;
30
+ }
31
+ const packageRoot = await resolvePackageRoot(repoRoot, normalized);
32
+ entries.set(normalized, {
33
+ relativePath: normalized,
34
+ ecosystem: inferEcosystemFromPath(normalized),
35
+ packageRoot
36
+ });
37
+ }
38
+ return [...entries.values()];
39
+ }
40
+ export async function warmWorktreeDependencies(request, executor = defaultCommandExecutor) {
41
+ const baseCheckoutPath = path.resolve(request.baseCheckoutPath);
42
+ const worktreePath = path.resolve(request.worktreePath);
43
+ const allowedRoot = path.resolve(request.allowedRoot);
44
+ assertWithinRoot(allowedRoot, baseCheckoutPath, "Base checkout");
45
+ assertWithinRoot(allowedRoot, worktreePath, "Worktree");
46
+ const warmPaths = await detectWarmPaths(baseCheckoutPath, request.warmPathOverrides ?? []);
47
+ const linked = [];
48
+ const skipped = [];
49
+ const fallbacks = [];
50
+ for (const entry of warmPaths) {
51
+ const sourcePath = path.join(baseCheckoutPath, entry.relativePath);
52
+ const targetPath = path.join(worktreePath, entry.relativePath);
53
+ assertWithinRoot(allowedRoot, targetPath, "Warm target");
54
+ const sourceExists = await directoryExists(sourcePath);
55
+ const fallbackReason = await resolveFallbackReason(entry, baseCheckoutPath, worktreePath, sourceExists);
56
+ if (fallbackReason) {
57
+ const install = await runInstallFallback(entry, worktreePath, fallbackReason, executor);
58
+ fallbacks.push(install);
59
+ continue;
60
+ }
61
+ if (!sourceExists) {
62
+ skipped.push(entry.relativePath);
63
+ continue;
64
+ }
65
+ await createDirectoryLink(sourcePath, targetPath, allowedRoot);
66
+ linked.push(entry.relativePath);
67
+ }
68
+ return { linked, skipped, fallbacks };
69
+ }
70
+ export async function createDirectoryLink(sourcePath, targetPath, allowedRoot) {
71
+ const resolvedSource = path.resolve(sourcePath);
72
+ const resolvedTarget = path.resolve(targetPath);
73
+ const resolvedRoot = path.resolve(allowedRoot);
74
+ assertWithinRoot(resolvedRoot, resolvedSource, "Link source");
75
+ assertWithinRoot(resolvedRoot, resolvedTarget, "Link target");
76
+ assertWithinRoot(resolvedRoot, path.dirname(resolvedTarget), "Link target parent");
77
+ const stat = await fs.stat(resolvedSource).catch(() => null);
78
+ if (!stat?.isDirectory()) {
79
+ throw new Error(`Link source must be an existing directory: ${resolvedSource}`);
80
+ }
81
+ await fs.rm(resolvedTarget, { recursive: true, force: true });
82
+ await fs.mkdir(path.dirname(resolvedTarget), { recursive: true });
83
+ if (process.platform === "win32") {
84
+ await fs.symlink(resolvedSource, resolvedTarget, "junction");
85
+ return;
86
+ }
87
+ await fs.symlink(resolvedSource, resolvedTarget, "dir");
88
+ }
89
+ async function collectDetectedPaths(repoRoot, currentDir, entries, depth) {
90
+ if (depth > 6) {
91
+ return;
92
+ }
93
+ const relativeDir = path.relative(repoRoot, currentDir);
94
+ const normalizedDir = relativeDir.length === 0 ? "" : normalizeRelativePath(relativeDir);
95
+ if (normalizedDir) {
96
+ const packageJsonPath = path.join(currentDir, "package.json");
97
+ if (await pathExists(packageJsonPath)) {
98
+ addEntry(entries, repoRoot, normalizedDir, "node_modules", "node");
99
+ }
100
+ if (await hasPythonMarker(currentDir)) {
101
+ addEntry(entries, repoRoot, normalizedDir, ".venv", "python");
102
+ }
103
+ if (await hasDotNetMarker(currentDir)) {
104
+ const binRelative = joinRelative(normalizedDir, "bin");
105
+ const objRelative = joinRelative(normalizedDir, "obj");
106
+ if (await directoryExists(path.join(repoRoot, binRelative))) {
107
+ addEntry(entries, repoRoot, normalizedDir, "bin", "dotnet", binRelative);
108
+ }
109
+ if (await directoryExists(path.join(repoRoot, objRelative))) {
110
+ addEntry(entries, repoRoot, normalizedDir, "obj", "dotnet", objRelative);
111
+ }
112
+ }
113
+ }
114
+ else {
115
+ if (await pathExists(path.join(currentDir, "package.json"))) {
116
+ addEntry(entries, repoRoot, "", "node_modules", "node");
117
+ }
118
+ if (await hasPythonMarker(currentDir)) {
119
+ addEntry(entries, repoRoot, "", ".venv", "python");
120
+ }
121
+ if (await hasDotNetMarker(currentDir)) {
122
+ if (await directoryExists(path.join(repoRoot, "bin"))) {
123
+ addEntry(entries, repoRoot, "", "bin", "dotnet", "bin");
124
+ }
125
+ if (await directoryExists(path.join(repoRoot, "obj"))) {
126
+ addEntry(entries, repoRoot, "", "obj", "dotnet", "obj");
127
+ }
128
+ }
129
+ }
130
+ const children = await fs.readdir(currentDir, { withFileTypes: true }).catch(() => []);
131
+ for (const child of children) {
132
+ if (!child.isDirectory() || SKIP_DIR_NAMES.has(child.name) || child.name.startsWith(".")) {
133
+ continue;
134
+ }
135
+ await collectDetectedPaths(repoRoot, path.join(currentDir, child.name), entries, depth + 1);
136
+ }
137
+ }
138
+ function addEntry(entries, repoRoot, packageRootRelative, folderName, ecosystem, explicitRelativePath) {
139
+ const relativePath = explicitRelativePath ?? joinRelative(packageRootRelative, folderName);
140
+ entries.set(relativePath, {
141
+ relativePath,
142
+ ecosystem,
143
+ packageRoot: packageRootRelative.length > 0 ? packageRootRelative : "."
144
+ });
145
+ }
146
+ async function resolveFallbackReason(entry, baseCheckoutPath, worktreePath, sourceExists) {
147
+ if (entry.ecosystem === "node") {
148
+ if (!sourceExists) {
149
+ return "Base checkout is missing node_modules.";
150
+ }
151
+ if (await lockfilesDiffer(baseCheckoutPath, worktreePath, entry.packageRoot)) {
152
+ return "Lockfile differs between base checkout and worktree.";
153
+ }
154
+ }
155
+ if (entry.ecosystem === "python" && !sourceExists) {
156
+ return "Base checkout is missing .venv.";
157
+ }
158
+ return null;
159
+ }
160
+ export async function lockfilesDiffer(baseCheckoutPath, worktreePath, packageRootRelative) {
161
+ const lockfileNames = ["package-lock.json", "pnpm-lock.yaml", "yarn.lock", "npm-shrinkwrap.json"];
162
+ const packageRoot = packageRootRelative === "." ? "" : packageRootRelative;
163
+ for (const lockfile of lockfileNames) {
164
+ const baseLock = path.join(baseCheckoutPath, packageRoot, lockfile);
165
+ const worktreeLock = path.join(worktreePath, packageRoot, lockfile);
166
+ const baseExists = await pathExists(baseLock);
167
+ const worktreeExists = await pathExists(worktreeLock);
168
+ if (baseExists !== worktreeExists) {
169
+ return true;
170
+ }
171
+ if (!baseExists) {
172
+ continue;
173
+ }
174
+ const baseHash = await sha256File(baseLock);
175
+ const worktreeHash = await sha256File(worktreeLock);
176
+ if (baseHash !== worktreeHash) {
177
+ return true;
178
+ }
179
+ }
180
+ return false;
181
+ }
182
+ async function runInstallFallback(entry, worktreePath, reason, executor) {
183
+ const cwd = path.join(worktreePath, entry.packageRoot === "." ? "" : entry.packageRoot);
184
+ if (entry.ecosystem === "node") {
185
+ const lockPath = path.join(cwd, "package-lock.json");
186
+ const args = (await pathExists(lockPath)) ? ["ci"] : ["install"];
187
+ await executor.run(cwd, "npm", args);
188
+ return { ecosystem: entry.ecosystem, packageRoot: entry.packageRoot, reason, command: `npm ${args.join(" ")}` };
189
+ }
190
+ if (entry.ecosystem === "python") {
191
+ await executor.run(cwd, "python", ["-m", "venv", ".venv"]);
192
+ const requirements = path.join(cwd, "requirements.txt");
193
+ if (await pathExists(requirements)) {
194
+ const pip = process.platform === "win32" ? path.join(".venv", "Scripts", "pip") : path.join(".venv", "bin", "pip");
195
+ await executor.run(cwd, pip, ["install", "-r", "requirements.txt"]);
196
+ return {
197
+ ecosystem: entry.ecosystem,
198
+ packageRoot: entry.packageRoot,
199
+ reason,
200
+ command: "python -m venv .venv && pip install -r requirements.txt"
201
+ };
202
+ }
203
+ return {
204
+ ecosystem: entry.ecosystem,
205
+ packageRoot: entry.packageRoot,
206
+ reason,
207
+ command: "python -m venv .venv"
208
+ };
209
+ }
210
+ await executor.run(cwd, "dotnet", ["restore"]);
211
+ return {
212
+ ecosystem: entry.ecosystem,
213
+ packageRoot: entry.packageRoot,
214
+ reason,
215
+ command: "dotnet restore"
216
+ };
217
+ }
218
+ export async function requiresInstallFallback(entry, baseCheckoutPath, worktreePath, sourceExists) {
219
+ return resolveFallbackReason(entry, baseCheckoutPath, worktreePath, sourceExists);
220
+ }
221
+ export async function declaresNativeModules(packageRootPath) {
222
+ const packageJsonPath = path.join(packageRootPath, "package.json");
223
+ if (!(await pathExists(packageJsonPath))) {
224
+ return false;
225
+ }
226
+ const raw = await fs.readFile(packageJsonPath, "utf8");
227
+ const lower = raw.toLowerCase();
228
+ return NATIVE_MODULE_HINTS.some((hint) => lower.includes(`"${hint}"`));
229
+ }
230
+ async function hasPythonMarker(dir) {
231
+ return (await pathExists(path.join(dir, "pyproject.toml")))
232
+ || (await pathExists(path.join(dir, "requirements.txt")));
233
+ }
234
+ async function hasDotNetMarker(dir) {
235
+ const entries = await fs.readdir(dir).catch(() => []);
236
+ return entries.some((name) => name.endsWith(".csproj") || name.endsWith(".sln"));
237
+ }
238
+ async function resolvePackageRoot(repoRoot, relativePath) {
239
+ const segments = relativePath.split("/").filter(Boolean);
240
+ if (segments.length === 0) {
241
+ return ".";
242
+ }
243
+ const last = segments[segments.length - 1];
244
+ if (last === "node_modules" || last === ".venv" || last === "bin" || last === "obj") {
245
+ const parent = segments.slice(0, -1).join("/");
246
+ return parent.length > 0 ? parent : ".";
247
+ }
248
+ const candidate = path.join(repoRoot, ...segments);
249
+ if (await pathExists(path.join(candidate, "package.json"))) {
250
+ return segments.join("/");
251
+ }
252
+ return segments.slice(0, -1).join("/") || ".";
253
+ }
254
+ function inferEcosystemFromPath(relativePath) {
255
+ const base = path.basename(relativePath);
256
+ if (base === ".venv") {
257
+ return "python";
258
+ }
259
+ if (base === "bin" || base === "obj") {
260
+ return "dotnet";
261
+ }
262
+ return "node";
263
+ }
264
+ function joinRelative(packageRootRelative, folderName) {
265
+ return packageRootRelative.length > 0
266
+ ? `${packageRootRelative}/${folderName}`.replace(/\\/g, "/")
267
+ : folderName;
268
+ }
269
+ function normalizeRelativePath(value) {
270
+ return value.replace(/\\/g, "/").replace(/^\/+/, "").replace(/\/+$/, "");
271
+ }
272
+ function assertWithinRoot(root, candidate, label) {
273
+ if (!isSubPath(root, candidate)) {
274
+ throw new Error(`${label} must stay under ${root}.`);
275
+ }
276
+ }
277
+ async function directoryExists(inputPath) {
278
+ const stat = await fs.stat(inputPath).catch(() => null);
279
+ return stat?.isDirectory() ?? false;
280
+ }
281
+ async function pathExists(inputPath) {
282
+ return (await fs.stat(inputPath).catch(() => null)) !== null;
283
+ }
284
+ async function sha256File(filePath) {
285
+ const crypto = await import("node:crypto");
286
+ const content = await fs.readFile(filePath);
287
+ return crypto.createHash("sha256").update(content).digest("hex");
288
+ }
289
+ const defaultCommandExecutor = {
290
+ async run(cwd, command, args) {
291
+ const result = await defaultProcessRunner.run({ command, cwd, args });
292
+ if (result.exitCode !== 0) {
293
+ throw new Error(`${command} ${args.join(" ")} failed (${result.exitCode}): ${result.stderr.trim()}`);
294
+ }
295
+ }
296
+ };
297
+ const defaultProcessRunner = new NodeProcessRunner();
@@ -1,2 +1,4 @@
1
1
  export { GitWorktreeService } from "./worktree.service.js";
2
+ export { detectWarmPaths, warmWorktreeDependencies, createDirectoryLink, lockfilesDiffer, requiresInstallFallback } from "./dependency-warmer.js";
3
+ export type { WarmDependenciesResult, WarmPathEntry, InstallFallbackResult } from "./dependency-warmer.js";
2
4
  export type { WorktreeService } from "./worktree.base.js";
@@ -1 +1,2 @@
1
1
  export { GitWorktreeService } from "./worktree.service.js";
2
+ export { detectWarmPaths, warmWorktreeDependencies, createDirectoryLink, lockfilesDiffer, requiresInstallFallback } from "./dependency-warmer.js";
@@ -7,6 +7,10 @@ export declare class GitWorktreeService implements WorktreeService {
7
7
  prepare(request: WorktreePrepareRequest): Promise<WorktreeHandle>;
8
8
  release(handle: WorktreeHandle): Promise<void>;
9
9
  private resolveRepoPath;
10
+ private findExistingRepoPath;
11
+ private resolveMatchingRepo;
12
+ private ensureBaseBranch;
13
+ private gitRefExists;
10
14
  private ensureGitIdentity;
11
15
  }
12
16
  export declare function normalizeBranchName(value: string | null | undefined, fallback: string): string;