@voybio/ace-swarm 0.2.3 → 0.2.5

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/dist/helpers.js CHANGED
@@ -8,8 +8,8 @@ import { fileURLToPath } from "node:url";
8
8
  import { buildHostInstructionText } from "./ace-server-instructions.js";
9
9
  import { isInside, normalizeRelPath } from "./shared.js";
10
10
  import { getWorkspaceStorePath, listStoreKeysSync, parseVirtualStorePath, readStoreBlobSync, readVirtualStorePathSync, toVirtualStorePath, } from "./store/store-snapshot.js";
11
- import { isOperationalArtifactPath, operationalArtifactKey, writeStoreBlobSync, writeOperationalArtifactSync, } from "./store/store-artifacts.js";
12
- import { buildOpenAiCompatibleBaseUrl, DEFAULT_LLAMA_CPP_MODEL, DEFAULT_OLLAMA_MODEL, normalizeLocalBaseUrl, } from "./tui/provider-discovery.js";
11
+ import { isOperationalArtifactPath, operationalArtifactKey, writeStoreBlobSync, } from "./store/store-artifacts.js";
12
+ import { buildProviderDoctorCommands, buildOpenAiCompatibleBaseUrl, defaultModelForProvider, normalizeLocalBaseUrl, normalizeProvider, } from "./tui/provider-discovery.js";
13
13
  export { isInside, isReadError, normalizeRelPath, looksLikeSwarmHandoffPath } from "./shared.js";
14
14
  const __filename = fileURLToPath(import.meta.url);
15
15
  const __dirname = dirname(__filename);
@@ -111,6 +111,14 @@ export const ACE_GITHUB_ROOT_REL = ".github";
111
111
  export const ACE_HOST_AGENTS_REL = "AGENTS.md";
112
112
  export const ACE_HOST_CLAUDE_REL = "CLAUDE.md";
113
113
  export const ACE_HOST_CURSOR_RULES_REL = ".cursorrules";
