@voybio/ace-swarm 0.2.5 → 2.4.1
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/CHANGELOG.md +19 -1
- package/README.md +21 -13
- package/assets/.agents/ACE/agent-qa/instructions.md +11 -0
- package/assets/agent-state/EVIDENCE_LOG.md +1 -1
- package/assets/agent-state/MODULES/roles/capability-framework.json +41 -0
- package/assets/agent-state/MODULES/roles/capability-git.json +33 -0
- package/assets/agent-state/MODULES/roles/capability-safety.json +37 -0
- package/assets/agent-state/MODULES/schemas/ACE_RUNTIME_PROFILE.schema.json +21 -0
- package/assets/agent-state/MODULES/schemas/RUNTIME_EXECUTOR_SESSION_REGISTRY.schema.json +43 -0
- package/assets/agent-state/MODULES/schemas/RUNTIME_TOOL_SPEC_REGISTRY.schema.json +43 -0
- package/assets/agent-state/MODULES/schemas/WORKSPACE_SESSION_REGISTRY.schema.json +11 -0
- package/assets/agent-state/STATUS.md +2 -2
- package/assets/agent-state/runtime-tool-specs.json +70 -2
- package/assets/instructions/ACE_Coder.instructions.md +13 -0
- package/assets/instructions/ACE_UI.instructions.md +11 -0
- package/assets/scripts/ace-hook-dispatch.mjs +70 -6
- package/assets/scripts/render-mcp-configs.sh +19 -5
- package/dist/ace-context.js +91 -11
- package/dist/ace-internal-tools.d.ts +3 -1
- package/dist/ace-internal-tools.js +10 -2
- package/dist/ace-server-instructions.js +3 -3
- package/dist/ace-state-resolver.js +5 -3
- package/dist/agent-runtime/role-adapters.d.ts +18 -1
- package/dist/agent-runtime/role-adapters.js +49 -5
- package/dist/astgrep-index.d.ts +57 -1
- package/dist/astgrep-index.js +140 -4
- package/dist/cli.js +232 -35
- package/dist/discovery-runtime-wrappers.d.ts +108 -0
- package/dist/discovery-runtime-wrappers.js +615 -0
- package/dist/handoff-registry.js +5 -5
- package/dist/helpers/artifacts.d.ts +19 -0
- package/dist/helpers/artifacts.js +152 -0
- package/dist/helpers/bootstrap.d.ts +24 -0
- package/dist/helpers/bootstrap.js +894 -0
- package/dist/helpers/constants.d.ts +53 -0
- package/dist/helpers/constants.js +295 -0
- package/dist/helpers/drift.d.ts +13 -0
- package/dist/helpers/drift.js +45 -0
- package/dist/helpers/path-utils.d.ts +24 -0
- package/dist/helpers/path-utils.js +123 -0
- package/dist/helpers/store-resolution.d.ts +19 -0
- package/dist/helpers/store-resolution.js +305 -0
- package/dist/helpers/workspace-root.d.ts +3 -0
- package/dist/helpers/workspace-root.js +80 -0
- package/dist/helpers.d.ts +8 -125
- package/dist/helpers.js +8 -1768
- package/dist/job-scheduler.js +33 -7
- package/dist/json-sanitizer.d.ts +16 -0
- package/dist/json-sanitizer.js +26 -0
- package/dist/local-model-policy.d.ts +27 -0
- package/dist/local-model-policy.js +84 -0
- package/dist/local-model-runtime.d.ts +6 -0
- package/dist/local-model-runtime.js +33 -21
- package/dist/model-bridge.d.ts +13 -1
- package/dist/model-bridge.js +410 -23
- package/dist/orchestrator-supervisor.d.ts +56 -0
- package/dist/orchestrator-supervisor.js +179 -1
- package/dist/plan-proposal.d.ts +115 -0
- package/dist/plan-proposal.js +1073 -0
- package/dist/run-ledger.js +3 -3
- package/dist/runtime-command.d.ts +8 -0
- package/dist/runtime-command.js +38 -6
- package/dist/runtime-executor.d.ts +20 -1
- package/dist/runtime-executor.js +737 -172
- package/dist/runtime-profile.d.ts +32 -0
- package/dist/runtime-profile.js +89 -13
- package/dist/runtime-tool-specs.d.ts +39 -0
- package/dist/runtime-tool-specs.js +144 -28
- package/dist/safe-edit.d.ts +7 -0
- package/dist/safe-edit.js +163 -37
- package/dist/schemas.js +48 -1
- package/dist/server.js +51 -0
- package/dist/shared.d.ts +3 -2
- package/dist/shared.js +2 -0
- package/dist/status-events.js +9 -6
- package/dist/store/ace-packed-store.d.ts +3 -2
- package/dist/store/ace-packed-store.js +188 -110
- package/dist/store/bootstrap-store.d.ts +2 -1
- package/dist/store/bootstrap-store.js +102 -83
- package/dist/store/cache-workspace.js +11 -5
- package/dist/store/materializers/context-snapshot-materializer.js +6 -2
- package/dist/store/materializers/hook-context-materializer.d.ts +6 -9
- package/dist/store/materializers/hook-context-materializer.js +11 -21
- package/dist/store/materializers/host-file-materializer.js +6 -0
- package/dist/store/materializers/projection-manager.d.ts +0 -1
- package/dist/store/materializers/projection-manager.js +5 -13
- package/dist/store/materializers/scheduler-projection-materializer.js +1 -1
- package/dist/store/materializers/vericify-projector.d.ts +7 -7
- package/dist/store/materializers/vericify-projector.js +11 -11
- package/dist/store/repositories/local-model-runtime-repository.d.ts +120 -3
- package/dist/store/repositories/local-model-runtime-repository.js +242 -6
- package/dist/store/repositories/vericify-repository.d.ts +1 -1
- package/dist/store/skills-install.d.ts +4 -0
- package/dist/store/skills-install.js +21 -12
- package/dist/store/state-reader.d.ts +2 -0
- package/dist/store/state-reader.js +20 -0
- package/dist/store/store-artifacts.d.ts +7 -0
- package/dist/store/store-artifacts.js +27 -1
- package/dist/store/store-authority-audit.d.ts +18 -1
- package/dist/store/store-authority-audit.js +115 -5
- package/dist/store/store-snapshot.d.ts +3 -0
- package/dist/store/store-snapshot.js +22 -2
- package/dist/store/workspace-store-paths.d.ts +39 -0
- package/dist/store/workspace-store-paths.js +94 -0
- package/dist/store/write-coordinator.d.ts +65 -0
- package/dist/store/write-coordinator.js +386 -0
- package/dist/todo-state.js +5 -5
- package/dist/tools-agent.d.ts +20 -0
- package/dist/tools-agent.js +789 -25
- package/dist/tools-discovery.js +136 -1
- package/dist/tools-files.d.ts +7 -0
- package/dist/tools-files.js +1002 -11
- package/dist/tools-framework.js +105 -66
- package/dist/tools-handoff.js +2 -2
- package/dist/tools-lifecycle.js +4 -4
- package/dist/tools-memory.js +6 -6
- package/dist/tools-todo.js +2 -2
- package/dist/tracker-adapters.d.ts +1 -1
- package/dist/tracker-adapters.js +13 -18
- package/dist/tracker-sync.js +5 -3
- package/dist/tui/agent-runner.js +3 -1
- package/dist/tui/chat.js +103 -7
- package/dist/tui/dashboard.d.ts +1 -0
- package/dist/tui/dashboard.js +43 -0
- package/dist/tui/index.js +10 -1
- package/dist/tui/layout.d.ts +20 -0
- package/dist/tui/layout.js +31 -1
- package/dist/tui/local-model-contract.d.ts +6 -2
- package/dist/tui/local-model-contract.js +16 -3
- package/dist/tui/ollama.d.ts +8 -1
- package/dist/tui/ollama.js +53 -12
- package/dist/tui/openai-compatible.d.ts +13 -0
- package/dist/tui/openai-compatible.js +305 -5
- package/dist/tui/provider-discovery.d.ts +1 -0
- package/dist/tui/provider-discovery.js +35 -11
- package/dist/vericify-bridge.d.ts +6 -1
- package/dist/vericify-bridge.js +27 -3
- package/dist/workspace-manager.d.ts +30 -3
- package/dist/workspace-manager.js +257 -27
- package/package.json +1 -2
- package/dist/internal-tool-runtime.d.ts +0 -21
- package/dist/internal-tool-runtime.js +0 -136
- package/dist/store/workspace-snapshot.d.ts +0 -26
- package/dist/store/workspace-snapshot.js +0 -107
package/dist/astgrep-index.js
CHANGED
|
@@ -1,8 +1,144 @@
|
|
|
1
1
|
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
2
3
|
import { isAbsolute, relative, resolve } from "node:path";
|
|
3
4
|
import { spawnSync } from "node:child_process";
|
|
4
|
-
import {
|
|
5
|
+
import { appendStatusEventSafe } from "./status-events.js";
|
|
5
6
|
import { safeRead, safeWrite, WORKSPACE_ROOT, wsPath } from "./helpers.js";
|
|
7
|
+
export function runAstgrepQuery(pattern, lang, roots, _contextLines) {
|
|
8
|
+
const cmd = detectAstgrepCommand();
|
|
9
|
+
const raw = runAstgrep(cmd, pattern, lang, roots);
|
|
10
|
+
return raw.slice(0, 200).map((m) => ({
|
|
11
|
+
file: m.file ?? "",
|
|
12
|
+
line: m.range?.start?.line ?? 0,
|
|
13
|
+
column: m.range?.start?.column ?? 0,
|
|
14
|
+
text: m.text ?? "",
|
|
15
|
+
context_lines: [],
|
|
16
|
+
}));
|
|
17
|
+
}
|
|
18
|
+
function hashTextContent(content) {
|
|
19
|
+
return `sha256:${createHash("sha256").update(content, "utf8").digest("hex")}`;
|
|
20
|
+
}
|
|
21
|
+
function textPreview(text, maxChars = 160) {
|
|
22
|
+
const normalized = text.replace(/\s+/g, " ").trim();
|
|
23
|
+
if (normalized.length <= maxChars)
|
|
24
|
+
return normalized;
|
|
25
|
+
return `${normalized.slice(0, Math.max(0, maxChars - 1)).trimEnd()}…`;
|
|
26
|
+
}
|
|
27
|
+
function nodeKindForMatch(match) {
|
|
28
|
+
if (typeof match.nodeKind === "string" && match.nodeKind.length > 0)
|
|
29
|
+
return match.nodeKind;
|
|
30
|
+
if (typeof match.kind === "string" && match.kind.length > 0)
|
|
31
|
+
return match.kind;
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
34
|
+
function captureMapForMatch(match) {
|
|
35
|
+
if (!match.metaVariables)
|
|
36
|
+
return undefined;
|
|
37
|
+
const captures = Object.entries(match.metaVariables).reduce((acc, [key]) => {
|
|
38
|
+
const text = extractMetaVarText(match, key);
|
|
39
|
+
if (typeof text === "string" && text.length > 0)
|
|
40
|
+
acc[key] = text;
|
|
41
|
+
return acc;
|
|
42
|
+
}, {});
|
|
43
|
+
return Object.keys(captures).length > 0 ? captures : undefined;
|
|
44
|
+
}
|
|
45
|
+
function computeMatchId(input) {
|
|
46
|
+
const encoded = [
|
|
47
|
+
input.file,
|
|
48
|
+
input.range.start?.line ?? 0,
|
|
49
|
+
input.range.start?.column ?? 0,
|
|
50
|
+
input.range.end?.line ?? 0,
|
|
51
|
+
input.range.end?.column ?? 0,
|
|
52
|
+
input.file_hash,
|
|
53
|
+
input.pattern,
|
|
54
|
+
input.lang,
|
|
55
|
+
input.matched_text,
|
|
56
|
+
].join("|");
|
|
57
|
+
return `match_${createHash("sha256").update(encoded, "utf8").digest("hex").slice(0, 16)}`;
|
|
58
|
+
}
|
|
59
|
+
function matchUsesSymbolHint(match, symbolHint) {
|
|
60
|
+
if ((match.text ?? "").includes(symbolHint))
|
|
61
|
+
return true;
|
|
62
|
+
if (!match.metaVariables)
|
|
63
|
+
return false;
|
|
64
|
+
return Object.keys(match.metaVariables).some((key) => {
|
|
65
|
+
const text = extractMetaVarText(match, key);
|
|
66
|
+
return typeof text === "string" && text.includes(symbolHint);
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
export function locateAstgrepMatches(input) {
|
|
70
|
+
const scope = resolveScope(input.scope ?? "src");
|
|
71
|
+
const astgrepCmd = detectAstgrepCommand();
|
|
72
|
+
if (!astgrepCmd) {
|
|
73
|
+
return {
|
|
74
|
+
ok: false,
|
|
75
|
+
pattern: input.pattern,
|
|
76
|
+
lang: input.lang,
|
|
77
|
+
scope: scope.rel,
|
|
78
|
+
symbol_hint: input.symbol_hint,
|
|
79
|
+
astgrep_command: null,
|
|
80
|
+
total_matches: 0,
|
|
81
|
+
matches: [],
|
|
82
|
+
error: "ast-grep command not available",
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
const rawMatches = runAstgrep(astgrepCmd, input.pattern, input.lang, [scope.abs]);
|
|
86
|
+
const effectiveMatches = input.symbol_hint && input.symbol_hint.trim().length > 0
|
|
87
|
+
? (() => {
|
|
88
|
+
const hinted = rawMatches.filter((match) => matchUsesSymbolHint(match, input.symbol_hint));
|
|
89
|
+
return hinted.length > 0 ? hinted : rawMatches;
|
|
90
|
+
})()
|
|
91
|
+
: rawMatches;
|
|
92
|
+
const limit = input.max_results ?? 50;
|
|
93
|
+
const matches = effectiveMatches.slice(0, limit).flatMap((match) => {
|
|
94
|
+
const absFile = typeof match.file === "string" && match.file.length > 0
|
|
95
|
+
? (isAbsolute(match.file) ? match.file : resolve(WORKSPACE_ROOT, match.file))
|
|
96
|
+
: undefined;
|
|
97
|
+
if (!absFile || !isInside(WORKSPACE_ROOT, absFile) || !match.range)
|
|
98
|
+
return [];
|
|
99
|
+
const matchedText = match.text ?? "";
|
|
100
|
+
const fileContent = safeReadText(absFile);
|
|
101
|
+
const fileHash = hashTextContent(fileContent);
|
|
102
|
+
const file = normalizeHitPath(relative(WORKSPACE_ROOT, absFile));
|
|
103
|
+
const range = {
|
|
104
|
+
start: {
|
|
105
|
+
line: match.range.start?.line ?? 0,
|
|
106
|
+
column: match.range.start?.column ?? 0,
|
|
107
|
+
},
|
|
108
|
+
end: {
|
|
109
|
+
line: match.range.end?.line ?? 0,
|
|
110
|
+
column: match.range.end?.column ?? 0,
|
|
111
|
+
},
|
|
112
|
+
};
|
|
113
|
+
return [{
|
|
114
|
+
match_id: computeMatchId({
|
|
115
|
+
file,
|
|
116
|
+
range,
|
|
117
|
+
matched_text: matchedText,
|
|
118
|
+
file_hash: fileHash,
|
|
119
|
+
pattern: input.pattern,
|
|
120
|
+
lang: input.lang,
|
|
121
|
+
}),
|
|
122
|
+
file,
|
|
123
|
+
range,
|
|
124
|
+
text_preview: textPreview(matchedText),
|
|
125
|
+
matched_text: matchedText,
|
|
126
|
+
captures: captureMapForMatch(match),
|
|
127
|
+
node_kind: nodeKindForMatch(match),
|
|
128
|
+
file_hash: fileHash,
|
|
129
|
+
}];
|
|
130
|
+
});
|
|
131
|
+
return {
|
|
132
|
+
ok: true,
|
|
133
|
+
pattern: input.pattern,
|
|
134
|
+
lang: input.lang,
|
|
135
|
+
scope: scope.rel,
|
|
136
|
+
symbol_hint: input.symbol_hint,
|
|
137
|
+
astgrep_command: astgrepCmd,
|
|
138
|
+
total_matches: effectiveMatches.length,
|
|
139
|
+
matches,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
6
142
|
const CODE_EXTENSIONS = new Set(["ts", "tsx", "js", "mjs", "cjs", "py", "rs", "go"]);
|
|
7
143
|
const EXCLUDE_DIRS = new Set([
|
|
8
144
|
".git",
|
|
@@ -28,7 +164,7 @@ function resolveScope(scope) {
|
|
|
28
164
|
}
|
|
29
165
|
return { abs, rel: rel || "." };
|
|
30
166
|
}
|
|
31
|
-
function detectAstgrepCommand() {
|
|
167
|
+
export function detectAstgrepCommand() {
|
|
32
168
|
for (const cmd of ["ast-grep", "sg"]) {
|
|
33
169
|
const probe = spawnSync(cmd, ["--version"], { encoding: "utf8" });
|
|
34
170
|
if (probe.status === 0)
|
|
@@ -180,7 +316,7 @@ function appendEvidenceLine(timestamp, astgrepCmd, stats) {
|
|
|
180
316
|
].join("\n");
|
|
181
317
|
safeWrite("agent-state/EVIDENCE_LOG.md", `${seed}${entry}\n`);
|
|
182
318
|
}
|
|
183
|
-
export function refreshAstgrepIndex(input = {}) {
|
|
319
|
+
export async function refreshAstgrepIndex(input = {}) {
|
|
184
320
|
const generatedAt = new Date().toISOString();
|
|
185
321
|
const scope = resolveScope(input.scope);
|
|
186
322
|
const appendEvidence = input.append_evidence ?? true;
|
|
@@ -440,7 +576,7 @@ export function refreshAstgrepIndex(input = {}) {
|
|
|
440
576
|
if (emitEvent) {
|
|
441
577
|
const status = astgrepCmd ? "pass" : "blocked";
|
|
442
578
|
const eventType = astgrepCmd ? "DISCOVERY_INDEX_READY" : "DISCOVERY_SCOPE_BLOCKED";
|
|
443
|
-
const event =
|
|
579
|
+
const event = await appendStatusEventSafe({
|
|
444
580
|
source_module: "capability-astgrep",
|
|
445
581
|
event_type: eventType,
|
|
446
582
|
status,
|
package/dist/cli.js
CHANGED
|
@@ -1,20 +1,22 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
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";
|
|
6
6
|
import { appendRunLedgerEntrySafe } from "./run-ledger.js";
|
|
7
|
-
import { waitForPendingStatusEventMirrors } from "./status-events.js";
|
|
7
|
+
import { appendStatusEventSafe, 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
|
-
import { buildOpenAiCompatibleBaseUrl, buildProviderDoctorCommands, defaultModelForProvider, discoverProviderContext, isLocalLlmProvider, normalizeLocalBaseUrl, normalizeProvider, scanLocalModelRuntimes, } from "./tui/provider-discovery.js";
|
|
17
|
+
import { buildOpenAiCompatibleBaseUrl, buildProviderDoctorCommands, defaultModelForProvider, discoverProviderContext, isLocalLlmProvider, normalizeLocalBaseUrl, normalizeLlamaCppHfModelName, normalizeProvider, scanLocalModelRuntimes, } from "./tui/provider-discovery.js";
|
|
17
18
|
import { diagnoseChatRuntimeConfig, OpenAICompatibleClient, } from "./tui/openai-compatible.js";
|
|
19
|
+
import { runShellCommand } from "./runtime-command.js";
|
|
18
20
|
function printHelp() {
|
|
19
21
|
console.log(`ACE Swarm CLI
|
|
20
22
|
|
|
@@ -23,6 +25,7 @@ Usage:
|
|
|
23
25
|
ace serve Alias for mcp
|
|
24
26
|
ace tui [options] Launch interactive TUI dashboard
|
|
25
27
|
ace init [options] Bootstrap the ACE store into current workspace
|
|
28
|
+
ace connect [provider] Register a live local runtime or hosted provider in the store
|
|
26
29
|
ace turnkey [options] Project minimal workspace bootstrap stubs from the ACE store
|
|
27
30
|
ace doctor [options] Validate ACE runtime + MCP readiness
|
|
28
31
|
ace cache [options] Cache ACE artifacts into ace-state.ace and optionally clean projections
|
|
@@ -38,7 +41,7 @@ Options for tui:
|
|
|
38
41
|
--ollama-url <url> Legacy alias for --base-url
|
|
39
42
|
|
|
40
43
|
Options for init:
|
|
41
|
-
--project <name> Project name stored in
|
|
44
|
+
--project <name> Project name stored in agent-state/ace-state.ace metadata
|
|
42
45
|
--force Overwrite scaffolded files if they already exist
|
|
43
46
|
--mcp-config Also write .vscode/mcp.json workspace bridge
|
|
44
47
|
--client-config-bundle Also write minimal workspace host stubs (AGENTS.md, CLAUDE.md, .cursorrules, .github/copilot-instructions.md)
|
|
@@ -47,23 +50,32 @@ Options for init:
|
|
|
47
50
|
--base-url <url> Runtime base URL override (local or OpenAI-compatible)
|
|
48
51
|
--ollama-url <url> Legacy alias for --base-url
|
|
49
52
|
|
|
53
|
+
Options for connect:
|
|
54
|
+
llama-server|llama-cli Optional launcher name for llama.cpp HF runtimes
|
|
55
|
+
--hf <repo> Hugging Face model repo or local model identifier
|
|
56
|
+
--model <name> Alias for --hf when you already know the model name
|
|
57
|
+
--base-url <url> OpenAI-compatible endpoint for the live runtime
|
|
58
|
+
--provider <name> Explicit provider override when not using a launcher alias
|
|
59
|
+
|
|
50
60
|
Options for doctor:
|
|
51
|
-
--llm <provider> ollama|llama.cpp|codex|claude|gemini|copilot|... (default: auto from
|
|
61
|
+
--llm <provider> ollama|llama.cpp|codex|claude|gemini|copilot|... (default: auto from agent-state/ace-state.ace)
|
|
52
62
|
--model <name> Model name override
|
|
53
63
|
--base-url <url> Runtime base URL override
|
|
54
64
|
--ollama-url <url> Legacy alias for --base-url
|
|
55
65
|
--scan Probe common local Ollama + llama.cpp endpoints when URL is unset
|
|
66
|
+
--repair-ollama Opt-in: run ollama pull <model> when doctor finds a missing Ollama model
|
|
56
67
|
|
|
57
68
|
Options for cache:
|
|
58
69
|
--dry-run Preview what would be cached and cleaned (no writes/deletes)
|
|
59
70
|
--no-clean Keep workspace ACE artifacts after caching them into ace-state.ace
|
|
60
71
|
|
|
61
72
|
Options for mcp-config:
|
|
62
|
-
--client <name> codex|vscode|claude|cursor|antigravity
|
|
73
|
+
--client <name> codex|vscode|copilot|claude|cursor|antigravity
|
|
63
74
|
--all Print all client snippets for optional global install
|
|
64
75
|
|
|
65
76
|
preconfig writes .mcp-config/ at the workspace root with ready-to-use config files for every
|
|
66
|
-
supported MCP client
|
|
77
|
+
supported MCP client plus a root .mcp.json for GitHub Copilot CLI. Run once after ace init.
|
|
78
|
+
Each file includes the install hint for its client.
|
|
67
79
|
`);
|
|
68
80
|
}
|
|
69
81
|
function readFlagValue(args, flag) {
|
|
@@ -96,6 +108,29 @@ function readLlmProfile() {
|
|
|
96
108
|
function readBaseUrlFlag(args) {
|
|
97
109
|
return normalizeLocalBaseUrl(readFlagValue(args, "--base-url") ?? readFlagValue(args, "--ollama-url"));
|
|
98
110
|
}
|
|
111
|
+
function readConnectModelFlag(args) {
|
|
112
|
+
return (readFlagValue(args, "--hf") ??
|
|
113
|
+
readFlagValue(args, "-hf") ??
|
|
114
|
+
readFlagValue(args, "--model"))?.trim();
|
|
115
|
+
}
|
|
116
|
+
function parseConnectArgs(args) {
|
|
117
|
+
const firstToken = args[0];
|
|
118
|
+
const hasLauncher = Boolean(firstToken && !firstToken.startsWith("-"));
|
|
119
|
+
const launcher = hasLauncher ? firstToken?.trim().toLowerCase() : undefined;
|
|
120
|
+
const providerOverride = normalizeProvider(readFlagValue(args, "--provider")?.trim());
|
|
121
|
+
const provider = providerOverride ??
|
|
122
|
+
(launcher === "llama-server" || launcher === "llama-cli" ? "llama.cpp" : providerOverride);
|
|
123
|
+
const rawModel = readConnectModelFlag(args);
|
|
124
|
+
const model = provider === "llama.cpp" ? normalizeLlamaCppHfModelName(rawModel) ?? rawModel : rawModel;
|
|
125
|
+
const baseUrl = readBaseUrlFlag(args);
|
|
126
|
+
const launcherCommand = hasLauncher ? args.join(" ").trim() : undefined;
|
|
127
|
+
return {
|
|
128
|
+
provider: provider,
|
|
129
|
+
model,
|
|
130
|
+
baseUrl,
|
|
131
|
+
launcherCommand,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
99
134
|
function parseLlmOptions(args) {
|
|
100
135
|
const profile = readLlmProfile();
|
|
101
136
|
const selected = normalizeProvider(readFlagValue(args, "--llm")?.trim() ?? profile?.provider?.trim());
|
|
@@ -105,8 +140,11 @@ function parseLlmOptions(args) {
|
|
|
105
140
|
throw new Error(`Unsupported LLM provider: ${selected}`);
|
|
106
141
|
}
|
|
107
142
|
const provider = selected;
|
|
108
|
-
const
|
|
109
|
-
const
|
|
143
|
+
const explicitModel = readFlagValue(args, "--model")?.trim();
|
|
144
|
+
const profileModel = profile?.model?.trim();
|
|
145
|
+
const llmModel = explicitModel ||
|
|
146
|
+
profileModel ||
|
|
147
|
+
(provider === "ollama" ? defaultModelForProvider(provider) : undefined);
|
|
110
148
|
const llmBaseUrl = readBaseUrlFlag(args) ?? normalizeLocalBaseUrl(profile?.base_url);
|
|
111
149
|
return {
|
|
112
150
|
llmProvider: provider,
|
|
@@ -116,12 +154,14 @@ function parseLlmOptions(args) {
|
|
|
116
154
|
}
|
|
117
155
|
async function writeLlmProfile(profile) {
|
|
118
156
|
const storePath = getWorkspaceStorePath(WORKSPACE_ROOT);
|
|
119
|
-
await
|
|
157
|
+
await withStoreWriteCoordinator(storePath, async () => {
|
|
120
158
|
const payload = {
|
|
121
159
|
provider: profile.provider,
|
|
122
|
-
model: profile.model,
|
|
123
160
|
generated_at: new Date().toISOString(),
|
|
124
161
|
};
|
|
162
|
+
if (profile.model) {
|
|
163
|
+
payload.model = profile.model;
|
|
164
|
+
}
|
|
125
165
|
if (profile.baseUrl) {
|
|
126
166
|
payload.base_url = profile.baseUrl;
|
|
127
167
|
if (profile.provider === "ollama") {
|
|
@@ -129,6 +169,9 @@ async function writeLlmProfile(profile) {
|
|
|
129
169
|
payload.default_api_key = "ollama";
|
|
130
170
|
}
|
|
131
171
|
}
|
|
172
|
+
if (profile.launcherCommand) {
|
|
173
|
+
payload.launch_command = profile.launcherCommand;
|
|
174
|
+
}
|
|
132
175
|
const doctorCommands = buildProviderDoctorCommands(profile.provider, profile.model, profile.baseUrl);
|
|
133
176
|
const store = await openStore(storePath);
|
|
134
177
|
try {
|
|
@@ -148,12 +191,46 @@ async function writeLlmProfile(profile) {
|
|
|
148
191
|
finally {
|
|
149
192
|
await store.close();
|
|
150
193
|
}
|
|
151
|
-
});
|
|
194
|
+
}, { operation_label: "writeLlmProfile" });
|
|
152
195
|
return `${storePath}#state/runtime/llm_profile`;
|
|
153
196
|
}
|
|
197
|
+
async function runConnect(args) {
|
|
198
|
+
const parsed = parseConnectArgs(args);
|
|
199
|
+
if (!parsed.provider) {
|
|
200
|
+
throw new Error("ace connect expects a provider or launcher command. Try `ace connect llama-server -hf <model> --base-url http://127.0.0.1:8080`.");
|
|
201
|
+
}
|
|
202
|
+
if (!ALL_LLM_PROVIDERS.includes(parsed.provider)) {
|
|
203
|
+
throw new Error(`Unsupported LLM provider: ${parsed.provider}`);
|
|
204
|
+
}
|
|
205
|
+
if (isLocalLlmProvider(parsed.provider) && !parsed.model) {
|
|
206
|
+
throw new Error("ace connect for local runtimes requires an explicit model. Pass `--hf <repo>` or `--model <name>`.");
|
|
207
|
+
}
|
|
208
|
+
const storeResult = await bootstrapStoreWorkspace({
|
|
209
|
+
workspaceRoot: WORKSPACE_ROOT,
|
|
210
|
+
llm: parsed.provider,
|
|
211
|
+
model: parsed.model,
|
|
212
|
+
baseUrl: parsed.baseUrl,
|
|
213
|
+
includeMcpConfig: false,
|
|
214
|
+
includeClientConfigBundle: false,
|
|
215
|
+
launcherCommand: parsed.launcherCommand,
|
|
216
|
+
});
|
|
217
|
+
console.log("ACE connect complete");
|
|
218
|
+
console.log(`Workspace: ${WORKSPACE_ROOT}`);
|
|
219
|
+
console.log(`Store: ${storeResult.storePath}`);
|
|
220
|
+
console.log(`Provider: ${parsed.provider}`);
|
|
221
|
+
console.log(`Model: ${parsed.model ?? "(not set)"}`);
|
|
222
|
+
console.log(`Base URL: ${parsed.baseUrl ?? "(not set)"}`);
|
|
223
|
+
if (parsed.launcherCommand) {
|
|
224
|
+
console.log(`Launcher: ${parsed.launcherCommand}`);
|
|
225
|
+
}
|
|
226
|
+
console.log(`Profile path: ${storeResult.storePath}#state/runtime/llm_profile`);
|
|
227
|
+
for (const warning of storeResult.warnings) {
|
|
228
|
+
console.warn(` [store] ${warning}`);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
154
231
|
async function recordDiscoveryProfile(input) {
|
|
155
232
|
const storePath = getWorkspaceStorePath(WORKSPACE_ROOT);
|
|
156
|
-
await
|
|
233
|
+
await withStoreWriteCoordinator(storePath, async () => {
|
|
157
234
|
const store = await openStore(storePath);
|
|
158
235
|
try {
|
|
159
236
|
const discovery = new DiscoveryRepository(store);
|
|
@@ -176,7 +253,7 @@ async function recordDiscoveryProfile(input) {
|
|
|
176
253
|
finally {
|
|
177
254
|
await store.close();
|
|
178
255
|
}
|
|
179
|
-
});
|
|
256
|
+
}, { operation_label: "recordDiscoveryProfile" });
|
|
180
257
|
}
|
|
181
258
|
async function runInit(args, mode = "init") {
|
|
182
259
|
const projectName = readFlagValue(args, "--project");
|
|
@@ -197,7 +274,7 @@ async function runInit(args, mode = "init") {
|
|
|
197
274
|
model: llm.llmModel ?? undefined,
|
|
198
275
|
baseUrl: llm.llmBaseUrl ?? undefined,
|
|
199
276
|
});
|
|
200
|
-
const astIndex = refreshAstgrepIndex({
|
|
277
|
+
const astIndex = await refreshAstgrepIndex({
|
|
201
278
|
scope: ".",
|
|
202
279
|
append_evidence: true,
|
|
203
280
|
emit_event: true,
|
|
@@ -253,7 +330,7 @@ async function runInit(args, mode = "init") {
|
|
|
253
330
|
message: `ace ${mode} completed: ${storeResult.materialized.length} bootstrap files materialized`,
|
|
254
331
|
artifacts: [
|
|
255
332
|
`${ACE_TASKS_ROOT_REL}/`,
|
|
256
|
-
|
|
333
|
+
storeResult.storePath,
|
|
257
334
|
delta.index_path,
|
|
258
335
|
astIndex.output_json_path,
|
|
259
336
|
],
|
|
@@ -294,6 +371,13 @@ async function listLocalRuntimeModels(provider, baseUrl) {
|
|
|
294
371
|
headers: { Accept: "application/json" },
|
|
295
372
|
});
|
|
296
373
|
if (!response.ok) {
|
|
374
|
+
const text = await response.text().catch(() => "");
|
|
375
|
+
if (response.status >= 500 && /unable to load model/i.test(text)) {
|
|
376
|
+
const error = new Error(`${response.status} ${response.statusText}: ollama_model_load_error. ` +
|
|
377
|
+
`Suggested remediation: run ollama pull <model> or ace doctor --repair-ollama.`);
|
|
378
|
+
error.reason_code = "ollama_model_load_error";
|
|
379
|
+
throw error;
|
|
380
|
+
}
|
|
297
381
|
throw new Error(`${response.status} ${response.statusText}`);
|
|
298
382
|
}
|
|
299
383
|
const payload = (await response.json().catch(() => ({})));
|
|
@@ -319,6 +403,7 @@ async function listLocalRuntimeModels(provider, baseUrl) {
|
|
|
319
403
|
}
|
|
320
404
|
async function runDoctor(args) {
|
|
321
405
|
const llm = parseLlmOptions(args);
|
|
406
|
+
const repairOllama = args.includes("--repair-ollama");
|
|
322
407
|
const checks = [];
|
|
323
408
|
const mcpConfigPaths = [wsPath(".vscode", "mcp.json")];
|
|
324
409
|
const hasWorkspaceMcpConfig = mcpConfigPaths.some((path) => fileExists(path));
|
|
@@ -340,12 +425,14 @@ async function runDoctor(args) {
|
|
|
340
425
|
ok: hasProfile,
|
|
341
426
|
detail: hasProfile
|
|
342
427
|
? profilePath
|
|
343
|
-
: `Missing
|
|
428
|
+
: `Missing agent-state/ace-state.ace#state/runtime/llm_profile. Run \`ace init --llm <provider>\` or \`ace doctor --scan\` for a local runtime.`,
|
|
344
429
|
});
|
|
345
430
|
let provider = llm.llmProvider;
|
|
346
431
|
let model = llm.llmModel;
|
|
347
432
|
let baseUrl = llm.llmBaseUrl;
|
|
348
|
-
const shouldScan = args.includes("--scan") ||
|
|
433
|
+
const shouldScan = args.includes("--scan") ||
|
|
434
|
+
(!provider && !baseUrl) ||
|
|
435
|
+
(isLocalLlmProvider(provider) && (!baseUrl || !model));
|
|
349
436
|
if (shouldScan) {
|
|
350
437
|
const scanned = await scanLocalModelRuntimes({
|
|
351
438
|
workspaceRoot: WORKSPACE_ROOT,
|
|
@@ -357,7 +444,7 @@ async function runDoctor(args) {
|
|
|
357
444
|
if (chosen) {
|
|
358
445
|
provider = chosen.provider ?? provider;
|
|
359
446
|
baseUrl = baseUrl ?? chosen.baseUrl;
|
|
360
|
-
model = model || chosen.models[0]
|
|
447
|
+
model = model || chosen.models[0];
|
|
361
448
|
checks.push({
|
|
362
449
|
name: "Runtime endpoint discovered",
|
|
363
450
|
ok: true,
|
|
@@ -365,7 +452,7 @@ async function runDoctor(args) {
|
|
|
365
452
|
});
|
|
366
453
|
const writtenProfilePath = await writeLlmProfile({
|
|
367
454
|
provider: provider ?? chosen.provider,
|
|
368
|
-
model
|
|
455
|
+
model,
|
|
369
456
|
baseUrl,
|
|
370
457
|
});
|
|
371
458
|
checks.push({
|
|
@@ -383,9 +470,33 @@ async function runDoctor(args) {
|
|
|
383
470
|
}
|
|
384
471
|
}
|
|
385
472
|
if (!provider) {
|
|
386
|
-
throw new Error(`No runtime provider configured. Use --llm <provider>, bootstrap one into
|
|
473
|
+
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.`);
|
|
387
474
|
}
|
|
388
475
|
if (!model) {
|
|
476
|
+
if (isLocalLlmProvider(provider)) {
|
|
477
|
+
checks.push({
|
|
478
|
+
name: "Runtime model configured",
|
|
479
|
+
ok: false,
|
|
480
|
+
detail: "No local runtime model is configured yet. Run `ace connect` with a user-supplied HF model or run `ace doctor --scan` after the runtime is up.",
|
|
481
|
+
});
|
|
482
|
+
const failed = checks.filter((check) => !check.ok);
|
|
483
|
+
console.log("ACE Doctor Report");
|
|
484
|
+
console.log(`Workspace: ${WORKSPACE_ROOT}`);
|
|
485
|
+
console.log(`Provider: ${provider}`);
|
|
486
|
+
console.log("Model: (not set)");
|
|
487
|
+
if (baseUrl) {
|
|
488
|
+
console.log(`Base URL: ${baseUrl}`);
|
|
489
|
+
}
|
|
490
|
+
console.log("");
|
|
491
|
+
for (const check of checks) {
|
|
492
|
+
const status = check.ok ? "PASS" : "FAIL";
|
|
493
|
+
console.log(`- [${status}] ${check.name}: ${check.detail}`);
|
|
494
|
+
}
|
|
495
|
+
if (failed.length > 0) {
|
|
496
|
+
process.exitCode = 1;
|
|
497
|
+
}
|
|
498
|
+
return;
|
|
499
|
+
}
|
|
389
500
|
model = defaultModelForProvider(provider);
|
|
390
501
|
}
|
|
391
502
|
if (isLocalLlmProvider(provider) && !baseUrl) {
|
|
@@ -423,6 +534,23 @@ async function runDoctor(args) {
|
|
|
423
534
|
});
|
|
424
535
|
}
|
|
425
536
|
catch (error) {
|
|
537
|
+
const reasonCode = typeof error === "object" && error !== null && "reason_code" in error
|
|
538
|
+
? String(error.reason_code)
|
|
539
|
+
: undefined;
|
|
540
|
+
if (reasonCode === "ollama_model_load_error") {
|
|
541
|
+
await appendStatusEventSafe({
|
|
542
|
+
source_module: "capability-ops",
|
|
543
|
+
event_type: "OLLAMA_MODEL_LOAD_ERROR",
|
|
544
|
+
status: "blocked",
|
|
545
|
+
summary: `Ollama model-load error for ${model}: ${error instanceof Error ? error.message : String(error)}`,
|
|
546
|
+
objective_id: "ollama-doctor",
|
|
547
|
+
payload: {
|
|
548
|
+
reason_code: "ollama_model_load_error",
|
|
549
|
+
model,
|
|
550
|
+
base_url: baseUrl,
|
|
551
|
+
},
|
|
552
|
+
}).catch(() => undefined);
|
|
553
|
+
}
|
|
426
554
|
checks.push({
|
|
427
555
|
name: "Runtime endpoint reachable",
|
|
428
556
|
ok: false,
|
|
@@ -448,6 +576,44 @@ async function runDoctor(args) {
|
|
|
448
576
|
? `${model} found`
|
|
449
577
|
: `${model} not reported by llama.cpp (available: ${modelNames.join(", ") || "none"})`,
|
|
450
578
|
});
|
|
579
|
+
if (provider === "ollama" && !hasModel && repairOllama) {
|
|
580
|
+
const repair = await runShellCommand(`ollama pull ${model}`, {
|
|
581
|
+
cwd: WORKSPACE_ROOT,
|
|
582
|
+
timeout_ms: 10 * 60_000,
|
|
583
|
+
});
|
|
584
|
+
await appendRunLedgerEntrySafe({
|
|
585
|
+
tool: "doctor",
|
|
586
|
+
category: repair.exit_code === 0 ? "info" : "regression",
|
|
587
|
+
message: `Opt-in Ollama repair attempted for ${model}`,
|
|
588
|
+
artifacts: [],
|
|
589
|
+
metadata: {
|
|
590
|
+
reason_code: "ollama_model_load_error",
|
|
591
|
+
model,
|
|
592
|
+
exit_code: repair.exit_code,
|
|
593
|
+
timed_out: repair.timed_out,
|
|
594
|
+
},
|
|
595
|
+
}).catch(() => undefined);
|
|
596
|
+
await appendStatusEventSafe({
|
|
597
|
+
source_module: "capability-ops",
|
|
598
|
+
event_type: "OLLAMA_REPAIR_ATTEMPTED",
|
|
599
|
+
status: repair.exit_code === 0 ? "done" : "fail",
|
|
600
|
+
summary: `Opt-in Ollama repair attempted for ${model}`,
|
|
601
|
+
objective_id: "ollama-doctor",
|
|
602
|
+
payload: {
|
|
603
|
+
reason_code: "ollama_model_load_error",
|
|
604
|
+
model,
|
|
605
|
+
exit_code: repair.exit_code,
|
|
606
|
+
timed_out: repair.timed_out,
|
|
607
|
+
},
|
|
608
|
+
}).catch(() => undefined);
|
|
609
|
+
checks.push({
|
|
610
|
+
name: "Ollama model repair attempted",
|
|
611
|
+
ok: repair.exit_code === 0,
|
|
612
|
+
detail: repair.exit_code === 0
|
|
613
|
+
? `ollama pull ${model} completed`
|
|
614
|
+
: `ollama pull ${model} failed with exit ${repair.exit_code}: ${repair.stderr || repair.stdout || "no output"}`,
|
|
615
|
+
});
|
|
616
|
+
}
|
|
451
617
|
}
|
|
452
618
|
else {
|
|
453
619
|
const diagnosis = diagnoseChatRuntimeConfig(provider, model, baseUrl ? { baseUrl } : undefined);
|
|
@@ -520,16 +686,21 @@ function parseClientFlag(args) {
|
|
|
520
686
|
return match;
|
|
521
687
|
}
|
|
522
688
|
async function runPreconfig() {
|
|
523
|
-
const
|
|
524
|
-
|
|
525
|
-
|
|
689
|
+
const resolved = await ensureCanonicalWorkspaceStore(WORKSPACE_ROOT, {
|
|
690
|
+
operationLabel: "runPreconfig",
|
|
691
|
+
});
|
|
692
|
+
if (resolved.mode === "missing") {
|
|
693
|
+
console.error(`No ACE store found. Expected canonical ${resolved.canonicalPath}; legacy fallback ${resolved.legacyPath} was also not found. Run 'ace init' first.`);
|
|
526
694
|
process.exit(1);
|
|
527
695
|
}
|
|
528
|
-
const
|
|
696
|
+
for (const w of resolved.warnings) {
|
|
697
|
+
console.warn(`[preconfig] ${w}`);
|
|
698
|
+
}
|
|
699
|
+
const store = await openStore(resolved.storePath);
|
|
529
700
|
try {
|
|
530
701
|
const mat = new HostFileMaterializer(store, WORKSPACE_ROOT);
|
|
531
702
|
const written = await mat.materializeMcpBundle();
|
|
532
|
-
console.log(`ACE preconfig complete — wrote ${written.length} files to .mcp-config
|
|
703
|
+
console.log(`ACE preconfig complete — wrote ${written.length} files to .mcp-config/ and root .mcp.json\n`);
|
|
533
704
|
for (const p of written) {
|
|
534
705
|
console.log(` ${p}`);
|
|
535
706
|
}
|
|
@@ -581,14 +752,36 @@ async function main() {
|
|
|
581
752
|
cliModel,
|
|
582
753
|
cliBaseUrl,
|
|
583
754
|
});
|
|
755
|
+
const needsLocalScan = isLocalLlmProvider(discovered.provider) &&
|
|
756
|
+
(!discovered.baseUrl || !discovered.model || discovered.model === defaultModelForProvider(discovered.provider));
|
|
757
|
+
const runtime = needsLocalScan
|
|
758
|
+
? await scanLocalModelRuntimes({
|
|
759
|
+
workspaceRoot: WORKSPACE_ROOT,
|
|
760
|
+
preferredProvider: discovered.provider,
|
|
761
|
+
explicitBaseUrl: discovered.baseUrl,
|
|
762
|
+
})
|
|
763
|
+
: undefined;
|
|
764
|
+
const scanned = runtime?.candidates.find((candidate) => candidate.provider === discovered.provider) ??
|
|
765
|
+
runtime?.candidates[0];
|
|
766
|
+
const resolvedTui = scanned?.models[0]
|
|
767
|
+
? {
|
|
768
|
+
...discovered,
|
|
769
|
+
model: scanned.models[0],
|
|
770
|
+
baseUrl: discovered.baseUrl ?? scanned.baseUrl,
|
|
771
|
+
providerBaseUrls: {
|
|
772
|
+
...discovered.providerBaseUrls,
|
|
773
|
+
[scanned.provider]: discovered.baseUrl ?? scanned.baseUrl,
|
|
774
|
+
},
|
|
775
|
+
}
|
|
776
|
+
: discovered;
|
|
584
777
|
await runTui({
|
|
585
|
-
provider:
|
|
586
|
-
model:
|
|
587
|
-
providers:
|
|
588
|
-
modelsByProvider:
|
|
589
|
-
baseUrl:
|
|
590
|
-
ollamaUrl:
|
|
591
|
-
providerBaseUrls:
|
|
778
|
+
provider: resolvedTui.provider,
|
|
779
|
+
model: resolvedTui.model,
|
|
780
|
+
providers: resolvedTui.providers,
|
|
781
|
+
modelsByProvider: resolvedTui.modelsByProvider,
|
|
782
|
+
baseUrl: resolvedTui.baseUrl,
|
|
783
|
+
ollamaUrl: resolvedTui.ollamaUrl,
|
|
784
|
+
providerBaseUrls: resolvedTui.providerBaseUrls,
|
|
592
785
|
workspaceRoot: WORKSPACE_ROOT,
|
|
593
786
|
});
|
|
594
787
|
return;
|
|
@@ -597,6 +790,10 @@ async function main() {
|
|
|
597
790
|
await runInit(args.slice(1), "init");
|
|
598
791
|
return;
|
|
599
792
|
}
|
|
793
|
+
if (command === "connect") {
|
|
794
|
+
await runConnect(args.slice(1));
|
|
795
|
+
return;
|
|
796
|
+
}
|
|
600
797
|
if (command === "turnkey") {
|
|
601
798
|
await runInit(args.slice(1), "turnkey");
|
|
602
799
|
return;
|
|
@@ -649,7 +846,7 @@ async function main() {
|
|
|
649
846
|
}
|
|
650
847
|
if (command === "migrate") {
|
|
651
848
|
const { importFromFileWorkspace } = await import("./store/importer.js");
|
|
652
|
-
const storePath = args[1] ??
|
|
849
|
+
const storePath = args[1] ?? getWorkspaceStorePath(WORKSPACE_ROOT);
|
|
653
850
|
console.log(`Migrating file workspace at ${WORKSPACE_ROOT} to store at ${storePath}...`);
|
|
654
851
|
const result = await importFromFileWorkspace(WORKSPACE_ROOT, storePath);
|
|
655
852
|
console.log(`Migration complete:`);
|