@voybio/ace-swarm 0.2.4 → 2.4.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 (125) hide show
  1. package/CHANGELOG.md +11 -1
  2. package/README.md +20 -13
  3. package/assets/.agents/skills/eval-harness/SKILL.md +14 -0
  4. package/assets/.agents/skills/handoff-lint/SKILL.md +14 -0
  5. package/assets/.agents/skills/incident-commander/SKILL.md +14 -0
  6. package/assets/.agents/skills/memory-curator/SKILL.md +14 -0
  7. package/assets/.agents/skills/release-sentry/SKILL.md +14 -0
  8. package/assets/.agents/skills/risk-quant/SKILL.md +14 -0
  9. package/assets/.agents/skills/schema-forge/SKILL.md +14 -0
  10. package/assets/.agents/skills/state-auditor/SKILL.md +14 -0
  11. package/assets/agent-state/EVIDENCE_LOG.md +1 -1
  12. package/assets/agent-state/MODULES/gates/gate-correctness.json +1 -1
  13. package/assets/agent-state/MODULES/roles/capability-framework.json +41 -0
  14. package/assets/agent-state/MODULES/roles/capability-git.json +33 -0
  15. package/assets/agent-state/MODULES/roles/capability-safety.json +37 -0
  16. package/assets/agent-state/MODULES/schemas/ACE_RUNTIME_PROFILE.schema.json +21 -0
  17. package/assets/agent-state/MODULES/schemas/RUNTIME_EXECUTOR_SESSION_REGISTRY.schema.json +43 -0
  18. package/assets/agent-state/MODULES/schemas/WORKSPACE_SESSION_REGISTRY.schema.json +11 -0
  19. package/assets/agent-state/STATUS.md +2 -2
  20. package/assets/scripts/ace-hook-dispatch.mjs +70 -6
  21. package/assets/scripts/render-mcp-configs.sh +19 -5
  22. package/dist/ace-context.js +22 -1
  23. package/dist/ace-server-instructions.js +3 -3
  24. package/dist/ace-state-resolver.js +5 -3
  25. package/dist/astgrep-index.d.ts +9 -1
  26. package/dist/astgrep-index.js +14 -3
  27. package/dist/cli.js +52 -20
  28. package/dist/handoff-registry.js +5 -5
  29. package/dist/helpers/artifacts.d.ts +19 -0
  30. package/dist/helpers/artifacts.js +152 -0
  31. package/dist/helpers/bootstrap.d.ts +24 -0
  32. package/dist/helpers/bootstrap.js +894 -0
  33. package/dist/helpers/constants.d.ts +53 -0
  34. package/dist/helpers/constants.js +288 -0
  35. package/dist/helpers/drift.d.ts +13 -0
  36. package/dist/helpers/drift.js +45 -0
  37. package/dist/helpers/path-utils.d.ts +17 -0
  38. package/dist/helpers/path-utils.js +104 -0
  39. package/dist/helpers/store-resolution.d.ts +19 -0
  40. package/dist/helpers/store-resolution.js +301 -0
  41. package/dist/helpers/workspace-root.d.ts +3 -0
  42. package/dist/helpers/workspace-root.js +80 -0
  43. package/dist/helpers.d.ts +8 -123
  44. package/dist/helpers.js +8 -1747
  45. package/dist/job-scheduler.js +3 -3
  46. package/dist/local-model-runtime.js +12 -1
  47. package/dist/model-bridge.d.ts +7 -0
  48. package/dist/model-bridge.js +75 -5
  49. package/dist/orchestrator-supervisor.d.ts +14 -0
  50. package/dist/orchestrator-supervisor.js +72 -1
  51. package/dist/run-ledger.js +3 -3
  52. package/dist/runtime-command.d.ts +8 -0
  53. package/dist/runtime-command.js +38 -6
  54. package/dist/runtime-executor.d.ts +14 -0
  55. package/dist/runtime-executor.js +669 -171
  56. package/dist/runtime-profile.d.ts +32 -0
  57. package/dist/runtime-profile.js +89 -13
  58. package/dist/runtime-tool-specs.d.ts +21 -0
  59. package/dist/runtime-tool-specs.js +78 -3
  60. package/dist/safe-edit.d.ts +7 -0
  61. package/dist/safe-edit.js +163 -37
  62. package/dist/schemas.js +19 -0
  63. package/dist/shared.d.ts +2 -2
  64. package/dist/status-events.js +9 -6
  65. package/dist/store/ace-packed-store.d.ts +3 -2
  66. package/dist/store/ace-packed-store.js +188 -110
  67. package/dist/store/bootstrap-store.d.ts +1 -1
  68. package/dist/store/bootstrap-store.js +94 -81
  69. package/dist/store/cache-workspace.d.ts +22 -0
  70. package/dist/store/cache-workspace.js +149 -0
  71. package/dist/store/materializers/context-snapshot-materializer.js +6 -7
  72. package/dist/store/materializers/hook-context-materializer.d.ts +6 -9
  73. package/dist/store/materializers/hook-context-materializer.js +11 -21
  74. package/dist/store/materializers/host-file-materializer.js +6 -0
  75. package/dist/store/materializers/projection-manager.d.ts +0 -1
  76. package/dist/store/materializers/projection-manager.js +5 -13
  77. package/dist/store/materializers/scheduler-projection-materializer.js +1 -1
  78. package/dist/store/materializers/vericify-projector.d.ts +7 -7
  79. package/dist/store/materializers/vericify-projector.js +11 -11
  80. package/dist/store/repositories/local-model-runtime-repository.d.ts +120 -3
  81. package/dist/store/repositories/local-model-runtime-repository.js +242 -6
  82. package/dist/store/skills-install.d.ts +4 -0
  83. package/dist/store/skills-install.js +21 -12
  84. package/dist/store/state-reader.d.ts +2 -0
  85. package/dist/store/state-reader.js +20 -0
  86. package/dist/store/store-artifacts.d.ts +7 -0
  87. package/dist/store/store-artifacts.js +27 -1
  88. package/dist/store/store-authority-audit.d.ts +18 -1
  89. package/dist/store/store-authority-audit.js +115 -5
  90. package/dist/store/store-snapshot.d.ts +3 -0
  91. package/dist/store/store-snapshot.js +22 -2
  92. package/dist/store/workspace-store-paths.d.ts +39 -0
  93. package/dist/store/workspace-store-paths.js +94 -0
  94. package/dist/store/write-coordinator.d.ts +65 -0
  95. package/dist/store/write-coordinator.js +386 -0
  96. package/dist/todo-state.js +5 -5
  97. package/dist/tools-agent.js +319 -34
  98. package/dist/tools-discovery.js +1 -1
  99. package/dist/tools-files.d.ts +7 -0
  100. package/dist/tools-files.js +299 -10
  101. package/dist/tools-framework.js +107 -27
  102. package/dist/tools-handoff.js +2 -2
  103. package/dist/tools-lifecycle.js +4 -4
  104. package/dist/tools-memory.js +6 -6
  105. package/dist/tools-todo.js +2 -2
  106. package/dist/tracker-adapters.d.ts +1 -1
  107. package/dist/tracker-adapters.js +13 -18
  108. package/dist/tracker-sync.js +5 -3
  109. package/dist/tui/agent-runner.js +3 -1
  110. package/dist/tui/chat.js +103 -7
  111. package/dist/tui/dashboard.d.ts +1 -0
  112. package/dist/tui/dashboard.js +43 -0
  113. package/dist/tui/layout.d.ts +20 -0
  114. package/dist/tui/layout.js +31 -1
  115. package/dist/tui/local-model-contract.d.ts +6 -2
  116. package/dist/tui/local-model-contract.js +16 -3
  117. package/dist/vericify-bridge.d.ts +5 -0
  118. package/dist/vericify-bridge.js +27 -3
  119. package/dist/workspace-manager.d.ts +30 -3
  120. package/dist/workspace-manager.js +257 -27
  121. package/package.json +1 -2
  122. package/dist/internal-tool-runtime.d.ts +0 -21
  123. package/dist/internal-tool-runtime.js +0 -136
  124. package/dist/store/workspace-snapshot.d.ts +0 -26
  125. package/dist/store/workspace-snapshot.js +0 -107
