spacecode 2.9.0 → 2.10.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/dist/index.js +2 -2
- package/dist/index.js.map +2 -2
- package/dist/repl.mjs +354 -60
- package/package.json +1 -1
package/dist/repl.mjs
CHANGED
|
@@ -6019,9 +6019,9 @@ async function sha256(content) {
|
|
|
6019
6019
|
const hashArray = new Uint8Array(hashBuffer);
|
|
6020
6020
|
return Array.from(hashArray).map((b2) => b2.toString(16).padStart(2, "0")).join("");
|
|
6021
6021
|
}
|
|
6022
|
-
async function computeLockfileHash(
|
|
6022
|
+
async function computeLockfileHash(readFile11) {
|
|
6023
6023
|
for (const name of LOCKFILES) {
|
|
6024
|
-
const content = await
|
|
6024
|
+
const content = await readFile11(name);
|
|
6025
6025
|
if (content) {
|
|
6026
6026
|
const hash = await sha256(content);
|
|
6027
6027
|
return hash.slice(0, 12);
|
|
@@ -7252,6 +7252,92 @@ var init_trust = __esm({
|
|
|
7252
7252
|
}
|
|
7253
7253
|
});
|
|
7254
7254
|
|
|
7255
|
+
// ../agent/src/import/source-changes.ts
|
|
7256
|
+
function fingerprintSources(files, hash) {
|
|
7257
|
+
const fingerprint = {};
|
|
7258
|
+
for (const [path12, content] of Object.entries(files)) fingerprint[path12] = hash(content);
|
|
7259
|
+
return fingerprint;
|
|
7260
|
+
}
|
|
7261
|
+
function diffSources(before, after) {
|
|
7262
|
+
const has2 = (o, k2) => Object.hasOwn(o, k2);
|
|
7263
|
+
const added = Object.keys(after).filter((k2) => !has2(before, k2)).sort();
|
|
7264
|
+
const removed = Object.keys(before).filter((k2) => !has2(after, k2)).sort();
|
|
7265
|
+
const modified = Object.keys(after).filter((k2) => has2(before, k2) && before[k2] !== after[k2]).sort();
|
|
7266
|
+
return { added, modified, removed, changed: added.length + modified.length + removed.length > 0 };
|
|
7267
|
+
}
|
|
7268
|
+
function formatSourceChanges(c) {
|
|
7269
|
+
return [...c.added.map((p) => `added: ${p}`), ...c.modified.map((p) => `modified: ${p}`), ...c.removed.map((p) => `removed: ${p}`)];
|
|
7270
|
+
}
|
|
7271
|
+
var init_source_changes = __esm({
|
|
7272
|
+
"../agent/src/import/source-changes.ts"() {
|
|
7273
|
+
"use strict";
|
|
7274
|
+
}
|
|
7275
|
+
});
|
|
7276
|
+
|
|
7277
|
+
// ../agent/src/import/mcp-tool-cache.ts
|
|
7278
|
+
function serverCacheKey(def, hash) {
|
|
7279
|
+
return hash(JSON.stringify([def.name, def.command, def.args, [...def.envNames].sort()]));
|
|
7280
|
+
}
|
|
7281
|
+
function cleanTool(raw) {
|
|
7282
|
+
if (!isPlainObject(raw) || typeof raw.name !== "string" || raw.name === "" || typeof raw.description !== "string" || !isPlainObject(raw.inputSchema)) return null;
|
|
7283
|
+
return { name: raw.name, description: raw.description.slice(0, MAX_DESCRIPTION), inputSchema: raw.inputSchema };
|
|
7284
|
+
}
|
|
7285
|
+
function parseToolCache(text) {
|
|
7286
|
+
if (text === null || text === "") return { cache: {} };
|
|
7287
|
+
let raw;
|
|
7288
|
+
try {
|
|
7289
|
+
raw = JSON.parse(text);
|
|
7290
|
+
} catch (err) {
|
|
7291
|
+
if (!(err instanceof SyntaxError)) throw err;
|
|
7292
|
+
return { cache: {}, error: "tool cache ignored: not valid JSON" };
|
|
7293
|
+
}
|
|
7294
|
+
if (!isPlainObject(raw) || raw.version !== 1 || !isPlainObject(raw.servers)) return { cache: {}, error: "tool cache ignored: unsupported format" };
|
|
7295
|
+
const cache4 = {};
|
|
7296
|
+
for (const key of Object.keys(raw.servers)) {
|
|
7297
|
+
if (Object.keys(cache4).length >= MAX_SERVERS) break;
|
|
7298
|
+
const entry = raw.servers[key];
|
|
7299
|
+
if (!isPlainObject(entry) || !Array.isArray(entry.tools)) continue;
|
|
7300
|
+
const tools = [];
|
|
7301
|
+
for (const t of entry.tools) {
|
|
7302
|
+
const clean = cleanTool(t);
|
|
7303
|
+
if (clean) tools.push(clean);
|
|
7304
|
+
if (tools.length >= MAX_TOOLS_PER_SERVER) break;
|
|
7305
|
+
}
|
|
7306
|
+
Object.defineProperty(cache4, key, { value: { tools }, enumerable: true, writable: true, configurable: true });
|
|
7307
|
+
}
|
|
7308
|
+
return { cache: cache4 };
|
|
7309
|
+
}
|
|
7310
|
+
function serializeToolCache(cache4) {
|
|
7311
|
+
return JSON.stringify({ version: 1, servers: cache4 });
|
|
7312
|
+
}
|
|
7313
|
+
function getCachedTools(cache4, key) {
|
|
7314
|
+
return own(cache4, key) ? cache4[key].tools : void 0;
|
|
7315
|
+
}
|
|
7316
|
+
function sameTools(a, b2) {
|
|
7317
|
+
return JSON.stringify(a) === JSON.stringify(b2);
|
|
7318
|
+
}
|
|
7319
|
+
function putCachedTools(cache4, key, tools) {
|
|
7320
|
+
const existing = getCachedTools(cache4, key);
|
|
7321
|
+
if (existing && sameTools(existing, tools)) return cache4;
|
|
7322
|
+
const next = {};
|
|
7323
|
+
for (const k2 of Object.keys(cache4)) if (k2 !== key) Object.defineProperty(next, k2, { value: cache4[k2], enumerable: true, writable: true, configurable: true });
|
|
7324
|
+
Object.defineProperty(next, key, { value: { tools }, enumerable: true, writable: true, configurable: true });
|
|
7325
|
+
const keys = Object.keys(next);
|
|
7326
|
+
for (const k2 of keys.slice(0, Math.max(0, keys.length - MAX_SERVERS))) delete next[k2];
|
|
7327
|
+
return next;
|
|
7328
|
+
}
|
|
7329
|
+
var MAX_TOOLS_PER_SERVER, MAX_SERVERS, MAX_DESCRIPTION, own, isPlainObject;
|
|
7330
|
+
var init_mcp_tool_cache = __esm({
|
|
7331
|
+
"../agent/src/import/mcp-tool-cache.ts"() {
|
|
7332
|
+
"use strict";
|
|
7333
|
+
MAX_TOOLS_PER_SERVER = 200;
|
|
7334
|
+
MAX_SERVERS = 500;
|
|
7335
|
+
MAX_DESCRIPTION = 1024;
|
|
7336
|
+
own = (o, k2) => Object.hasOwn(o, k2);
|
|
7337
|
+
isPlainObject = (v2) => typeof v2 === "object" && v2 !== null && !Array.isArray(v2);
|
|
7338
|
+
}
|
|
7339
|
+
});
|
|
7340
|
+
|
|
7255
7341
|
// ../agent/src/import/imports-config.ts
|
|
7256
7342
|
function allOffConfig() {
|
|
7257
7343
|
return {
|
|
@@ -7267,7 +7353,7 @@ function allOffConfig() {
|
|
|
7267
7353
|
models: {}
|
|
7268
7354
|
};
|
|
7269
7355
|
}
|
|
7270
|
-
function
|
|
7356
|
+
function isPlainObject2(value) {
|
|
7271
7357
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
7272
7358
|
}
|
|
7273
7359
|
function parseImportsConfig(text, path12) {
|
|
@@ -7279,11 +7365,11 @@ function parseImportsConfig(text, path12) {
|
|
|
7279
7365
|
} catch {
|
|
7280
7366
|
return fail("invalid JSON");
|
|
7281
7367
|
}
|
|
7282
|
-
if (!
|
|
7368
|
+
if (!isPlainObject2(parsed)) return fail("top-level JSON must be an object");
|
|
7283
7369
|
if (parsed.version !== 1) return fail(`unsupported version ${String(parsed.version)}`);
|
|
7284
7370
|
const config = allOffConfig();
|
|
7285
7371
|
if (parsed.categories !== void 0) {
|
|
7286
|
-
if (!
|
|
7372
|
+
if (!isPlainObject2(parsed.categories)) return fail("categories must be an object");
|
|
7287
7373
|
for (const [key, value] of Object.entries(parsed.categories)) {
|
|
7288
7374
|
if (!IMPORT_CATEGORIES.includes(key)) return fail(`unknown category key "${key}"`);
|
|
7289
7375
|
if (typeof value !== "boolean") return fail(`category "${key}" must be a boolean`);
|
|
@@ -7291,7 +7377,7 @@ function parseImportsConfig(text, path12) {
|
|
|
7291
7377
|
}
|
|
7292
7378
|
}
|
|
7293
7379
|
if (parsed.models !== void 0) {
|
|
7294
|
-
if (!
|
|
7380
|
+
if (!isPlainObject2(parsed.models)) return fail("models must be an object");
|
|
7295
7381
|
for (const key of MODEL_KEYS) {
|
|
7296
7382
|
const value = parsed.models[key];
|
|
7297
7383
|
if (value === void 0) continue;
|
|
@@ -7986,6 +8072,7 @@ __export(src_exports, {
|
|
|
7986
8072
|
createSkillTool: () => createSkillTool,
|
|
7987
8073
|
createTodoTool: () => createTodoTool,
|
|
7988
8074
|
defaultModeFor: () => defaultModeFor,
|
|
8075
|
+
diffSources: () => diffSources,
|
|
7989
8076
|
estimateTokens: () => estimateTokens,
|
|
7990
8077
|
estimateTotalTokens: () => estimateTotalTokens,
|
|
7991
8078
|
expandCommand: () => expandCommand,
|
|
@@ -7993,9 +8080,12 @@ __export(src_exports, {
|
|
|
7993
8080
|
expandServerEnv: () => expandServerEnv,
|
|
7994
8081
|
extractRuleLines: () => extractRuleLines,
|
|
7995
8082
|
findUnsupported: () => findUnsupported,
|
|
8083
|
+
fingerprintSources: () => fingerprintSources,
|
|
7996
8084
|
flattenMessageText: () => flattenMessageText,
|
|
7997
8085
|
formatSkillBody: () => formatSkillBody,
|
|
8086
|
+
formatSourceChanges: () => formatSourceChanges,
|
|
7998
8087
|
formatToolResult: () => formatToolResult,
|
|
8088
|
+
getCachedTools: () => getCachedTools,
|
|
7999
8089
|
guardHost: () => guardHost,
|
|
8000
8090
|
isReadOnlyCommand: () => isReadOnlyCommand,
|
|
8001
8091
|
joinSections: () => joinSections,
|
|
@@ -8007,13 +8097,18 @@ __export(src_exports, {
|
|
|
8007
8097
|
parseImportsConfig: () => parseImportsConfig,
|
|
8008
8098
|
parseSkillFile: () => parseSkillFile,
|
|
8009
8099
|
parseTodos: () => parseTodos,
|
|
8100
|
+
parseToolCache: () => parseToolCache,
|
|
8010
8101
|
prepareAdaptation: () => prepareAdaptation,
|
|
8011
8102
|
projectKey: () => projectKey,
|
|
8103
|
+
putCachedTools: () => putCachedTools,
|
|
8012
8104
|
readClaudeAssets: () => readClaudeAssets,
|
|
8013
8105
|
renderProjectCard: () => renderProjectCard,
|
|
8014
8106
|
runAgentLoop: () => runAgentLoop,
|
|
8015
8107
|
safeSkillPath: () => safeSkillPath,
|
|
8108
|
+
sameTools: () => sameTools,
|
|
8016
8109
|
serializeImportsConfig: () => serializeImportsConfig,
|
|
8110
|
+
serializeToolCache: () => serializeToolCache,
|
|
8111
|
+
serverCacheKey: () => serverCacheKey,
|
|
8017
8112
|
siblingAssetName: () => siblingAssetName,
|
|
8018
8113
|
splitCommand: () => splitCommand,
|
|
8019
8114
|
splitSections: () => splitSections,
|
|
@@ -8060,6 +8155,8 @@ var init_src = __esm({
|
|
|
8060
8155
|
init_adapt_plan();
|
|
8061
8156
|
init_instructions_tool();
|
|
8062
8157
|
init_trust();
|
|
8158
|
+
init_source_changes();
|
|
8159
|
+
init_mcp_tool_cache();
|
|
8063
8160
|
init_imports_config();
|
|
8064
8161
|
init_permission_rules();
|
|
8065
8162
|
init_command_args();
|
|
@@ -8089,7 +8186,7 @@ __export(api_key_exports, {
|
|
|
8089
8186
|
});
|
|
8090
8187
|
import { join } from "node:path";
|
|
8091
8188
|
async function resolveApiKey(deps) {
|
|
8092
|
-
const { env: env3, readFile:
|
|
8189
|
+
const { env: env3, readFile: readFile11, homedir: homedir3 } = deps;
|
|
8093
8190
|
for (const name of ["SPACECODE_API_KEY", "ANTHROPIC_API_KEY"]) {
|
|
8094
8191
|
const value = env3[name]?.trim();
|
|
8095
8192
|
if (value) return value;
|
|
@@ -8098,7 +8195,7 @@ async function resolveApiKey(deps) {
|
|
|
8098
8195
|
const file = join(dir, "auth.json");
|
|
8099
8196
|
let raw;
|
|
8100
8197
|
try {
|
|
8101
|
-
raw = await
|
|
8198
|
+
raw = await readFile11(file);
|
|
8102
8199
|
} catch (err) {
|
|
8103
8200
|
if (err.code === "ENOENT") return null;
|
|
8104
8201
|
throw new Error(`Cannot read ${file}: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -8878,6 +8975,14 @@ var init_mcp_manager = __esm({
|
|
|
8878
8975
|
servers = /* @__PURE__ */ new Map();
|
|
8879
8976
|
turnSignal;
|
|
8880
8977
|
startPromise = null;
|
|
8978
|
+
primePromise = null;
|
|
8979
|
+
cache = {};
|
|
8980
|
+
/** Cached tools of approved, startable servers, usable until the live list replaces them. */
|
|
8981
|
+
cachedTools = /* @__PURE__ */ new Map();
|
|
8982
|
+
/** Settles when a server has connected or failed, so a call on a cached tool can wait for it. */
|
|
8983
|
+
connected = /* @__PURE__ */ new Map();
|
|
8984
|
+
cacheNotes = [];
|
|
8985
|
+
cacheWrite = Promise.resolve();
|
|
8881
8986
|
/** Server states in definition order, so output does not depend on which server connected first. */
|
|
8882
8987
|
ordered() {
|
|
8883
8988
|
return this.deps.defs.map((d) => this.servers.get(d.name)).filter((s) => s !== void 0);
|
|
@@ -8886,11 +8991,86 @@ var init_mcp_manager = __esm({
|
|
|
8886
8991
|
beginTurn(signal) {
|
|
8887
8992
|
this.turnSignal = signal;
|
|
8888
8993
|
}
|
|
8994
|
+
/**
|
|
8995
|
+
* Loads cached tool lists for servers that could start right now (stdio, approved on this machine, environment set),
|
|
8996
|
+
* without spawning anything. Await it before building the first toolset. Never throws; a bad cache is noted in /status.
|
|
8997
|
+
*/
|
|
8998
|
+
prime() {
|
|
8999
|
+
this.primePromise ??= this.primeOnce();
|
|
9000
|
+
return this.primePromise;
|
|
9001
|
+
}
|
|
9002
|
+
async primeOnce() {
|
|
9003
|
+
const tc = this.deps.toolCache;
|
|
9004
|
+
if (!tc) return;
|
|
9005
|
+
try {
|
|
9006
|
+
const parsed = parseToolCache(await tc.read());
|
|
9007
|
+
this.cache = parsed.cache;
|
|
9008
|
+
if (parsed.error) this.cacheNotes.push(parsed.error);
|
|
9009
|
+
} catch (err) {
|
|
9010
|
+
this.cacheNotes.push(`tool cache not read (${err instanceof Error ? err.message : String(err)})`);
|
|
9011
|
+
return;
|
|
9012
|
+
}
|
|
9013
|
+
for (const def of this.deps.defs) {
|
|
9014
|
+
try {
|
|
9015
|
+
if (def.transport !== "stdio") continue;
|
|
9016
|
+
const envNames = approvalEnv(def.env);
|
|
9017
|
+
if (!await this.deps.isStdioApproved({ command: def.command, args: def.args, envNames })) continue;
|
|
9018
|
+
if (!expandServerEnv({ command: def.command, args: def.args, env: def.env }, this.deps.env).ok) continue;
|
|
9019
|
+
const hit = getCachedTools(this.cache, serverCacheKey({ name: def.name, command: def.command, args: def.args, envNames }, tc.hash));
|
|
9020
|
+
if (hit) this.cachedTools.set(def.name, this.usable(def.name, hit).tools);
|
|
9021
|
+
} catch (err) {
|
|
9022
|
+
this.cacheNotes.push(`${def.name}: cached tools skipped (${err instanceof Error ? err.message : String(err)})`);
|
|
9023
|
+
}
|
|
9024
|
+
}
|
|
9025
|
+
}
|
|
8889
9026
|
/** Starts every eligible server in the background. Resolves when all have connected or failed; callers need not await it. */
|
|
8890
9027
|
start() {
|
|
8891
|
-
this.startPromise ??= Promise.all(this.deps.defs.map((def) =>
|
|
9028
|
+
this.startPromise ??= Promise.all(this.deps.defs.map((def) => {
|
|
9029
|
+
const p = this.startOne(def);
|
|
9030
|
+
this.connected.set(def.name, p);
|
|
9031
|
+
return p;
|
|
9032
|
+
})).then(() => void 0);
|
|
8892
9033
|
return this.startPromise;
|
|
8893
9034
|
}
|
|
9035
|
+
/** Tools whose full names are valid and unique, plus the ones that are not. */
|
|
9036
|
+
usable(server, listed) {
|
|
9037
|
+
const tools = [];
|
|
9038
|
+
const skipped = [];
|
|
9039
|
+
const seen = /* @__PURE__ */ new Set();
|
|
9040
|
+
for (const tool of listed) {
|
|
9041
|
+
const full = mcpToolName(server, tool.name);
|
|
9042
|
+
if (!TOOL_NAME_RE.test(full) || seen.has(full)) skipped.push(tool.name);
|
|
9043
|
+
else {
|
|
9044
|
+
seen.add(full);
|
|
9045
|
+
tools.push(tool);
|
|
9046
|
+
}
|
|
9047
|
+
}
|
|
9048
|
+
return { tools, skipped };
|
|
9049
|
+
}
|
|
9050
|
+
/** Live tools once connected; cached ones while a server is still connecting (or not started yet). */
|
|
9051
|
+
toolsOf(def) {
|
|
9052
|
+
const s = this.servers.get(def.name);
|
|
9053
|
+
if (s?.status === "ready") return s.tools;
|
|
9054
|
+
if (s === void 0 || s.status === "connecting") return this.cachedTools.get(def.name) ?? [];
|
|
9055
|
+
return [];
|
|
9056
|
+
}
|
|
9057
|
+
/** Stores the live list for the next start. Writes are serialized; a failure becomes a /status line. */
|
|
9058
|
+
saveToolCache(def, tools) {
|
|
9059
|
+
const tc = this.deps.toolCache;
|
|
9060
|
+
if (!tc) return Promise.resolve();
|
|
9061
|
+
this.cacheWrite = this.cacheWrite.then(async () => {
|
|
9062
|
+
try {
|
|
9063
|
+
const key = serverCacheKey({ name: def.name, command: def.command, args: def.args, envNames: approvalEnv(def.env) }, tc.hash);
|
|
9064
|
+
const next = putCachedTools(this.cache, key, tools.map((t) => ({ name: t.name, description: t.description, inputSchema: t.inputSchema })));
|
|
9065
|
+
if (next === this.cache) return;
|
|
9066
|
+
await tc.write(serializeToolCache(next));
|
|
9067
|
+
this.cache = next;
|
|
9068
|
+
} catch (err) {
|
|
9069
|
+
this.cacheNotes.push(`${def.name}: tool list not cached (${err instanceof Error ? err.message : String(err)})`);
|
|
9070
|
+
}
|
|
9071
|
+
});
|
|
9072
|
+
return this.cacheWrite;
|
|
9073
|
+
}
|
|
8894
9074
|
async startOne(def) {
|
|
8895
9075
|
const name = def.name;
|
|
8896
9076
|
const skip = (reason) => void this.servers.set(name, { name, def, status: "skipped", reason });
|
|
@@ -8941,19 +9121,11 @@ var init_mcp_manager = __esm({
|
|
|
8941
9121
|
});
|
|
8942
9122
|
await client.initialize();
|
|
8943
9123
|
const listed = await client.listTools();
|
|
8944
|
-
const tools =
|
|
8945
|
-
const skippedTools = [];
|
|
8946
|
-
const seen = /* @__PURE__ */ new Set();
|
|
8947
|
-
for (const tool of listed.tools) {
|
|
8948
|
-
const full = mcpToolName(name, tool.name);
|
|
8949
|
-
if (!TOOL_NAME_RE.test(full) || seen.has(full)) skippedTools.push(tool.name);
|
|
8950
|
-
else {
|
|
8951
|
-
seen.add(full);
|
|
8952
|
-
tools.push(tool);
|
|
8953
|
-
}
|
|
8954
|
-
}
|
|
9124
|
+
const { tools, skipped: skippedTools } = this.usable(name, listed.tools);
|
|
8955
9125
|
if (listed.skipped > 0) skippedTools.push(`${listed.skipped} unnamed`);
|
|
8956
9126
|
this.servers.set(name, { name, def, status: "ready", client, transport, session: session2, tools, skippedTools });
|
|
9127
|
+
this.cachedTools.delete(name);
|
|
9128
|
+
await this.saveToolCache(def, tools);
|
|
8957
9129
|
} catch (err) {
|
|
8958
9130
|
await transport?.close().catch(() => void 0);
|
|
8959
9131
|
fail(err instanceof Error ? err.message : String(err));
|
|
@@ -8962,22 +9134,29 @@ var init_mcp_manager = __esm({
|
|
|
8962
9134
|
/** Tools of every connected server, as the model sees them. */
|
|
8963
9135
|
tools() {
|
|
8964
9136
|
const out = [];
|
|
8965
|
-
for (const
|
|
8966
|
-
|
|
8967
|
-
|
|
8968
|
-
out.push({ name: mcpToolName(s.name, t.name), description: `[MCP server ${s.name}] ${t.description}`.slice(0, 1024), input_schema: t.inputSchema });
|
|
9137
|
+
for (const def of this.deps.defs) {
|
|
9138
|
+
for (const t of this.toolsOf(def)) {
|
|
9139
|
+
out.push({ name: mcpToolName(def.name, t.name), description: `[MCP server ${def.name}] ${t.description}`.slice(0, 1024), input_schema: t.inputSchema });
|
|
8969
9140
|
}
|
|
8970
9141
|
}
|
|
8971
9142
|
return out;
|
|
8972
9143
|
}
|
|
8973
9144
|
handlers() {
|
|
8974
9145
|
const out = {};
|
|
8975
|
-
for (const
|
|
8976
|
-
|
|
8977
|
-
for (const t of s.tools) out[mcpToolName(s.name, t.name)] = (input) => this.call(s.name, t.name, input);
|
|
9146
|
+
for (const def of this.deps.defs) {
|
|
9147
|
+
for (const t of this.toolsOf(def)) out[mcpToolName(def.name, t.name)] = (input) => this.callWhenReady(def.name, t.name, input);
|
|
8978
9148
|
}
|
|
8979
9149
|
return out;
|
|
8980
9150
|
}
|
|
9151
|
+
/** A call on a cached tool waits for its server to connect (bounded by the connect timeout) and then goes through the normal checks. */
|
|
9152
|
+
async callWhenReady(server, tool, input) {
|
|
9153
|
+
await this.connected.get(server);
|
|
9154
|
+
const state = this.servers.get(server);
|
|
9155
|
+
if (state?.status === "ready" && !state.tools.some((t) => t.name === tool)) {
|
|
9156
|
+
return { output: `MCP server "${server}" no longer offers ${tool}; its tool list changed since it was cached.`, is_error: true };
|
|
9157
|
+
}
|
|
9158
|
+
return this.call(server, tool, input);
|
|
9159
|
+
}
|
|
8981
9160
|
async call(server, tool, input) {
|
|
8982
9161
|
const full = mcpToolName(server, tool);
|
|
8983
9162
|
const state = this.servers.get(server);
|
|
@@ -9019,11 +9198,18 @@ var init_mcp_manager = __esm({
|
|
|
9019
9198
|
/** One line per server for /status; reasons never include env values or stderr. */
|
|
9020
9199
|
statusLines() {
|
|
9021
9200
|
const lines2 = [];
|
|
9022
|
-
for (const
|
|
9201
|
+
for (const def of this.deps.defs) {
|
|
9202
|
+
const s = this.servers.get(def.name);
|
|
9203
|
+
if (s === void 0) {
|
|
9204
|
+
const cached3 = this.cachedTools.get(def.name);
|
|
9205
|
+
if (cached3) lines2.push(`mcp: ${def.name} connecting... (${cached3.length} tools from cache)`);
|
|
9206
|
+
continue;
|
|
9207
|
+
}
|
|
9023
9208
|
if (s.status === "ready") lines2.push(`mcp: ${s.name} ready, ${s.tools.length} tools${s.skippedTools.length ? ` (not usable: ${s.skippedTools.join(", ")})` : ""}`);
|
|
9024
|
-
else if (s.status === "connecting") lines2.push(`mcp: ${s.name} connecting
|
|
9209
|
+
else if (s.status === "connecting") lines2.push(`mcp: ${s.name} connecting...${this.cachedTools.has(s.name) ? ` (${this.cachedTools.get(s.name).length} tools from cache)` : ""}`);
|
|
9025
9210
|
else lines2.push(`mcp: ${s.name} NOT available (${s.reason})`);
|
|
9026
9211
|
}
|
|
9212
|
+
for (const n of this.cacheNotes) lines2.push(`mcp: ${n}`);
|
|
9027
9213
|
return lines2;
|
|
9028
9214
|
}
|
|
9029
9215
|
/** For /tools: names grouped by server. */
|
|
@@ -44327,7 +44513,7 @@ var require_core = __commonJS({
|
|
|
44327
44513
|
return match && match.index === 0;
|
|
44328
44514
|
}
|
|
44329
44515
|
var BACKREF_RE = /\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./;
|
|
44330
|
-
function
|
|
44516
|
+
function join15(regexps, separator = "|") {
|
|
44331
44517
|
let numCaptures = 0;
|
|
44332
44518
|
return regexps.map((regex2) => {
|
|
44333
44519
|
numCaptures += 1;
|
|
@@ -44631,7 +44817,7 @@ var require_core = __commonJS({
|
|
|
44631
44817
|
this.exec = () => null;
|
|
44632
44818
|
}
|
|
44633
44819
|
const terminators = this.regexes.map((el) => el[1]);
|
|
44634
|
-
this.matcherRe = langRe(
|
|
44820
|
+
this.matcherRe = langRe(join15(terminators), true);
|
|
44635
44821
|
this.lastIndex = 0;
|
|
44636
44822
|
}
|
|
44637
44823
|
/** @param {string} s */
|
|
@@ -103444,6 +103630,7 @@ var init_slash_commands = __esm({
|
|
|
103444
103630
|
{ name: "turns", description: "Browse earlier turns and open their steps (shortcut: ctrl+o)" },
|
|
103445
103631
|
{ name: "commands", description: "List the slash commands imported from Claude Code" },
|
|
103446
103632
|
{ name: "skills", description: "List imported skills; /skills show <name> [file], /skills delete <name>" },
|
|
103633
|
+
{ name: "import", description: "Re-check imported Claude Code sources: /import reload shows changes; /import reload trust applies them" },
|
|
103447
103634
|
{ name: "tools", description: "List the tools available to the agent" },
|
|
103448
103635
|
{ name: "exit", description: "Quit the REPL (alias: /quit)" },
|
|
103449
103636
|
{ name: "quit", description: "Quit the REPL" }
|
|
@@ -104974,12 +105161,37 @@ var init_skills_command = __esm({
|
|
|
104974
105161
|
}
|
|
104975
105162
|
});
|
|
104976
105163
|
|
|
104977
|
-
// src/
|
|
105164
|
+
// src/mcp/tool-cache-file.ts
|
|
104978
105165
|
import { readFile as readFile8 } from "node:fs/promises";
|
|
104979
105166
|
import { join as join11 } from "node:path";
|
|
105167
|
+
function nodeToolCache(configDir) {
|
|
105168
|
+
const path12 = join11(configDir, "mcp-tools-cache.json");
|
|
105169
|
+
return {
|
|
105170
|
+
read: async () => {
|
|
105171
|
+
try {
|
|
105172
|
+
return await readFile8(path12, "utf8");
|
|
105173
|
+
} catch (err) {
|
|
105174
|
+
if (err.code === "ENOENT") return null;
|
|
105175
|
+
throw err;
|
|
105176
|
+
}
|
|
105177
|
+
},
|
|
105178
|
+
write: (text) => writeTextAtomic(path12, text),
|
|
105179
|
+
hash: sha2562
|
|
105180
|
+
};
|
|
105181
|
+
}
|
|
105182
|
+
var init_tool_cache_file = __esm({
|
|
105183
|
+
"src/mcp/tool-cache-file.ts"() {
|
|
105184
|
+
"use strict";
|
|
105185
|
+
init_import_gate();
|
|
105186
|
+
}
|
|
105187
|
+
});
|
|
105188
|
+
|
|
105189
|
+
// src/permission-rules-loader.ts
|
|
105190
|
+
import { readFile as readFile9 } from "node:fs/promises";
|
|
105191
|
+
import { join as join12 } from "node:path";
|
|
104980
105192
|
async function readOrNull2(path12) {
|
|
104981
105193
|
try {
|
|
104982
|
-
return await
|
|
105194
|
+
return await readFile9(path12, "utf8");
|
|
104983
105195
|
} catch (err) {
|
|
104984
105196
|
if (err.code === "ENOENT") return null;
|
|
104985
105197
|
throw err;
|
|
@@ -104987,7 +105199,7 @@ async function readOrNull2(path12) {
|
|
|
104987
105199
|
}
|
|
104988
105200
|
async function loadPermissionRules(deps) {
|
|
104989
105201
|
const none = (line) => ({ rules: void 0, defaultMode: void 0, statusLines: [line] });
|
|
104990
|
-
const configPath =
|
|
105202
|
+
const configPath = join12(deps.configDir, "imports.json");
|
|
104991
105203
|
try {
|
|
104992
105204
|
const loaded = parseImportsConfig(await readOrNull2(configPath), configPath);
|
|
104993
105205
|
if (loaded.error) return none("permission rules: off (imports.json unreadable)");
|
|
@@ -104996,13 +105208,13 @@ async function loadPermissionRules(deps) {
|
|
|
104996
105208
|
return none(`permission rules: off (imports.json not read: ${err instanceof Error ? err.message : String(err)})`);
|
|
104997
105209
|
}
|
|
104998
105210
|
const lines2 = [];
|
|
104999
|
-
const files = [{ scope: "user", path:
|
|
105211
|
+
const files = [{ scope: "user", path: join12(deps.homeDir, ".claude", "settings.json") }];
|
|
105000
105212
|
try {
|
|
105001
105213
|
const sources2 = await readRepoSources(deps.cwd);
|
|
105002
105214
|
if (sources2[".claude/settings.json"] !== void 0 || sources2[".claude/settings.local.json"] !== void 0) {
|
|
105003
105215
|
const status = await createNodeTrustStore(deps.configDir).repoStatus(deps.cwd, sources2);
|
|
105004
105216
|
if (status === "trusted" || status === "session") {
|
|
105005
|
-
files.push({ scope: "repo", path:
|
|
105217
|
+
files.push({ scope: "repo", path: join12(deps.cwd, ".claude", "settings.json") }, { scope: "local", path: join12(deps.cwd, ".claude", "settings.local.json") });
|
|
105006
105218
|
} else lines2.push(`permission rules: repo settings NOT loaded (${status === "never" ? "marked never trust" : "untrusted repo"})`);
|
|
105007
105219
|
}
|
|
105008
105220
|
} catch (err) {
|
|
@@ -105043,8 +105255,43 @@ var init_permission_rules_loader = __esm({
|
|
|
105043
105255
|
}
|
|
105044
105256
|
});
|
|
105045
105257
|
|
|
105258
|
+
// src/import-reload.ts
|
|
105259
|
+
async function checkRepoSources(deps, loaded) {
|
|
105260
|
+
const files = await readRepoSources(deps.cwd);
|
|
105261
|
+
const current = fingerprintSources(files, sha2562);
|
|
105262
|
+
const status = Object.keys(files).length === 0 ? "untrusted" : await createNodeTrustStore(deps.configDir).repoStatus(deps.cwd, files);
|
|
105263
|
+
return { current, changes: diffSources(loaded, current), trusted: status === "trusted" || status === "session" };
|
|
105264
|
+
}
|
|
105265
|
+
async function describeReload(deps, loaded) {
|
|
105266
|
+
const check = await checkRepoSources(deps, loaded);
|
|
105267
|
+
if (!check.changes.changed) return ["No change in the repo import sources since they were loaded."];
|
|
105268
|
+
const files = await readRepoSources(deps.cwd);
|
|
105269
|
+
return [
|
|
105270
|
+
"Repo import sources changed since they were loaded:",
|
|
105271
|
+
...formatSourceChanges(check.changes).map((l) => ` ${l}`),
|
|
105272
|
+
"",
|
|
105273
|
+
"Trusting the new state would load:",
|
|
105274
|
+
...describeRepoSources(files).length ? describeRepoSources(files) : [" (nothing)"],
|
|
105275
|
+
"",
|
|
105276
|
+
"Run /import reload trust to trust it and apply the rules and commands. MCP servers and skills apply on the next start."
|
|
105277
|
+
];
|
|
105278
|
+
}
|
|
105279
|
+
async function trustCurrentSources(deps) {
|
|
105280
|
+
const files = await readRepoSources(deps.cwd);
|
|
105281
|
+
await createNodeTrustStore(deps.configDir).setRepo(deps.cwd, files, "trusted");
|
|
105282
|
+
return fingerprintSources(files, sha2562);
|
|
105283
|
+
}
|
|
105284
|
+
var init_import_reload = __esm({
|
|
105285
|
+
"src/import-reload.ts"() {
|
|
105286
|
+
"use strict";
|
|
105287
|
+
init_src();
|
|
105288
|
+
init_import_gate();
|
|
105289
|
+
init_repo_trust_prompt();
|
|
105290
|
+
}
|
|
105291
|
+
});
|
|
105292
|
+
|
|
105046
105293
|
// src/command-loader.ts
|
|
105047
|
-
import { join as
|
|
105294
|
+
import { join as join13 } from "node:path";
|
|
105048
105295
|
async function userCommands(deps, lines2) {
|
|
105049
105296
|
try {
|
|
105050
105297
|
const apiKey = await resolveApiKey({ env: deps.env, readFile: deps.readFile, homedir: deps.homedir });
|
|
@@ -105099,7 +105346,7 @@ async function repoCommands(deps, lines2) {
|
|
|
105099
105346
|
}
|
|
105100
105347
|
}
|
|
105101
105348
|
async function loadCommands(deps) {
|
|
105102
|
-
const configPath =
|
|
105349
|
+
const configPath = join13(deps.configDir, "imports.json");
|
|
105103
105350
|
try {
|
|
105104
105351
|
const loaded = parseImportsConfig(await deps.readFile(configPath).catch((err) => {
|
|
105105
105352
|
if (err.code === "ENOENT") return null;
|
|
@@ -105132,9 +105379,9 @@ var init_command_loader = __esm({
|
|
|
105132
105379
|
});
|
|
105133
105380
|
|
|
105134
105381
|
// src/useReplController.ts
|
|
105135
|
-
import { readFile as
|
|
105382
|
+
import { readFile as readFile10, writeFile as writeFile6, mkdir as mkdir6 } from "node:fs/promises";
|
|
105136
105383
|
import { homedir as homedir2 } from "node:os";
|
|
105137
|
-
import { join as
|
|
105384
|
+
import { join as join14 } from "node:path";
|
|
105138
105385
|
import { execFile as execFile4 } from "node:child_process";
|
|
105139
105386
|
function useReplController({ repoName: repoName2, yolo: yolo2, mentions: mentionsOpt, sessions, resumeLatest: resumeLatest2, onViewTurns, onViewResume, isEscOwned }) {
|
|
105140
105387
|
const { exit } = use_app_default();
|
|
@@ -105157,7 +105404,7 @@ function useReplController({ repoName: repoName2, yolo: yolo2, mentions: mention
|
|
|
105157
105404
|
}, [turnStartedAt]);
|
|
105158
105405
|
const projectRoot = (0, import_react30.useMemo)(() => process.cwd(), []);
|
|
105159
105406
|
const promptTemplates = (0, import_react30.useCallback)(
|
|
105160
|
-
() => loadPromptTemplates({ env: process.env, readFile: (p) =>
|
|
105407
|
+
() => loadPromptTemplates({ env: process.env, readFile: (p) => readFile10(p, "utf-8"), writeFile: (p, d) => writeFile6(p, d, "utf-8"), mkdir: (p) => mkdir6(p, { recursive: true }).then(() => void 0), homedir: homedir2, fetch }),
|
|
105161
105408
|
[]
|
|
105162
105409
|
);
|
|
105163
105410
|
(0, import_react30.useEffect)(() => {
|
|
@@ -105173,11 +105420,11 @@ function useReplController({ repoName: repoName2, yolo: yolo2, mentions: mention
|
|
|
105173
105420
|
const recordingRef = (0, import_react30.useRef)(null);
|
|
105174
105421
|
const recording = () => recordingRef.current ??= createRecordingRuntime({
|
|
105175
105422
|
env: process.env,
|
|
105176
|
-
configDir: process.env.SPACECODE_CONFIG_DIR?.trim() ||
|
|
105423
|
+
configDir: process.env.SPACECODE_CONFIG_DIR?.trim() || join14(homedir2(), ".spacecode"),
|
|
105177
105424
|
projectRoot,
|
|
105178
105425
|
fetch,
|
|
105179
105426
|
baseUrl: backendUrl(process.env),
|
|
105180
|
-
resolveApiKey: () => resolveApiKey({ env: process.env, readFile: (p) =>
|
|
105427
|
+
resolveApiKey: () => resolveApiKey({ env: process.env, readFile: (p) => readFile10(p, "utf-8"), homedir: homedir2 }).catch(() => null),
|
|
105181
105428
|
log: (m2) => console.error(`[agent-ui] ${m2}`)
|
|
105182
105429
|
});
|
|
105183
105430
|
const resumeListRef = (0, import_react30.useRef)([]);
|
|
@@ -105196,14 +105443,15 @@ function useReplController({ repoName: repoName2, yolo: yolo2, mentions: mention
|
|
|
105196
105443
|
const pendingRef = (0, import_react30.useRef)(null);
|
|
105197
105444
|
const toolsetRef = (0, import_react30.useRef)(null);
|
|
105198
105445
|
const rulesRef = (0, import_react30.useRef)(void 0);
|
|
105446
|
+
const loadedSourcesRef = (0, import_react30.useRef)(null);
|
|
105199
105447
|
const [commandsLoaded, setCommandsLoaded] = (0, import_react30.useState)({ commands: [], statusLines: [] });
|
|
105200
105448
|
(0, import_react30.useEffect)(() => {
|
|
105201
105449
|
let live2 = true;
|
|
105202
105450
|
loadCommands({
|
|
105203
|
-
configDir: process.env.SPACECODE_CONFIG_DIR?.trim() ||
|
|
105451
|
+
configDir: process.env.SPACECODE_CONFIG_DIR?.trim() || join14(homedir2(), ".spacecode"),
|
|
105204
105452
|
cwd: process.cwd(),
|
|
105205
105453
|
env: process.env,
|
|
105206
|
-
readFile: (p) =>
|
|
105454
|
+
readFile: (p) => readFile10(p, "utf-8"),
|
|
105207
105455
|
homedir: homedir2,
|
|
105208
105456
|
fetch,
|
|
105209
105457
|
builtins: SLASH_COMMANDS.map((c) => c.name)
|
|
@@ -105420,6 +105668,29 @@ function useReplController({ repoName: repoName2, yolo: yolo2, mentions: mention
|
|
|
105420
105668
|
case "context":
|
|
105421
105669
|
notice(formatContextReport({ contextTokens: estimateTotalTokens("", history), maxContextTokens: DEFAULT_MAX_CONTEXT_TOKENS }));
|
|
105422
105670
|
return;
|
|
105671
|
+
case "import": {
|
|
105672
|
+
const configDir = process.env.SPACECODE_CONFIG_DIR?.trim() || join14(homedir2(), ".spacecode");
|
|
105673
|
+
const deps = { configDir, cwd: process.cwd() };
|
|
105674
|
+
const [sub, ...rest] = slash.args.split(/\s+/).filter(Boolean);
|
|
105675
|
+
if (sub !== "reload" || rest.length > 1 || rest[0] !== void 0 && rest[0] !== "trust") {
|
|
105676
|
+
notice("Usage: /import reload (show changes to the repo import sources) or /import reload trust (trust the new state and apply it).");
|
|
105677
|
+
return;
|
|
105678
|
+
}
|
|
105679
|
+
try {
|
|
105680
|
+
const baseline = loadedSourcesRef.current ?? fingerprintSources(await readRepoSources(process.cwd()), sha2562);
|
|
105681
|
+
if (rest[0] === "trust") {
|
|
105682
|
+
loadedSourcesRef.current = await trustCurrentSources(deps);
|
|
105683
|
+
const loaded = await loadPermissionRules({ configDir, homeDir: homedir2(), cwd: process.cwd() });
|
|
105684
|
+
rulesRef.current = loaded.rules;
|
|
105685
|
+
const cmds = await loadCommands({ configDir, cwd: process.cwd(), env: process.env, readFile: (p) => readFile10(p, "utf-8"), homedir: homedir2, fetch, builtins: SLASH_COMMANDS.map((c) => c.name) });
|
|
105686
|
+
setCommandsLoaded(cmds);
|
|
105687
|
+
notice(["Trusted the repo import sources as they are now.", ...loaded.statusLines, ...cmds.statusLines, "MCP servers and skills apply on the next start."].join("\n"));
|
|
105688
|
+
} else notice((await describeReload(deps, baseline)).join("\n"));
|
|
105689
|
+
} catch (err) {
|
|
105690
|
+
notice(`/import reload failed (${err instanceof Error ? err.message : String(err)}); nothing was changed.`);
|
|
105691
|
+
}
|
|
105692
|
+
return;
|
|
105693
|
+
}
|
|
105423
105694
|
case "record":
|
|
105424
105695
|
notice(await recording().command(slash.args));
|
|
105425
105696
|
return;
|
|
@@ -105491,7 +105762,7 @@ function useReplController({ repoName: repoName2, yolo: yolo2, mentions: mention
|
|
|
105491
105762
|
}
|
|
105492
105763
|
setIsRunning(true);
|
|
105493
105764
|
try {
|
|
105494
|
-
const apiKey2 = await resolveApiKey({ env: process.env, readFile: (p) =>
|
|
105765
|
+
const apiKey2 = await resolveApiKey({ env: process.env, readFile: (p) => readFile10(p, "utf-8"), homedir: homedir2 });
|
|
105495
105766
|
if (!apiKey2) {
|
|
105496
105767
|
notice(MISSING_KEY_MESSAGE);
|
|
105497
105768
|
return;
|
|
@@ -105513,7 +105784,7 @@ function useReplController({ repoName: repoName2, yolo: yolo2, mentions: mention
|
|
|
105513
105784
|
}
|
|
105514
105785
|
case "model": {
|
|
105515
105786
|
try {
|
|
105516
|
-
const apiKey2 = await resolveApiKey({ env: process.env, readFile: (p) =>
|
|
105787
|
+
const apiKey2 = await resolveApiKey({ env: process.env, readFile: (p) => readFile10(p, "utf-8"), homedir: homedir2 });
|
|
105517
105788
|
if (!apiKey2) {
|
|
105518
105789
|
notice(MISSING_KEY_MESSAGE);
|
|
105519
105790
|
return;
|
|
@@ -105548,7 +105819,7 @@ ${list2.map((c) => ` /${c.name} (${c.scope})${c.description ? ` ${c.descripti
|
|
|
105548
105819
|
}
|
|
105549
105820
|
case "skills": {
|
|
105550
105821
|
const state = profileRef.current;
|
|
105551
|
-
const apiKey2 = await resolveApiKey({ env: process.env, readFile: (p) =>
|
|
105822
|
+
const apiKey2 = await resolveApiKey({ env: process.env, readFile: (p) => readFile10(p, "utf-8"), homedir: homedir2 }).catch(() => null);
|
|
105552
105823
|
const client = apiKey2 ? createProfileClient({ baseUrl: backendUrl(process.env), apiKey: apiKey2, fetch }) : null;
|
|
105553
105824
|
notice(await runSkillsCommand(slash.args, { source: state?.skills ?? null, client, statusLines: state?.statusLines.filter((l) => l.startsWith("skills")) ?? ["skills: not loaded yet (loads on your first message)"] }));
|
|
105554
105825
|
return;
|
|
@@ -105570,7 +105841,7 @@ ${list2.map((c) => ` /${c.name} (${c.scope})${c.description ? ` ${c.descripti
|
|
|
105570
105841
|
try {
|
|
105571
105842
|
apiKey = await resolveApiKey({
|
|
105572
105843
|
env: process.env,
|
|
105573
|
-
readFile: (p) =>
|
|
105844
|
+
readFile: (p) => readFile10(p, "utf-8"),
|
|
105574
105845
|
homedir: homedir2
|
|
105575
105846
|
});
|
|
105576
105847
|
} catch (err) {
|
|
@@ -105605,9 +105876,25 @@ ${list2.map((c) => ` /${c.name} (${c.scope})${c.description ? ` ${c.descripti
|
|
|
105605
105876
|
let failed = false;
|
|
105606
105877
|
let keepForRetry = false;
|
|
105607
105878
|
try {
|
|
105879
|
+
if (loadedSourcesRef.current) {
|
|
105880
|
+
const configDir = process.env.SPACECODE_CONFIG_DIR?.trim() || join14(homedir2(), ".spacecode");
|
|
105881
|
+
try {
|
|
105882
|
+
const check = await checkRepoSources({ configDir, cwd: process.cwd() }, loadedSourcesRef.current);
|
|
105883
|
+
if (check.changes.changed) {
|
|
105884
|
+
const loaded = await loadPermissionRules({ configDir, homeDir: homedir2(), cwd: process.cwd() });
|
|
105885
|
+
rulesRef.current = loaded.rules;
|
|
105886
|
+
loadedSourcesRef.current = check.current;
|
|
105887
|
+
const lead = check.trusted ? "Repo import sources changed since they were loaded; the new state is already trusted, so its permission rules now apply:" : "Repo import sources changed since they were loaded; repo permission rules are dropped until you trust the new state:";
|
|
105888
|
+
notice([lead, ...formatSourceChanges(check.changes).map((l) => ` ${l}`), ...check.trusted ? [] : ["Run /import reload to review, /import reload trust to apply."]].join("\n"));
|
|
105889
|
+
}
|
|
105890
|
+
} catch (err) {
|
|
105891
|
+
rulesRef.current = void 0;
|
|
105892
|
+
notice(`Repo import source check failed (${err instanceof Error ? err.message : String(err)}); repo permission rules are dropped.`);
|
|
105893
|
+
}
|
|
105894
|
+
}
|
|
105608
105895
|
profilePromiseRef.current ??= prepareProfile({
|
|
105609
105896
|
env: process.env,
|
|
105610
|
-
readFile: (p) =>
|
|
105897
|
+
readFile: (p) => readFile10(p, "utf-8"),
|
|
105611
105898
|
writeFile: (p, d) => writeFile6(p, d, { encoding: "utf-8", mode: 384 }),
|
|
105612
105899
|
mkdir: async (p) => {
|
|
105613
105900
|
await mkdir6(p, { recursive: true });
|
|
@@ -105617,17 +105904,19 @@ ${list2.map((c) => ` /${c.name} (${c.scope})${c.description ? ` ${c.descripti
|
|
|
105617
105904
|
cwd: process.cwd(),
|
|
105618
105905
|
gitRoot: gitTopLevel,
|
|
105619
105906
|
importGate: async () => {
|
|
105620
|
-
const configDir = process.env.SPACECODE_CONFIG_DIR?.trim() ||
|
|
105907
|
+
const configDir = process.env.SPACECODE_CONFIG_DIR?.trim() || join14(homedir2(), ".spacecode");
|
|
105908
|
+
const fingerprint = fingerprintSources(await readRepoSources(process.cwd()), sha2562);
|
|
105621
105909
|
const gate = await importGateStatus({ configDir, cwd: process.cwd(), interactive: Boolean(process.stdin.isTTY) });
|
|
105622
105910
|
const loaded = await loadPermissionRules({ configDir, homeDir: homedir2(), cwd: process.cwd() });
|
|
105623
105911
|
rulesRef.current = loaded.rules;
|
|
105912
|
+
loadedSourcesRef.current = fingerprint;
|
|
105624
105913
|
if (loaded.defaultMode && !yolo2) permissions.set(loaded.defaultMode);
|
|
105625
105914
|
return [...gate, ...loaded.statusLines];
|
|
105626
105915
|
}
|
|
105627
105916
|
});
|
|
105628
105917
|
profileRef.current = await profilePromiseRef.current;
|
|
105629
105918
|
if (mcpRef.current === null && (profileRef.current?.mcpDefs?.length ?? 0) > 0) {
|
|
105630
|
-
const configDir = process.env.SPACECODE_CONFIG_DIR?.trim() ||
|
|
105919
|
+
const configDir = process.env.SPACECODE_CONFIG_DIR?.trim() || join14(homedir2(), ".spacecode");
|
|
105631
105920
|
const trust = createNodeTrustStore(configDir);
|
|
105632
105921
|
const manager = new McpManager({
|
|
105633
105922
|
defs: profileRef.current.mcpDefs,
|
|
@@ -105637,10 +105926,12 @@ ${list2.map((c) => ` /${c.name} (${c.scope})${c.description ? ` ${c.descripti
|
|
|
105637
105926
|
...rulesRef.current ? { rules: rulesRef.current } : {},
|
|
105638
105927
|
permissions,
|
|
105639
105928
|
confirm: askUser,
|
|
105640
|
-
audit: new FileAuditLog(
|
|
105641
|
-
clientVersion: process.env.SPACECODE_VERSION ?? "0"
|
|
105929
|
+
audit: new FileAuditLog(join14(homedir2(), ".spacecode", "audit.jsonl")),
|
|
105930
|
+
clientVersion: process.env.SPACECODE_VERSION ?? "0",
|
|
105931
|
+
toolCache: nodeToolCache(configDir)
|
|
105642
105932
|
});
|
|
105643
105933
|
mcpRef.current = manager;
|
|
105934
|
+
await manager.prime();
|
|
105644
105935
|
void manager.start();
|
|
105645
105936
|
}
|
|
105646
105937
|
const toolset = createToolset();
|
|
@@ -105778,8 +106069,11 @@ var init_useReplController = __esm({
|
|
|
105778
106069
|
init_skills_command();
|
|
105779
106070
|
init_src();
|
|
105780
106071
|
init_mcp_manager();
|
|
106072
|
+
init_tool_cache_file();
|
|
105781
106073
|
init_import_gate();
|
|
105782
106074
|
init_permission_rules_loader();
|
|
106075
|
+
init_import_reload();
|
|
106076
|
+
init_src();
|
|
105783
106077
|
init_command_loader();
|
|
105784
106078
|
init_slash_commands();
|
|
105785
106079
|
init_useEventAccumulator();
|
|
@@ -106365,7 +106659,7 @@ function clearOnResize(stdout) {
|
|
|
106365
106659
|
var import_jsx_runtime14 = __toESM(require_jsx_runtime(), 1);
|
|
106366
106660
|
if (process.argv[2] === "import-claude") {
|
|
106367
106661
|
const { runImportClaude: runImportClaude2 } = await Promise.resolve().then(() => (init_import_claude(), import_claude_exports));
|
|
106368
|
-
const { readFile:
|
|
106662
|
+
const { readFile: readFile11, readdir: readdir6 } = await import("node:fs/promises");
|
|
106369
106663
|
const { execFile: execFile5 } = await import("node:child_process");
|
|
106370
106664
|
const gitRoot = () => new Promise((resolve2) => {
|
|
106371
106665
|
execFile5("git", ["rev-parse", "--show-toplevel"], { cwd: process.cwd() }, (err, stdout) => resolve2(err ? null : stdout.trim() || null));
|
|
@@ -106373,8 +106667,8 @@ if (process.argv[2] === "import-claude") {
|
|
|
106373
106667
|
const code = await runImportClaude2({
|
|
106374
106668
|
argv: process.argv.slice(2),
|
|
106375
106669
|
env: process.env,
|
|
106376
|
-
fs: { readFile: (p) =>
|
|
106377
|
-
readFile: (p) =>
|
|
106670
|
+
fs: { readFile: (p) => readFile11(p, "utf-8"), readdir: (p) => readdir6(p) },
|
|
106671
|
+
readFile: (p) => readFile11(p, "utf-8"),
|
|
106378
106672
|
homedir: os5.homedir,
|
|
106379
106673
|
cwd: process.cwd(),
|
|
106380
106674
|
gitRoot,
|
|
@@ -106399,9 +106693,9 @@ if (process.argv[2] === "approve-mcp") {
|
|
|
106399
106693
|
const { resolveApiKey: resolveApiKey2 } = await Promise.resolve().then(() => (init_api_key(), api_key_exports));
|
|
106400
106694
|
const { backendUrl: backendUrl2 } = await Promise.resolve().then(() => (init_profile_runtime(), profile_runtime_exports));
|
|
106401
106695
|
const { createProfileClient: createProfileClient2 } = await Promise.resolve().then(() => (init_src(), src_exports));
|
|
106402
|
-
const { readFile:
|
|
106696
|
+
const { readFile: readFile11 } = await import("node:fs/promises");
|
|
106403
106697
|
const configDir = process.env.SPACECODE_CONFIG_DIR?.trim() || path11.join(os5.homedir(), ".spacecode");
|
|
106404
|
-
const apiKey = await resolveApiKey2({ env: process.env, readFile: (p) =>
|
|
106698
|
+
const apiKey = await resolveApiKey2({ env: process.env, readFile: (p) => readFile11(p, "utf-8"), homedir: os5.homedir }).catch(() => null);
|
|
106405
106699
|
const interactive = Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
106406
106700
|
const { createInterface } = interactive ? await import("node:readline/promises") : { createInterface: void 0 };
|
|
106407
106701
|
const rl = createInterface ? createInterface({ input: process.stdin, output: process.stdout }) : void 0;
|
|
@@ -106409,7 +106703,7 @@ if (process.argv[2] === "approve-mcp") {
|
|
|
106409
106703
|
const code = await runApproveMcp2({
|
|
106410
106704
|
configDir,
|
|
106411
106705
|
cwd: process.cwd(),
|
|
106412
|
-
readFile: (p) =>
|
|
106706
|
+
readFile: (p) => readFile11(p, "utf-8"),
|
|
106413
106707
|
client: apiKey ? createProfileClient2({ baseUrl: backendUrl2(process.env), apiKey, fetch }) : null,
|
|
106414
106708
|
trust: createNodeTrustStore2(configDir),
|
|
106415
106709
|
...rl ? { ask: async (q2) => {
|