114
+ const ACE_PROJECTED_WORKSPACE_FILES = new Set([
115
+ `${ACE_TASKS_ROOT_REL}/todo.md`,
116
+ `${ACE_ROOT_REL}/ace-hook-context.json`,
117
+ `${ACE_ROOT_REL}/agent-state/job-queue.json`,
118
+ `${ACE_ROOT_REL}/agent-state/job-locks.json`,
119
+ `${ACE_ROOT_REL}/agent-state/scheduler-lease.json`,
120
+ `${ACE_ROOT_REL}/agent-state/context-snapshots/index.json`,
121
+ ]);
114
122
  const ACE_MANAGED_PREFIXES = [
115
123
  "agent-state",
116
124
  "global-state",
@@ -128,7 +136,14 @@ export const ALL_MCP_CLIENTS = [
128
136
  "cursor",
129
137
  "antigravity",
130
138
  ];
131
- export const ALL_LLM_PROVIDERS = ["ollama", "llama.cpp"];
139
+ export const ALL_LLM_PROVIDERS = [
140
+ "ollama",
141
+ "llama.cpp",
142
+ "codex",
143
+ "claude",
144
+ "gemini",
145
+ "copilot",
146
+ ];
132
147
  export const ALL_AGENTS = [
133
148
  "orchestrator",
134
149
  "vos",
@@ -379,6 +394,10 @@ export function mapAceWorkspaceRelativePath(path) {
379
394
  }
380
395
  return normalized;
381
396
  }
397
+ export function isProjectedAceWorkspacePath(filePath) {
398
+ const canonical = mapAceWorkspaceRelativePath(normalizeRelPath(filePath));
399
+ return ACE_PROJECTED_WORKSPACE_FILES.has(canonical);
400
+ }
382
401
  export function acePath(...segments) {
383
402
  return resolve(currentWorkspaceRoot(), ACE_ROOT_REL, ...segments);
384
403
  }
@@ -525,6 +544,9 @@ function resolveStoreFallbackKeys(filePath) {
525
544
  const statePrefix = `${ACE_ROOT_REL}/agent-state/`;
526
545
  if (canonical.startsWith(statePrefix)) {
527
546
  const rel = canonical.slice(statePrefix.length);
547
+ if (!rel.startsWith("context-snapshots/")) {
548
+ keys.add(`state/artifacts/agent-state/${rel}`);
549
+ }
528
550
  const runtimeProjectionKey = {
529
551
  "job-queue.json": "state/artifacts/agent-state/job-queue.json",
530
552
  "job-locks.json": "state/artifacts/agent-state/job-locks.json",
@@ -542,6 +564,10 @@ function resolveStoreFallbackKeys(filePath) {
542
564
  if (rel)
543
565
  keys.add(`state/memory/context_snapshots/${rel}`);
544
566
  }
567
+ const skillsPrefix = `${ACE_SKILLS_ROOT_REL}/`;
568
+ if (canonical.startsWith(skillsPrefix)) {
569
+ keys.add(`knowledge/skills/${canonical.slice(skillsPrefix.length)}`);
570
+ }
545
571
  const tasksPrefix = `${ACE_TASKS_ROOT_REL}/`;
546
572
  if (canonical.startsWith(tasksPrefix)) {
547
573
  keys.add(`knowledge/tasks/${canonical.slice(tasksPrefix.length)}`);
@@ -577,6 +603,9 @@ function resolveStoreFallbackKeys(filePath) {
577
603
  }
578
604
  return [...keys];
579
605
  }
606
+ export function resolveStoreFallbackKeysForPath(filePath) {
607
+ return resolveStoreFallbackKeys(filePath);
608
+ }
580
609
  function readFirstAvailable(paths) {
581
610
  const found = firstExistingPath(paths);
582
611
  if (!found) {
@@ -624,15 +653,14 @@ export function safeWrite(filePath, content) {
624
653
  const normalized = normalizeRelPath(filePath);
625
654
  const canonical = mapAceWorkspaceRelativePath(normalized);
626
655
  const root = currentWorkspaceRoot();
627
- if (canonical.startsWith(`${ACE_ROOT_REL}/agent-state/`)) {
628
- const rel = canonical.slice(`${ACE_ROOT_REL}/`.length);
629
- if (isOperationalArtifactPath(rel) && existsSync(getWorkspaceStorePath(root))) {
630
- return writeOperationalArtifactSync(root, rel, content);
631
- }
632
- }
656
+ let storeVirtualPath;
633
657
  const storeKeys = resolveStoreFallbackKeys(filePath);
634
658
  if (storeKeys.length > 0 && existsSync(getWorkspaceStorePath(root))) {
635
- writeStoreBlobSync(root, storeKeys[0], content);
659
+ storeVirtualPath = writeStoreBlobSync(root, storeKeys[0], content);
660
+ const isAcePath = canonical === ACE_ROOT_REL || canonical.startsWith(`${ACE_ROOT_REL}/`);
661
+ if (isAcePath && !isProjectedAceWorkspacePath(canonical)) {
662
+ return storeVirtualPath;
663
+ }
636
664
  }
637
665
  const abs = resolveWorkspaceWritePath(filePath);
638
666
  mkdirSync(dirname(abs), { recursive: true });
@@ -1325,10 +1353,11 @@ export function bootstrapAceWorkspace(options = {}) {
1325
1353
  const projectName = options.projectName?.trim() || "ACE Project";
1326
1354
  const includeMcpConfig = options.includeMcpConfig ?? true;
1327
1355
  const includeClientConfigBundle = options.includeClientConfigBundle ?? true;
1328
- const llmProvider = options.llmProvider;
1356
+ const normalizedProvider = normalizeProvider(options.llmProvider);
1357
+ const llmProvider = normalizedProvider;
1329
1358
  const llmModel = options.llmModel?.trim() ||
1330
1359
  options.ollamaModel?.trim() ||
1331
- (llmProvider === "llama.cpp" ? DEFAULT_LLAMA_CPP_MODEL : DEFAULT_OLLAMA_MODEL);
1360
+ defaultModelForProvider(llmProvider);
1332
1361
  const llmBaseUrl = normalizeLocalBaseUrl(options.llmBaseUrl ?? options.ollamaBaseUrl);
1333
1362
  if (llmProvider && !ALL_LLM_PROVIDERS.includes(llmProvider)) {
1334
1363
  throw new Error(`Unsupported llmProvider: ${llmProvider}`);
@@ -1654,12 +1683,14 @@ export function bootstrapAceWorkspace(options = {}) {
1654
1683
  ...(llmProvider
1655
1684
  ? [
1656
1685
  "",
1657
- "## Local Model Profile",
1686
+ "## LLM Runtime Profile",
1658
1687
  `- \`${ACE_LLM_ROOT_REL}/llm-profile.json\` (generated for ${llmProvider})`,
1659
- `- \`${ACE_LLM_ROOT_REL}/doctor-checks.md\` (commands to verify local runtime)`,
1688
+ `- \`${ACE_LLM_ROOT_REL}/doctor-checks.md\` (commands to verify provider runtime)`,
1660
1689
  llmBaseUrl
1661
1690
  ? `- Run \`ace doctor --llm ${llmProvider} --model ${llmModel} --base-url ${llmBaseUrl}\``
1662
- : `- Run \`ace doctor --llm ${llmProvider} --model ${llmModel} --scan\` to discover a local endpoint`,
1691
+ : llmProvider === "ollama" || llmProvider === "llama.cpp"
1692
+ ? `- Run \`ace doctor --llm ${llmProvider} --model ${llmModel} --scan\` to discover a local endpoint`
1693
+ : `- Run \`ace doctor --llm ${llmProvider} --model ${llmModel}\` after exporting the provider credentials`,
1663
1694
  ]
1664
1695
  : []),
1665
1696
  ].join("\n"), result, force);
@@ -1678,27 +1709,14 @@ export function bootstrapAceWorkspace(options = {}) {
1678
1709
  }
1679
1710
  }
1680
1711
  writeIfMissingOrForced(wsPath(".ace", "llm-profile.json"), JSON.stringify(profilePayload, null, 2), result, force);
1681
- const doctorCommands = llmProvider === "ollama"
1682
- ? [
1683
- "ollama serve",
1684
- `ollama pull ${llmModel}`,
1685
- ...(llmBaseUrl ? [`curl -s ${llmBaseUrl}/api/tags`] : []),
1686
- ]
1687
- : [
1688
- "# Start llama-server separately, for example:",
1689
- "# llama-server -m /path/to/model.gguf --port 8080",
1690
- ...(llmBaseUrl ? [`curl -s ${buildOpenAiCompatibleBaseUrl(llmBaseUrl)}/models`] : []),
1691
- ];
1712
+ const doctorCommands = buildProviderDoctorCommands(llmProvider, llmModel, llmBaseUrl);
1692
1713
  writeIfMissingOrForced(wsPath(".ace", "doctor-checks.md"), [
1693
- `# ACE + ${llmProvider} Doctor Checks`,
1714
+ `# ACE + ${llmProvider} Runtime Checks`,
1694
1715
  "",
1695
- "Run these commands to verify local-model readiness:",
1716
+ "Run these commands to verify ACE runtime readiness:",
1696
1717
  "",
1697
1718
  "```bash",
1698
1719
  ...doctorCommands,
1699
- llmBaseUrl
1700
- ? `ace doctor --llm ${llmProvider} --model ${llmModel} --base-url ${llmBaseUrl}`
1701
- : `ace doctor --llm ${llmProvider} --model ${llmModel} --scan`,
1702
1720
  "```",
1703
1721
  "",
1704
1722
  `MCP is still configured separately via \`${ACE_VSCODE_ROOT_REL}/mcp.json\` or \`${ACE_BUNDLE_ROOT_REL}/*\`.`,
@@ -1,6 +1,5 @@
1
- import { existsSync, readFileSync } from "node:fs";
2
1
  import { resolve } from "node:path";
3
- import { ROLE_ENUM, scoreDomains } from "./shared.js";
2
+ import { ROLE_ENUM } from "./shared.js";
4
3
  import { resolveWorkspaceRoot } from "./helpers.js";
5
4
  import { executeAceInternalTool } from "./ace-internal-tools.js";
6
5
  import { ModelBridge } from "./model-bridge.js";
@@ -28,61 +27,17 @@ function normalizeRoleCandidate(input) {
28
27
  ? candidate
29
28
  : undefined;
30
29
  }
31
- function parseRoleFromRoutingSummary(summary) {
32
- const match = summary.match(/\*\*Primary Swarm Agent\(s\):\*\*\s*([^\n]+)/i);
33
- if (!match?.[1])
34
- return undefined;
35
- const firstAgent = match[1]
36
- .split(",")
37
- .map((entry) => entry.trim())
38
- .find(Boolean);
39
- return normalizeRoleCandidate(firstAgent);
40
- }
41
- function hasAlignedTaskContract(workspaceRoot) {
42
- const required = [
43
- "agent-state/TASK.md",
44
- "agent-state/SCOPE.md",
45
- "agent-state/QUALITY_GATES.md",
46
- "agent-state/HANDOFF.json",
47
- ];
48
- return required.every((relativePath) => {
49
- const absolute = resolve(workspaceRoot, relativePath);
50
- if (!existsSync(absolute))
51
- return false;
52
- return readFileSync(absolute, "utf-8").trim().length > 0;
53
- });
30
+ function fallbackRoleForTask() {
31
+ return "orchestrator";
54
32
  }
55
- function fallbackRoleForTask(task, workspaceRoot) {
56
- if (!hasAlignedTaskContract(workspaceRoot)) {
57
- return "orchestrator";
58
- }
59
- const { domain } = scoreDomains(task);
60
- switch (domain) {
61
- case "engineering":
62
- return "coders";
63
- case "venture":
64
- return "vos";
65
- case "ux":
66
- return "ui";
67
- default:
68
- return "orchestrator";
69
- }
70
- }
71
- async function resolveRole(task, workspaceRoot, sessionId, requestedRole) {
33
+ async function resolveRole(task, sessionId, requestedRole) {
72
34
  const explicitRole = normalizeRoleCandidate(requestedRole);
73
35
  if (explicitRole) {
74
36
  return { role: explicitRole, routingSummary: undefined };
75
37
  }
76
38
  const routing = await executeAceInternalTool("route_task", { description: task, domain: "unknown" }, sessionId);
77
39
  const routingSummary = extractTextContent(routing);
78
- const routedRole = parseRoleFromRoutingSummary(routingSummary);
79
- const fallbackRole = fallbackRoleForTask(task, workspaceRoot);
80
- return {
81
- role: routedRole && !(routedRole === "orchestrator" && fallbackRole !== "orchestrator")
82
- ? routedRole
83
- : fallbackRole,
84
- routingSummary,
85
- };
40
+ return { role: fallbackRoleForTask(), routingSummary };
86
41
  }
87
42
  function resolveTier(requested, provider, model, role) {
88
43
  if (requested && requested !== "auto")
@@ -152,7 +107,7 @@ export async function runLocalModelTask(options) {
152
107
  ollamaUrl: options.ollamaUrl,
153
108
  });
154
109
  const bridge = new ModelBridge(options.clients ?? createDefaultModelBridgeClients(runtime));
155
- const { role, routingSummary } = await resolveRole(options.task, runtime.workspaceRoot, undefined, options.role);
110
+ const { role, routingSummary } = await resolveRole(options.task, undefined, options.role);
156
111
  const tier = resolveTier(options.tier, runtime.provider, runtime.model, role);
157
112
  const result = await bridge.run({
158
113
  task: options.task,
@@ -24,6 +24,7 @@ export interface BootstrapOptions {
24
24
  mcpServerPath?: string;
25
25
  llm?: string;
26
26
  model?: string;
27
+ baseUrl?: string;
27
28
  ollamaUrl?: string;
28
29
  }
29
30
  export interface BootstrapResult {
@@ -27,7 +27,7 @@ import { ProjectionManager } from "./materializers/projection-manager.js";
27
27
  import { TodoSyncer } from "./materializers/todo-syncer.js";
28
28
  import { OPERATIONAL_ARTIFACT_REL_PATHS, operationalArtifactKey, } from "./store-artifacts.js";
29
29
  import { STORE_VERSION } from "./types.js";
30
- import { DEFAULT_LLAMA_CPP_MODEL, DEFAULT_OLLAMA_MODEL, } from "../tui/provider-discovery.js";
30
+ import { buildProviderDoctorCommands, defaultModelForProvider, normalizeLocalBaseUrl, normalizeProvider, } from "../tui/provider-discovery.js";
31
31
  const __dirname = dirname(fileURLToPath(import.meta.url));
32
32
  function getAssetsRoot() {
33
33
  // Resolve relative to this file's location in the installed package
@@ -40,45 +40,32 @@ function getAssetsRoot() {
40
40
  async function applyLlmRuntimeConfig(store, opts) {
41
41
  if (!opts.llm)
42
42
  return;
43
- const configuredModel = opts.model ?? (opts.llm === "llama.cpp" ? DEFAULT_LLAMA_CPP_MODEL : DEFAULT_OLLAMA_MODEL);
43
+ const provider = normalizeProvider(opts.llm) ?? opts.llm;
44
+ const configuredModel = opts.model ?? defaultModelForProvider(provider);
45
+ const configuredBaseUrl = normalizeLocalBaseUrl(opts.baseUrl ?? opts.ollamaUrl);
44
46
  const profilePayload = {
45
- provider: opts.llm,
47
+ provider,
46
48
  model: configuredModel,
47
49
  generated_at: new Date().toISOString(),
48
50
  };
49
- if (opts.ollamaUrl) {
50
- profilePayload.base_url = opts.ollamaUrl;
51
- if (opts.llm === "ollama") {
52
- profilePayload.api_compat_base_url = opts.ollamaUrl.endsWith("/v1")
53
- ? opts.ollamaUrl
54
- : `${opts.ollamaUrl}/v1`;
51
+ if (configuredBaseUrl) {
52
+ profilePayload.base_url = configuredBaseUrl;
53
+ if (provider === "ollama") {
54
+ profilePayload.api_compat_base_url = configuredBaseUrl.endsWith("/v1")
55
+ ? configuredBaseUrl
56
+ : `${configuredBaseUrl}/v1`;
55
57
  profilePayload.default_api_key = "ollama";
56
58
  }
57
59
  }
58
60
  await store.setJSON("state/runtime/llm_profile", profilePayload);
59
- const doctorCommands = opts.llm === "ollama"
60
- ? [
61
- "ollama serve",
62
- `ollama pull ${configuredModel}`,
63
- ...(opts.ollamaUrl ? [`curl -s ${opts.ollamaUrl}/api/tags`] : []),
64
- ]
65
- : [
66
- "# Start llama-server separately, for example:",
67
- "# llama-server -m /path/to/model.gguf --port 8080",
68
- ...(opts.ollamaUrl
69
- ? [`curl -s ${opts.ollamaUrl.endsWith("/v1") ? opts.ollamaUrl : `${opts.ollamaUrl}/v1`}/models`]
70
- : []),
71
- ];
61
+ const doctorCommands = buildProviderDoctorCommands(provider, configuredModel, configuredBaseUrl);
72
62
  await store.setBlob("state/runtime/doctor_checks.md", [
73
- `# ACE + ${opts.llm} Doctor Checks`,
63
+ `# ACE + ${provider} Runtime Checks`,
74
64
  "",
75
- "Run these commands to verify local-model readiness:",
65
+ "Run these commands to verify ACE runtime readiness:",
76
66
  "",
77
67
  "```bash",
78
68
  ...doctorCommands,
79
- opts.ollamaUrl
80
- ? `ace doctor --llm ${opts.llm} --model ${configuredModel} --base-url ${opts.ollamaUrl}`
81
- : `ace doctor --llm ${opts.llm} --model ${configuredModel} --scan`,
82
69
  "```",
83
70
  "",
84
71
  ].join("\n"));
@@ -0,0 +1,22 @@
1
+ export interface CacheWorkspaceOptions {
2
+ dryRun?: boolean;
3
+ clean?: boolean;
4
+ }
5
+ export interface CacheWorkspaceResult {
6
+ storePath: string;
7
+ dry_run: boolean;
8
+ clean: boolean;
9
+ scanned_files: number;
10
+ cached_files: number;
11
+ removed_files: number;
12
+ kept_projected_files: number;
13
+ skipped_files: number;
14
+ warnings: string[];
15
+ cached: Array<{
16
+ path: string;
17
+ key: string;
18
+ bytes: number;
19
+ }>;
20
+ }
21
+ export declare function cacheWorkspaceArtifacts(workspaceRoot: string, options?: CacheWorkspaceOptions): Promise<CacheWorkspaceResult>;
22
+ //# sourceMappingURL=cache-workspace.d.ts.map
@@ -0,0 +1,143 @@
1
+ import { existsSync, readdirSync, readFileSync, rmSync } from "node:fs";
2
+ import { dirname, isAbsolute, join, relative } from "node:path";
3
+ import { ACE_ROOT_REL, isProjectedAceWorkspacePath, mapAceWorkspaceRelativePath, resolveStoreFallbackKeysForPath, } from "../helpers.js";
4
+ import { normalizeRelPath } from "../shared.js";
5
+ import { writeStoreBlobsSync } from "./store-artifacts.js";
6
+ const SCAN_ROOTS_REL = [
7
+ "agent-state",
8
+ ".agents/ACE/agent-state",
9
+ ".agents/ACE/tasks",
10
+ ".agents/ACE/skills",
11
+ ];
12
+ function isInside(base, target) {
13
+ const rel = relative(base, target);
14
+ return rel === "" || (!rel.startsWith("..") && !isAbsolute(rel));
15
+ }
16
+ function collectFiles(root) {
17
+ const out = [];
18
+ if (!existsSync(root))
19
+ return out;
20
+ const walk = (dir) => {
21
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
22
+ const abs = join(dir, entry.name);
23
+ if (entry.isDirectory()) {
24
+ walk(abs);
25
+ continue;
26
+ }
27
+ if (entry.isFile()) {
28
+ out.push(abs);
29
+ }
30
+ }
31
+ };
32
+ walk(root);
33
+ return out;
34
+ }
35
+ function pruneEmptyParents(path, stopAt) {
36
+ let cursor = dirname(path);
37
+ while (isInside(stopAt, cursor) && cursor !== stopAt && existsSync(cursor)) {
38
+ const entries = readdirSync(cursor);
39
+ if (entries.length > 0)
40
+ break;
41
+ rmSync(cursor, { recursive: true, force: true });
42
+ cursor = dirname(cursor);
43
+ }
44
+ }
45
+ function isTextBuffer(input) {
46
+ return !input.includes(0);
47
+ }
48
+ function selectScanRoots(workspaceRoot) {
49
+ return SCAN_ROOTS_REL.map((relPath) => join(workspaceRoot, relPath)).filter((abs) => existsSync(abs));
50
+ }
51
+ export async function cacheWorkspaceArtifacts(workspaceRoot, options = {}) {
52
+ const dryRun = options.dryRun ?? false;
53
+ const clean = options.clean ?? true;
54
+ const storePath = join(workspaceRoot, ".agents", "ACE", "ace-state.ace");
55
+ if (!existsSync(storePath)) {
56
+ throw new Error(`No store found at ${storePath}. Run 'ace init' first.`);
57
+ }
58
+ const warnings = [];
59
+ const scanRoots = selectScanRoots(workspaceRoot);
60
+ const candidates = [];
61
+ let scanned = 0;
62
+ let keptProjected = 0;
63
+ let skipped = 0;
64
+ for (const scanRoot of scanRoots) {
65
+ for (const absPath of collectFiles(scanRoot)) {
66
+ scanned += 1;
67
+ const relPath = normalizeRelPath(relative(workspaceRoot, absPath));
68
+ const canonicalPath = mapAceWorkspaceRelativePath(relPath);
69
+ if (!canonicalPath.startsWith(`${ACE_ROOT_REL}/`)) {
70
+ skipped += 1;
71
+ continue;
72
+ }
73
+ if (canonicalPath === `${ACE_ROOT_REL}/ace-state.ace`) {
74
+ skipped += 1;
75
+ continue;
76
+ }
77
+ if (isProjectedAceWorkspacePath(canonicalPath)) {
78
+ keptProjected += 1;
79
+ continue;
80
+ }
81
+ const raw = readFileSync(absPath);
82
+ if (!isTextBuffer(raw)) {
83
+ skipped += 1;
84
+ warnings.push(`Skipped non-text artifact: ${canonicalPath}`);
85
+ continue;
86
+ }
87
+ const fallbackKeys = resolveStoreFallbackKeysForPath(canonicalPath);
88
+ if (fallbackKeys.length === 0) {
89
+ skipped += 1;
90
+ warnings.push(`No store fallback key for ${canonicalPath}`);
91
+ continue;
92
+ }
93
+ candidates.push({
94
+ absPath,
95
+ canonicalPath,
96
+ key: fallbackKeys[0],
97
+ content: raw.toString("utf-8"),
98
+ bytes: raw.byteLength,
99
+ });
100
+ }
101
+ }
102
+ const dedupByKey = new Map();
103
+ for (const candidate of candidates) {
104
+ dedupByKey.set(candidate.key, candidate);
105
+ }
106
+ const uniqueForWrite = [...dedupByKey.values()];
107
+ if (!dryRun && uniqueForWrite.length > 0) {
108
+ writeStoreBlobsSync(workspaceRoot, uniqueForWrite.map((candidate) => ({
109
+ key: candidate.key,
110
+ content: candidate.content,
111
+ })));
112
+ }
113
+ let removed = 0;
114
+ if (!dryRun && clean && candidates.length > 0) {
115
+ const removedPaths = new Set();
116
+ for (const candidate of candidates) {
117
+ if (removedPaths.has(candidate.absPath))
118
+ continue;
119
+ rmSync(candidate.absPath, { force: true });
120
+ removedPaths.add(candidate.absPath);
121
+ removed += 1;
122
+ const pruneRoot = scanRoots.find((root) => isInside(root, candidate.absPath)) ?? workspaceRoot;
123
+ pruneEmptyParents(candidate.absPath, pruneRoot);
124
+ }
125
+ }
126
+ return {
127
+ storePath,
128
+ dry_run: dryRun,
129
+ clean,
130
+ scanned_files: scanned,
131
+ cached_files: candidates.length,
132
+ removed_files: removed,
133
+ kept_projected_files: keptProjected,
134
+ skipped_files: skipped,
135
+ warnings,
136
+ cached: candidates.map((candidate) => ({
137
+ path: candidate.canonicalPath,
138
+ key: candidate.key,
139
+ bytes: candidate.bytes,
140
+ })),
141
+ };
142
+ }
143
+ //# sourceMappingURL=cache-workspace.js.map
@@ -26,11 +26,6 @@ export class ContextSnapshotMaterializer {
26
26
  if (!existsSync(dir))
27
27
  mkdirSync(dir, { recursive: true });
28
28
  const snapshots = await this.repo.listSnapshots();
29
- const retained = new Set(["index.json"]);
30
- for (const snapshot of snapshots) {
31
- retained.add(snapshot.file);
32
- writeText(join(dir, snapshot.file), `${JSON.stringify(snapshot.record, null, 2)}\n`);
33
- }
34
29
  writeText(join(dir, "index.json"), `${JSON.stringify({
35
30
  snapshots: snapshots.map(({ name, file, timestamp, summary }) => ({
36
31
  name,
@@ -42,7 +37,7 @@ export class ContextSnapshotMaterializer {
42
37
  for (const file of readdirSync(dir)) {
43
38
  if (!file.endsWith(".json"))
44
39
  continue;
45
- if (retained.has(file))
40
+ if (file === "index.json")
46
41
  continue;
47
42
  rmSync(join(dir, file), { force: true });
48
43
  }