@@ -28,6 +28,9 @@ const ACE_CORE_FILES = [
28
28
  "agent-state/SCOPE.md",
29
29
  "agent-state/EVIDENCE_LOG.md",
30
30
  ];
31
+ const STORE_MAGIC = "ACEPACK1";
32
+ const STORE_HEADER_SIZE = 32;
33
+ const HOOK_CONTEXT_STORE_KEY = "state/runtime/hook_context";
31
34
 
32
35
  const PROTECTED_PATH_FRAGMENTS = [
33
36
  ".github/hooks/",
@@ -114,9 +117,61 @@ function resolveAcePath(workspaceRoot, relativePath) {
114
117
  return canonical;
115
118
  }
116
119
 
120
+ function getWorkspaceStorePath(workspaceRoot) {
121
+ const canonical = resolve(workspaceRoot, "agent-state", "ace-state.ace");
122
+ if (existsSync(canonical)) return canonical;
123
+ return resolve(workspaceRoot, ACE_ROOT, "ace-state.ace");
124
+ }
125
+
126
+ function readStoreUint64(buffer, offset) {
127
+ return buffer.readUInt32BE(offset) * 2 ** 32 + buffer.readUInt32BE(offset + 4);
128
+ }
129
+
130
+ function readStoreBlob(workspaceRoot, key) {
131
+ const storePath = getWorkspaceStorePath(workspaceRoot);
132
+ if (!existsSync(storePath)) return undefined;
133
+ try {
134
+ const file = readFileSync(storePath);
135
+ if (file.length < STORE_HEADER_SIZE) return undefined;
136
+ if (file.subarray(0, 8).toString("utf8") !== STORE_MAGIC) return undefined;
137
+ const indexOffset = readStoreUint64(file, 16);
138
+ const indexLength = readStoreUint64(file, 24);
139
+ const index = JSON.parse(file.subarray(indexOffset, indexOffset + indexLength).toString("utf8"));
140
+ const entry = index?.[key];
141
+ if (!entry || typeof entry.offset !== "number" || typeof entry.length !== "number") {
142
+ return undefined;
143
+ }
144
+ const start = entry.offset + 4;
145
+ const end = start + entry.length;
146
+ return file.subarray(start, end).toString("utf8");
147
+ } catch {
148
+ return undefined;
149
+ }
150
+ }
151
+
117
152
  function readIfPresent(workspaceRoot, relativePath) {
118
153
  const absolute = resolveAcePath(workspaceRoot, relativePath);
119
- if (!existsSync(absolute)) return undefined;
154
+ if (!existsSync(absolute)) {
155
+ const normalized = String(relativePath).replace(/\\/g, "/").replace(/^\.?\//, "");
156
+ const logicalPath = normalized.startsWith(`${ACE_ROOT}/`)
157
+ ? normalized.slice(`${ACE_ROOT}/`.length)
158
+ : normalized;
159
+ const rel = logicalPath.startsWith("agent-state/") ? logicalPath.slice("agent-state/".length) : "";
160
+ const keys = [];
161
+ if (rel) {
162
+ keys.push(`state/artifacts/agent-state/${rel}`);
163
+ keys.push(`knowledge/agent-state/${rel}`);
164
+ if (rel === "ACE_WORKFLOW.md") {
165
+ keys.unshift("knowledge/runtime/ACE_WORKFLOW.md");
166
+ keys.push("knowledge/kernel/ACE_WORKFLOW.md");
167
+ }
168
+ }
169
+ for (const key of keys) {
170
+ const blob = readStoreBlob(workspaceRoot, key);
171
+ if (typeof blob === "string") return blob;
172
+ }
173
+ return undefined;
174
+ }
120
175
  try {
121
176
  return readFileSync(absolute, "utf8");
122
177
  } catch {
@@ -203,12 +258,19 @@ function buildRoleToolHint(role) {
203
258
  }
204
259
 
205
260
  /**
206
- * Try to read the store-materialized hook context snapshot.
261
+ * Try to read the store-backed hook context snapshot.
207
262
  * Returns the parsed snapshot or undefined if not present / unreadable.
208
- * When present this is the single authoritative source; the dispatcher
209
- * never needs to open the .ace store directly.
210
263
  */
211
264
  function tryReadHookContextSnapshot(workspaceRoot) {
265
+ const storeRaw = readStoreBlob(workspaceRoot, HOOK_CONTEXT_STORE_KEY);
266
+ if (typeof storeRaw === "string") {
267
+ try {
268
+ const parsed = JSON.parse(storeRaw);
269
+ if (parsed?.schema_version === 1) return parsed;
270
+ } catch {
271
+ // Fall through to legacy file compatibility.
272
+ }
273
+ }
212
274
  const snapshotPath = resolve(workspaceRoot, ACE_ROOT, "ace-hook-context.json");
213
275
  if (!existsSync(snapshotPath)) return undefined;
214
276
  try {
@@ -222,7 +284,7 @@ function tryReadHookContextSnapshot(workspaceRoot) {
222
284
  }
223
285
 
224
286
  function aceContext(workspaceRoot) {
225
- // ── Fast path: store-materialized snapshot ──────────────────────────────
287
+ // ── Fast path: store-backed snapshot ────────────────────────────────────
226
288
  const snapshot = tryReadHookContextSnapshot(workspaceRoot);
227
289
  if (snapshot) {
228
290
  const taskObjective = snapshot.task?.content
@@ -233,7 +295,9 @@ function aceContext(workspaceRoot) {
233
295
  const scopeReminder = scopeLines.length
234
296
  ? `ACE scope reminder: ${truncate(scopeLines.slice(0, 4).join(" "), 220)}`
235
297
  : undefined;
236
- const hasAgentState = existsSync(resolve(workspaceRoot, ACE_ROOT, "agent-state"));
298
+ const hasAgentState =
299
+ existsSync(resolve(workspaceRoot, ACE_ROOT, "agent-state")) ||
300
+ existsSync(resolve(workspaceRoot, "agent-state", "ace-state.ace"));
237
301
  const hasSkills = existsSync(resolve(workspaceRoot, ACE_ROOT, "skills")) ||
238
302
  existsSync(resolve(workspaceRoot, ".agents", "skills"));
239
303
  return {
@@ -12,6 +12,9 @@ CURSOR_DIR="${ACE_ROOT}/.cursor"
12
12
  VSCODE_DIR="${ACE_ROOT}/.vscode"
13
13
  HOOK_COMMAND="node ./.agents/ACE/scripts/ace/ace-hook-dispatch.mjs"
14
14
  HOOK_COMMAND_WINDOWS="node .\\\\.agents\\\\ACE\\\\scripts\\\\ace\\\\ace-hook-dispatch.mjs"
15
+ NODE_BIN_DIR="$(dirname -- "$(command -v node)")"
16
+ CLI_ENTRY="${WORKSPACE_ROOT}/dist/cli.js"
17
+ MCP_LAUNCH="export PATH=\"${NODE_BIN_DIR}:\$PATH\" && if ! command -v node >/dev/null 2>&1; then [ -s \"\$HOME/.zprofile\" ] && . \"\$HOME/.zprofile\"; [ -s \"\$HOME/.zshrc\" ] && . \"\$HOME/.zshrc\"; fi && cd \"${WORKSPACE_ROOT}\" && ACE_WORKSPACE_ROOT=\"${WORKSPACE_ROOT}\" node \"${CLI_ENTRY}\" mcp"
15
18
 
16
19
  mkdir -p "${MCP_CONFIG_DIR}"
17
20
  mkdir -p "${GITHUB_DIR}/hooks"
@@ -25,7 +28,18 @@ cat > "${MCP_CONFIG_DIR}/vscode.mcp.json" <<JSON
25
28
  "ace-swarm": {
26
29
  "type": "stdio",
27
30
  "command": "/bin/zsh",
28
- "args": ["-lc", "cd \"${WORKSPACE_ROOT}\" && ACE_WORKSPACE_ROOT=\"${WORKSPACE_ROOT}\" npx -y ace-swarm mcp"]
31
+ "args": ["-lc", "${MCP_LAUNCH}"]
32
+ }
33
+ }
34
+ }
35
+ JSON
36
+
37
+ cat > "${WORKSPACE_ROOT}/.mcp.json" <<JSON
38
+ {
39
+ "mcpServers": {
40
+ "ace-swarm": {
41
+ "command": "/bin/zsh",
42
+ "args": ["-lc", "${MCP_LAUNCH}"]
29
43
  }
30
44
  }
31
45
  }
@@ -36,7 +50,7 @@ cat > "${MCP_CONFIG_DIR}/cursor.mcp.json" <<JSON
36
50
  "mcpServers": {
37
51
  "ace-swarm": {
38
52
  "command": "/bin/zsh",
39
- "args": ["-lc", "cd \"${WORKSPACE_ROOT}\" && ACE_WORKSPACE_ROOT=\"${WORKSPACE_ROOT}\" npx -y ace-swarm mcp"]
53
+ "args": ["-lc", "${MCP_LAUNCH}"]
40
54
  }
41
55
  }
42
56
  }
@@ -47,7 +61,7 @@ cat > "${MCP_CONFIG_DIR}/claude_desktop_config.json" <<JSON
47
61
  "mcpServers": {
48
62
  "ace-swarm": {
49
63
  "command": "/bin/zsh",
50
- "args": ["-lc", "cd \"${WORKSPACE_ROOT}\" && ACE_WORKSPACE_ROOT=\"${WORKSPACE_ROOT}\" npx -y ace-swarm mcp"]
64
+ "args": ["-lc", "${MCP_LAUNCH}"]
51
65
  }
52
66
  }
53
67
  }
@@ -58,7 +72,7 @@ cat > "${MCP_CONFIG_DIR}/antigravity.mcp.json" <<JSON
58
72
  "mcpServers": {
59
73
  "ace-swarm": {
60
74
  "command": "/bin/zsh",
61
- "args": ["-lc", "cd \"${WORKSPACE_ROOT}\" && ACE_WORKSPACE_ROOT=\"${WORKSPACE_ROOT}\" npx -y ace-swarm mcp"]
75
+ "args": ["-lc", "${MCP_LAUNCH}"]
62
76
  }
63
77
  }
64
78
  }
@@ -68,7 +82,7 @@ cat > "${MCP_CONFIG_DIR}/codex.config.toml" <<TOML
68
82
  # ACE instructions are scaffolded into .agents/ACE/AGENTS.md during bootstrap.
69
83
  [mcp_servers.ace-swarm]
70
84
  command = "/bin/zsh"
71
- args = ["-lc", "cd \"${WORKSPACE_ROOT}\" && ACE_WORKSPACE_ROOT=\"${WORKSPACE_ROOT}\" npx -y ace-swarm mcp"]
85
+ args = ["-lc", "${MCP_LAUNCH}"]
72
86
  TOML
73
87
 
74
88
  cat > "${ACE_ROOT}/CLAUDE.md" <<MD
@@ -1,6 +1,6 @@
1
1
  import { buildAceContinuityPacket, buildAceRecallContext, formatAceContinuityPacketMarkdown, formatAceRecallMarkdown, normalizeAutonomyPolicy, normalizeContinuityPolicy, } from "./ace-autonomy.js";
2
2
  import { listAceInternalToolCatalog, } from "./ace-internal-tools.js";
3
- import { ALL_AGENTS, readAgentInstructions } from "./helpers.js";
3
+ import { ALL_AGENTS, listAvailableSkills, readAgentInstructions } from "./helpers.js";
4
4
  import { readRuntimeProfileState, renderRuntimePrompt } from "./runtime-profile.js";
5
5
  const ROLE_TOOL_DEFAULTS = {
6
6
  orchestrator: [
@@ -156,6 +156,23 @@ function buildPlanningCatalog(role, task, tier) {
156
156
  .slice(0, limit)
157
157
  .map((entry) => entry.tool);
158
158
  }
159
+ function inferRelevantSkills(task) {
160
+ const lowered = task.toLowerCase();
161
+ const skillSignals = [
162
+ { name: "state-auditor", pattern: /\b(state|drift|authority|audit)\b/ },
163
+ { name: "handoff-lint", pattern: /\b(handoff|transition|payload)\b/ },
164
+ { name: "schema-forge", pattern: /\b(schema|contract|interface)\b/ },
165
+ { name: "release-sentry", pattern: /\b(ship|release|promotion|rollback)\b/ },
166
+ { name: "risk-quant", pattern: /\b(risk|blocker|severity)\b/ },
167
+ { name: "eval-harness", pattern: /\b(eval|regression|verify|validation)\b/ },
168
+ { name: "incident-commander", pattern: /\b(incident|outage|blocker storm)\b/ },
169
+ { name: "problem-triage", pattern: /\b(triage|classify|scope)\b/ },
170
+ ];
171
+ const available = new Set(listAvailableSkills().map((skill) => skill.name));
172
+ return skillSignals
173
+ .filter((signal) => signal.pattern.test(lowered) && available.has(signal.name))
174
+ .map((signal) => signal.name);
175
+ }
159
176
  export function buildToolPlan(options) {
160
177
  const tier = options.tier ?? "compressed";
161
178
  const catalog = Array.isArray(options.tools) && options.tools.length > 0
@@ -211,6 +228,7 @@ export function renderAceContext(options) {
211
228
  const roleInstructions = resolveRoleInstructions(options.role);
212
229
  const toolCatalog = renderToolCatalogMarkdown(tools, tier);
213
230
  const stateDigest = recall.task_contract.summary;
231
+ const relevantSkills = inferRelevantSkills(options.task);
214
232
  const systemPrompt = [
215
233
  runtimePrompt,
216
234
  "",
@@ -223,6 +241,9 @@ export function renderAceContext(options) {
223
241
  "## Tool Scope",
224
242
  toolCatalog || "- No ACE tools available.",
225
243
  "",
244
+ "## Relevant Skills",
245
+ relevantSkills.length > 0 ? `- ${relevantSkills.join(", ")}` : "- No task-specific skill hint matched.",
246
+ "",
226
247
  "## Compact Guardrails",
227
248
  "- Stop if you are about to claim tested, verified, or passed without evidence.",
228
249
  "- If ACE state is thin or contradictory, call recall_context or validate_framework before improvising.",
@@ -229,10 +229,10 @@ export function buildHostInstructionText(host, workspaceRoot) {
229
229
  "3. Prefer ACE prompts/resources (`ace-orchestrator`, `get_task_pack`, `get_skill_instructions`) over relying on this file.",
230
230
  "",
231
231
  "Ground truth:",
232
- "- `agent-state/*` is the logical state surface; the resolver reads it from top-level, nested, or store-backed layouts.",
233
- "- `.agents/ACE/ace-state.ace` remains the canonical store-backed projection when that layout is used.",
232
+ "- `agent-state/*` is the visible runtime state surface for this workspace.",
233
+ "- `agent-state/ace-state.ace` is the canonical store path; legacy nested store layouts are compatibility fallback only.",
234
234
  "- `.agents/ACE/tasks/todo.md` is the human-facing todo surface.",
235
- "- `.agents/ACE/ace-hook-context.json` is the compact hook snapshot.",
235
+ "- Hook context is store-backed in `agent-state/ace-state.ace` and consumed without extra workspace projections.",
236
236
  "",
237
237
  `Workspace digest hint: ${buildAceDigest(workspaceRoot)}.`,
238
238
  ].join("\n");
@@ -95,9 +95,11 @@ export function resolveAceStateLayout(workspaceRoot) {
95
95
  scopePath: resolveAceLogicalPath(workspaceRoot, "agent-state/SCOPE.md"),
96
96
  evidencePath: resolveAceLogicalPath(workspaceRoot, "agent-state/EVIDENCE_LOG.md"),
97
97
  storePath: storePresent ? storePath : undefined,
98
- hookSnapshotPath: existsSync(nestedAcePath(workspaceRoot, "ace-hook-context.json"))
99
- ? nestedAcePath(workspaceRoot, "ace-hook-context.json")
100
- : undefined,
98
+ hookSnapshotPath: readStoreBlobSync(workspaceRoot, "state/runtime/hook_context") != null
99
+ ? toVirtualStorePath(storePath, "state/runtime/hook_context")
100
+ : existsSync(nestedAcePath(workspaceRoot, "ace-hook-context.json"))
101
+ ? nestedAcePath(workspaceRoot, "ace-hook-context.json")
102
+ : undefined,
101
103
  isAcePresent: physicalMode !== "missing" ||
102
104
  CRITICAL_LOGICAL_PATHS.some((relativePath) => typeof readAceLogicalFile(workspaceRoot, relativePath) === "string"),
103
105
  missingCriticalArtifacts,
@@ -1,3 +1,11 @@
1
+ export interface AstgrepMatch {
2
+ file: string;
3
+ line: number;
4
+ column: number;
5
+ text: string;
6
+ context_lines?: string[];
7
+ }
8
+ export declare function runAstgrepQuery(pattern: string, lang: string, roots: string[], _contextLines?: number): AstgrepMatch[];
1
9
  export interface RefreshAstgrepIndexInput {
2
10
  scope?: string;
3
11
  append_evidence?: boolean;
@@ -20,5 +28,5 @@ export interface RefreshAstgrepIndexResult {
20
28
  todo_signals: number;
21
29
  };
22
30
  }
23
- export declare function refreshAstgrepIndex(input?: RefreshAstgrepIndexInput): RefreshAstgrepIndexResult;
31
+ export declare function refreshAstgrepIndex(input?: RefreshAstgrepIndexInput): Promise<RefreshAstgrepIndexResult>;
24
32
  //# sourceMappingURL=astgrep-index.d.ts.map
@@ -1,8 +1,19 @@
1
1
  import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
2
2
  import { isAbsolute, relative, resolve } from "node:path";
3
3
  import { spawnSync } from "node:child_process";
4
- import { appendStatusEvent } from "./status-events.js";
4
+ import { appendStatusEventSafe } from "./status-events.js";
5
5
  import { safeRead, safeWrite, WORKSPACE_ROOT, wsPath } from "./helpers.js";
6
+ export function runAstgrepQuery(pattern, lang, roots, _contextLines) {
7
+ const cmd = detectAstgrepCommand();
8
+ const raw = runAstgrep(cmd, pattern, lang, roots);
9
+ return raw.slice(0, 200).map((m) => ({
10
+ file: m.file ?? "",
11
+ line: m.range?.start?.line ?? 0,
12
+ column: m.range?.start?.column ?? 0,
13
+ text: m.text ?? "",
14
+ context_lines: [],
15
+ }));
16
+ }
6
17
  const CODE_EXTENSIONS = new Set(["ts", "tsx", "js", "mjs", "cjs", "py", "rs", "go"]);
7
18
  const EXCLUDE_DIRS = new Set([
8
19
  ".git",
@@ -180,7 +191,7 @@ function appendEvidenceLine(timestamp, astgrepCmd, stats) {
180
191
  ].join("\n");
181
192
  safeWrite("agent-state/EVIDENCE_LOG.md", `${seed}${entry}\n`);
182
193
  }
183
- export function refreshAstgrepIndex(input = {}) {
194
+ export async function refreshAstgrepIndex(input = {}) {
184
195
  const generatedAt = new Date().toISOString();
185
196
  const scope = resolveScope(input.scope);
186
197
  const appendEvidence = input.append_evidence ?? true;
@@ -440,7 +451,7 @@ export function refreshAstgrepIndex(input = {}) {
440
451
  if (emitEvent) {
441
452
  const status = astgrepCmd ? "pass" : "blocked";
442
453
  const eventType = astgrepCmd ? "DISCOVERY_INDEX_READY" : "DISCOVERY_SCOPE_BLOCKED";
443
- const event = appendStatusEvent({
454
+ const event = await appendStatusEventSafe({
444
455
  source_module: "capability-astgrep",
445
456
  event_type: eventType,
446
457
  status,
package/dist/cli.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { ACE_ROOT_REL, ACE_TASKS_ROOT_REL, ALL_MCP_CLIENTS, ALL_LLM_PROVIDERS, DEFAULTS_ROOT, PACKAGE_ROOT, WORKSPACE_ROOT, fileExists, getAllMcpServerConfigSnippets, getMcpClientInstallHint, getMcpServerConfigSnippet, wsPath, } from "./helpers.js";
2
+ import { ACE_TASKS_ROOT_REL, ALL_MCP_CLIENTS, ALL_LLM_PROVIDERS, DEFAULTS_ROOT, PACKAGE_ROOT, WORKSPACE_ROOT, fileExists, getAllMcpServerConfigSnippets, getMcpClientInstallHint, getMcpServerConfigSnippet, wsPath, } from "./helpers.js";
3
3
  import { refreshAstgrepIndex } from "./astgrep-index.js";
4
4
  import { scanWorkspaceDelta } from "./index-store.js";
5
5
  import { startStdioServer } from "./server.js";
@@ -8,9 +8,10 @@ import { waitForPendingStatusEventMirrors } from "./status-events.js";
8
8
  import { bootstrapStoreWorkspace } from "./store/bootstrap-store.js";
9
9
  import { HostFileMaterializer } from "./store/materializers/host-file-materializer.js";
10
10
  import { openStore } from "./store/ace-packed-store.js";
11
- import { withStoreWriteQueue } from "./store/write-queue.js";
12
11
  import { DiscoveryRepository } from "./store/repositories/discovery-repository.js";
12
+ import { withStoreWriteCoordinator } from "./store/write-coordinator.js";
13
13
  import { getWorkspaceStorePath, readStoreBlobSync, readStoreJsonSync, } from "./store/store-snapshot.js";
14
+ import { ensureCanonicalWorkspaceStore } from "./store/workspace-store-paths.js";
14
15
  import { readFileSync } from "node:fs";
15
16
  import { runTui } from "./tui/index.js";
16
17
  import { buildOpenAiCompatibleBaseUrl, buildProviderDoctorCommands, defaultModelForProvider, discoverProviderContext, isLocalLlmProvider, normalizeLocalBaseUrl, normalizeProvider, scanLocalModelRuntimes, } from "./tui/provider-discovery.js";
@@ -25,6 +26,7 @@ Usage:
25
26
  ace init [options] Bootstrap the ACE store into current workspace
26
27
  ace turnkey [options] Project minimal workspace bootstrap stubs from the ACE store
27
28
  ace doctor [options] Validate ACE runtime + MCP readiness
29
+ ace cache [options] Cache ACE artifacts into ace-state.ace and optionally clean projections
28
30
  ace mcp-config [options] Print global/client MCP config snippet(s) from store
29
31
  ace preconfig Write .mcp-config/ bundle for all supported clients to workspace root
30
32
  ace paths Show resolved package/workspace paths
@@ -37,7 +39,7 @@ Options for tui:
37
39
  --ollama-url <url> Legacy alias for --base-url
38
40
 
39
41
  Options for init:
40
- --project <name> Project name stored in ${ACE_ROOT_REL}/ace-state.ace metadata
42
+ --project <name> Project name stored in agent-state/ace-state.ace metadata
41
43
  --force Overwrite scaffolded files if they already exist
42
44
  --mcp-config Also write .vscode/mcp.json workspace bridge
43
45
  --client-config-bundle Also write minimal workspace host stubs (AGENTS.md, CLAUDE.md, .cursorrules, .github/copilot-instructions.md)
@@ -47,18 +49,23 @@ Options for init:
47
49
  --ollama-url <url> Legacy alias for --base-url
48
50
 
49
51
  Options for doctor:
50
- --llm <provider> ollama|llama.cpp|codex|claude|gemini|copilot|... (default: auto from ${ACE_ROOT_REL}/ace-state.ace)
52
+ --llm <provider> ollama|llama.cpp|codex|claude|gemini|copilot|... (default: auto from agent-state/ace-state.ace)
51
53
  --model <name> Model name override
52
54
  --base-url <url> Runtime base URL override
53
55
  --ollama-url <url> Legacy alias for --base-url
54
56
  --scan Probe common local Ollama + llama.cpp endpoints when URL is unset
55
57
 
58
+ Options for cache:
59
+ --dry-run Preview what would be cached and cleaned (no writes/deletes)
60
+ --no-clean Keep workspace ACE artifacts after caching them into ace-state.ace
61
+
56
62
  Options for mcp-config:
57
- --client <name> codex|vscode|claude|cursor|antigravity
63
+ --client <name> codex|vscode|copilot|claude|cursor|antigravity
58
64
  --all Print all client snippets for optional global install
59
65
 
60
66
  preconfig writes .mcp-config/ at the workspace root with ready-to-use config files for every
61
- supported MCP client. Run once after ace init. Each file includes the install hint for its client.
67
+ supported MCP client plus a root .mcp.json for GitHub Copilot CLI. Run once after ace init.
68
+ Each file includes the install hint for its client.
62
69
  `);
63
70
  }
64
71
  function readFlagValue(args, flag) {
@@ -111,7 +118,7 @@ function parseLlmOptions(args) {
111
118
  }
112
119
  async function writeLlmProfile(profile) {
113
120
  const storePath = getWorkspaceStorePath(WORKSPACE_ROOT);
114
- await withStoreWriteQueue(storePath, async () => {
121
+ await withStoreWriteCoordinator(storePath, async () => {
115
122
  const payload = {
116
123
  provider: profile.provider,
117
124
  model: profile.model,
@@ -143,12 +150,12 @@ async function writeLlmProfile(profile) {
143
150
  finally {
144
151
  await store.close();
145
152
  }
146
- });
153
+ }, { operation_label: "writeLlmProfile" });
147
154
  return `${storePath}#state/runtime/llm_profile`;
148
155
  }
149
156
  async function recordDiscoveryProfile(input) {
150
157
  const storePath = getWorkspaceStorePath(WORKSPACE_ROOT);
151
- await withStoreWriteQueue(storePath, async () => {
158
+ await withStoreWriteCoordinator(storePath, async () => {
152
159
  const store = await openStore(storePath);
153
160
  try {
154
161
  const discovery = new DiscoveryRepository(store);
@@ -171,7 +178,7 @@ async function recordDiscoveryProfile(input) {
171
178
  finally {
172
179
  await store.close();
173
180
  }
174
- });
181
+ }, { operation_label: "recordDiscoveryProfile" });
175
182
  }
176
183
  async function runInit(args, mode = "init") {
177
184
  const projectName = readFlagValue(args, "--project");
@@ -192,7 +199,7 @@ async function runInit(args, mode = "init") {
192
199
  model: llm.llmModel ?? undefined,
193
200
  baseUrl: llm.llmBaseUrl ?? undefined,
194
201
  });
195
- const astIndex = refreshAstgrepIndex({
202
+ const astIndex = await refreshAstgrepIndex({
196
203
  scope: ".",
197
204
  append_evidence: true,
198
205
  emit_event: true,
@@ -248,7 +255,7 @@ async function runInit(args, mode = "init") {
248
255
  message: `ace ${mode} completed: ${storeResult.materialized.length} bootstrap files materialized`,
249
256
  artifacts: [
250
257
  `${ACE_TASKS_ROOT_REL}/`,
251
- `${ACE_ROOT_REL}/ace-state.ace`,
258
+ storeResult.storePath,
252
259
  delta.index_path,
253
260
  astIndex.output_json_path,
254
261
  ],
@@ -335,7 +342,7 @@ async function runDoctor(args) {
335
342
  ok: hasProfile,
336
343
  detail: hasProfile
337
344
  ? profilePath
338
- : `Missing ${ACE_ROOT_REL}/ace-state.ace#state/runtime/llm_profile. Run \`ace init --llm <provider>\` or \`ace doctor --scan\` for a local runtime.`,
345
+ : `Missing agent-state/ace-state.ace#state/runtime/llm_profile. Run \`ace init --llm <provider>\` or \`ace doctor --scan\` for a local runtime.`,
339
346
  });
340
347
  let provider = llm.llmProvider;
341
348
  let model = llm.llmModel;
@@ -378,7 +385,7 @@ async function runDoctor(args) {
378
385
  }
379
386
  }
380
387
  if (!provider) {
381
- throw new Error(`No runtime provider configured. Use --llm <provider>, bootstrap one into ${ACE_ROOT_REL}/ace-state.ace#state/runtime/llm_profile, or run \`ace doctor --scan\` for a local runtime.`);
388
+ throw new Error(`No runtime provider configured. Use --llm <provider>, bootstrap one into agent-state/ace-state.ace#state/runtime/llm_profile, or run \`ace doctor --scan\` for a local runtime.`);
382
389
  }
383
390
  if (!model) {
384
391
  model = defaultModelForProvider(provider);
@@ -515,16 +522,21 @@ function parseClientFlag(args) {
515
522
  return match;
516
523
  }
517
524
  async function runPreconfig() {
518
- const storePath = wsPath(".agents", "ACE", "ace-state.ace");
519
- if (!fileExists(storePath)) {
520
- console.error(`No ACE store found at ${storePath}. Run 'ace init' first.`);
525
+ const resolved = await ensureCanonicalWorkspaceStore(WORKSPACE_ROOT, {
526
+ operationLabel: "runPreconfig",
527
+ });
528
+ if (resolved.mode === "missing") {
529
+ console.error(`No ACE store found. Expected canonical ${resolved.canonicalPath}; legacy fallback ${resolved.legacyPath} was also not found. Run 'ace init' first.`);
521
530
  process.exit(1);
522
531
  }
523
- const store = await openStore(storePath);
532
+ for (const w of resolved.warnings) {
533
+ console.warn(`[preconfig] ${w}`);
534
+ }
535
+ const store = await openStore(resolved.storePath);
524
536
  try {
525
537
  const mat = new HostFileMaterializer(store, WORKSPACE_ROOT);
526
538
  const written = await mat.materializeMcpBundle();
527
- console.log(`ACE preconfig complete — wrote ${written.length} files to .mcp-config/\n`);
539
+ console.log(`ACE preconfig complete — wrote ${written.length} files to .mcp-config/ and root .mcp.json\n`);
528
540
  for (const p of written) {
529
541
  console.log(` ${p}`);
530
542
  }
@@ -644,7 +656,7 @@ async function main() {
644
656
  }
645
657
  if (command === "migrate") {
646
658
  const { importFromFileWorkspace } = await import("./store/importer.js");
647
- const storePath = args[1] ?? `${WORKSPACE_ROOT}/.agents/ACE/ace-state.ace`;
659
+ const storePath = args[1] ?? getWorkspaceStorePath(WORKSPACE_ROOT);
648
660
  console.log(`Migrating file workspace at ${WORKSPACE_ROOT} to store at ${storePath}...`);
649
661
  const result = await importFromFileWorkspace(WORKSPACE_ROOT, storePath);
650
662
  console.log(`Migration complete:`);
@@ -680,6 +692,26 @@ async function main() {
680
692
  console.log(`Store compacted: ${result.before} → ${result.after} bytes (saved ${savedKb}KB)`);
681
693
  return;
682
694
  }
695
+ if (command === "cache") {
696
+ const { cacheWorkspaceArtifacts } = await import("./store/cache-workspace.js");
697
+ const dryRun = args.includes("--dry-run");
698
+ const clean = !args.includes("--no-clean");
699
+ const result = await cacheWorkspaceArtifacts(WORKSPACE_ROOT, { dryRun, clean });
700
+ console.log("ACE cache complete");
701
+ console.log(`Workspace: ${WORKSPACE_ROOT}`);
702
+ console.log(`Store: ${result.storePath}`);
703
+ console.log(`Scanned files: ${result.scanned_files}`);
704
+ console.log(`Cached files: ${result.cached_files}`);
705
+ console.log(`Kept projected files: ${result.kept_projected_files}`);
706
+ console.log(`Skipped files: ${result.skipped_files}`);
707
+ console.log(`Removed files: ${result.removed_files}`);
708
+ if (result.warnings.length > 0) {
709
+ console.warn(`Warnings (${result.warnings.length}):`);
710
+ for (const warning of result.warnings)
711
+ console.warn(` ! ${warning}`);
712
+ }
713
+ return;
714
+ }
683
715
  if (command === "help" || command === "--help" || command === "-h") {
684
716
  printHelp();
685
717
  return;
@@ -4,7 +4,7 @@ import { openStore } from "./store/ace-packed-store.js";
4
4
  import { ProjectionManager } from "./store/materializers/projection-manager.js";
5
5
  import { HandoffRepository } from "./store/repositories/handoff-repository.js";
6
6
  import { getWorkspaceStorePath, listStoreKeysSync, readStoreJsonSync, storeExistsSync, } from "./store/store-snapshot.js";
7
- import { withStoreWriteQueue } from "./store/write-queue.js";
7
+ import { withStoreWriteCoordinator } from "./store/write-coordinator.js";
8
8
  import { waitForPendingStatusEventMirrors } from "./status-events.js";
9
9
  import { waitForTodoStoreMirror } from "./todo-state.js";
10
10
  const HANDOFF_REGISTRY_REL = "agent-state/handoff-registry.json";
@@ -299,7 +299,7 @@ export async function registerHandoffSafe(input) {
299
299
  return withFileLock(HANDOFF_REGISTRY_REL, () => registerHandoff(input));
300
300
  }
301
301
  return withFileLock(HANDOFF_REGISTRY_REL, async () => {
302
- return withStoreWriteQueue(storePath, async () => {
302
+ return withStoreWriteCoordinator(storePath, async () => {
303
303
  const store = await openStore(storePath);
304
304
  try {
305
305
  const repo = new HandoffRepository(store);
@@ -346,7 +346,7 @@ export async function registerHandoffSafe(input) {
346
346
  finally {
347
347
  await store.close();
348
348
  }
349
- });
349
+ }, { operation_label: "registerHandoffSafe" });
350
350
  });
351
351
  }
352
352
  /**
@@ -361,7 +361,7 @@ export async function acknowledgeHandoffSafe(input) {
361
361
  return withFileLock(HANDOFF_REGISTRY_REL, () => acknowledgeHandoff(input));
362
362
  }
363
363
  return withFileLock(HANDOFF_REGISTRY_REL, async () => {
364
- return withStoreWriteQueue(storePath, async () => {
364
+ return withStoreWriteCoordinator(storePath, async () => {
365
365
  const store = await openStore(storePath);
366
366
  try {
367
367
  const repo = new HandoffRepository(store);
@@ -420,7 +420,7 @@ export async function acknowledgeHandoffSafe(input) {
420
420
  finally {
421
421
  await store.close();
422
422
  }
423
- });
423
+ }, { operation_label: "acknowledgeHandoffSafe" });
424
424
  });
425
425
  }
426
426
  //# sourceMappingURL=handoff-registry.js.map
@@ -0,0 +1,19 @@
1
+ import { type AgentRole, type KernelKey, type TaskKey } from "./constants.js";
2
+ export interface SkillReference {
3
+ name: string;
4
+ path: string;
5
+ source: "workspace" | "package-defaults" | "store";
6
+ }
7
+ export declare function getAgentInstructionPath(role: AgentRole): string | undefined;
8
+ export declare function getAgentManifestPath(role: AgentRole): string | undefined;
9
+ export declare function readAgentInstructions(role: AgentRole): string;
10
+ export declare function readAgentManifest(role: AgentRole): string;
11
+ export declare function getTaskArtifactPath(key: TaskKey): string | undefined;
12
+ export declare function readTaskArtifact(key: TaskKey): string;
13
+ export declare function getKernelArtifactPath(key: KernelKey): string | undefined;
14
+ export declare function readKernelArtifact(key: KernelKey): string;
15
+ export declare function resolveWritableTaskPath(key: TaskKey): string;
16
+ export declare function listAvailableSkills(): SkillReference[];
17
+ export declare function getSkillPath(name: string): string | undefined;
18
+ export declare function readSkillInstructions(name: string): string;
19
+ //# sourceMappingURL=artifacts.d.ts.map