spacecode 2.10.0 → 2.10.2
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 +1 -1
- package/dist/repl.mjs +480 -1380
- package/package.json +1 -1
package/dist/repl.mjs
CHANGED
|
@@ -3490,12 +3490,6 @@ function truncateOutput(text, maxChars) {
|
|
|
3490
3490
|
|
|
3491
3491
|
` + text.slice(text.length - effectiveTail);
|
|
3492
3492
|
}
|
|
3493
|
-
function createSimpleRouter(opts) {
|
|
3494
|
-
const threshold = opts.complexAfterTurn ?? 5;
|
|
3495
|
-
return (turn, _lastToolCount) => {
|
|
3496
|
-
return turn > threshold ? opts.complex : opts.default;
|
|
3497
|
-
};
|
|
3498
|
-
}
|
|
3499
3493
|
async function runAgentLoop(prompt, config, executor, emit, signal, existingMessages) {
|
|
3500
3494
|
const defaultModel = config.model ?? DEFAULT_MODEL;
|
|
3501
3495
|
const maxTurns = config.maxTurns ?? DEFAULT_MAX_TURNS;
|
|
@@ -4211,98 +4205,9 @@ ${s.lines.join("\n")}`)].join("\n");
|
|
|
4211
4205
|
});
|
|
4212
4206
|
|
|
4213
4207
|
// ../agent/src/storage/file-store.ts
|
|
4214
|
-
var FileStore;
|
|
4215
4208
|
var init_file_store = __esm({
|
|
4216
4209
|
"../agent/src/storage/file-store.ts"() {
|
|
4217
4210
|
"use strict";
|
|
4218
|
-
FileStore = class {
|
|
4219
|
-
constructor(db, sessionId) {
|
|
4220
|
-
this.db = db;
|
|
4221
|
-
this.sessionId = sessionId;
|
|
4222
|
-
}
|
|
4223
|
-
async readFile(path12) {
|
|
4224
|
-
const stmt = this.db.prepare(
|
|
4225
|
-
"SELECT content, status FROM session_files WHERE session_id = ? AND path = ?"
|
|
4226
|
-
).bind(this.sessionId, path12);
|
|
4227
|
-
const row = await stmt.first();
|
|
4228
|
-
if (row) {
|
|
4229
|
-
if (row.status === "deleted") return null;
|
|
4230
|
-
if (row.content !== null) return row.content;
|
|
4231
|
-
}
|
|
4232
|
-
return null;
|
|
4233
|
-
}
|
|
4234
|
-
async writeFile(path12, content) {
|
|
4235
|
-
await this.db.prepare(
|
|
4236
|
-
`INSERT OR REPLACE INTO session_files (session_id, path, status, content, updated_at)
|
|
4237
|
-
VALUES (?, ?, 'created', ?, ?)`
|
|
4238
|
-
).bind(this.sessionId, path12, content, Date.now()).run();
|
|
4239
|
-
}
|
|
4240
|
-
async editFile(path12, oldStr, newStr) {
|
|
4241
|
-
const content = await this.readFile(path12);
|
|
4242
|
-
if (content === null) {
|
|
4243
|
-
return { success: false, error: `File not found: ${path12}` };
|
|
4244
|
-
}
|
|
4245
|
-
if (!content.includes(oldStr)) {
|
|
4246
|
-
return { success: false, error: `String not found in ${path12}` };
|
|
4247
|
-
}
|
|
4248
|
-
const occurrences = content.split(oldStr).length - 1;
|
|
4249
|
-
if (occurrences > 1) {
|
|
4250
|
-
return { success: false, error: `String appears ${occurrences} times in ${path12}. Provide more context to make it unique.` };
|
|
4251
|
-
}
|
|
4252
|
-
const newContent = content.replace(oldStr, newStr);
|
|
4253
|
-
await this.db.prepare(
|
|
4254
|
-
`INSERT OR REPLACE INTO session_files (session_id, path, status, content, updated_at)
|
|
4255
|
-
VALUES (?, ?, 'modified', ?, ?)`
|
|
4256
|
-
).bind(this.sessionId, path12, newContent, Date.now()).run();
|
|
4257
|
-
return { success: true };
|
|
4258
|
-
}
|
|
4259
|
-
async listDirectory(dirPath) {
|
|
4260
|
-
const prefix = dirPath.endsWith("/") ? dirPath : `${dirPath}/`;
|
|
4261
|
-
const stmt = this.db.prepare(
|
|
4262
|
-
`SELECT DISTINCT path FROM session_files
|
|
4263
|
-
WHERE session_id = ? AND path LIKE ? AND status != 'deleted'`
|
|
4264
|
-
).bind(this.sessionId, `${prefix}%`);
|
|
4265
|
-
const { results } = await stmt.all();
|
|
4266
|
-
const children = /* @__PURE__ */ new Set();
|
|
4267
|
-
for (const row of results) {
|
|
4268
|
-
const relative2 = row.path.slice(prefix.length);
|
|
4269
|
-
const firstSlash = relative2.indexOf("/");
|
|
4270
|
-
if (firstSlash === -1) {
|
|
4271
|
-
children.add(relative2);
|
|
4272
|
-
} else {
|
|
4273
|
-
children.add(relative2.slice(0, firstSlash + 1));
|
|
4274
|
-
}
|
|
4275
|
-
}
|
|
4276
|
-
return Array.from(children).sort();
|
|
4277
|
-
}
|
|
4278
|
-
async getDirtyFiles() {
|
|
4279
|
-
const stmt = this.db.prepare(
|
|
4280
|
-
`SELECT path, content, status FROM session_files
|
|
4281
|
-
WHERE session_id = ? AND status IN ('modified', 'created')`
|
|
4282
|
-
).bind(this.sessionId);
|
|
4283
|
-
const { results } = await stmt.all();
|
|
4284
|
-
return results;
|
|
4285
|
-
}
|
|
4286
|
-
async markClean(paths) {
|
|
4287
|
-
if (paths.length === 0) return;
|
|
4288
|
-
const stmts = paths.map(
|
|
4289
|
-
(p) => this.db.prepare(
|
|
4290
|
-
`UPDATE session_files SET status = 'clean', updated_at = ? WHERE session_id = ? AND path = ?`
|
|
4291
|
-
).bind(Date.now(), this.sessionId, p)
|
|
4292
|
-
);
|
|
4293
|
-
await this.db.batch(stmts);
|
|
4294
|
-
}
|
|
4295
|
-
async upsertFiles(files) {
|
|
4296
|
-
if (files.length === 0) return;
|
|
4297
|
-
const stmts = files.map(
|
|
4298
|
-
(f) => this.db.prepare(
|
|
4299
|
-
`INSERT OR REPLACE INTO session_files (session_id, path, status, content, updated_at)
|
|
4300
|
-
VALUES (?, ?, ?, ?, ?)`
|
|
4301
|
-
).bind(this.sessionId, f.path, f.status, f.content, Date.now())
|
|
4302
|
-
);
|
|
4303
|
-
await this.db.batch(stmts);
|
|
4304
|
-
}
|
|
4305
|
-
};
|
|
4306
4211
|
}
|
|
4307
4212
|
});
|
|
4308
4213
|
|
|
@@ -4329,9 +4234,9 @@ function createFileTools(store) {
|
|
|
4329
4234
|
readCache.clear();
|
|
4330
4235
|
listCache.clear();
|
|
4331
4236
|
}
|
|
4332
|
-
function invalidatePath(
|
|
4333
|
-
readCache.delete(
|
|
4334
|
-
const parent =
|
|
4237
|
+
function invalidatePath(path13) {
|
|
4238
|
+
readCache.delete(path13);
|
|
4239
|
+
const parent = path13.includes("/") ? path13.slice(0, path13.lastIndexOf("/") + 1) : "";
|
|
4335
4240
|
listCache.delete(parent);
|
|
4336
4241
|
listCache.delete("");
|
|
4337
4242
|
listCache.delete(".");
|
|
@@ -4339,36 +4244,36 @@ function createFileTools(store) {
|
|
|
4339
4244
|
}
|
|
4340
4245
|
const handlers = {
|
|
4341
4246
|
read_file: async (input) => {
|
|
4342
|
-
const
|
|
4343
|
-
const cached3 = readCache.get(
|
|
4247
|
+
const path13 = input.path;
|
|
4248
|
+
const cached3 = readCache.get(path13);
|
|
4344
4249
|
if (cached3 !== void 0) {
|
|
4345
4250
|
return { output: cached3 };
|
|
4346
4251
|
}
|
|
4347
|
-
const content = await store.readFile(
|
|
4252
|
+
const content = await store.readFile(path13);
|
|
4348
4253
|
if (content === null) {
|
|
4349
|
-
return { output: `File not found: ${
|
|
4254
|
+
return { output: `File not found: ${path13}`, is_error: true };
|
|
4350
4255
|
}
|
|
4351
|
-
readCache.set(
|
|
4256
|
+
readCache.set(path13, content);
|
|
4352
4257
|
return { output: content };
|
|
4353
4258
|
},
|
|
4354
4259
|
write_file: async (input) => {
|
|
4355
|
-
const
|
|
4260
|
+
const path13 = input.path;
|
|
4356
4261
|
const content = input.content;
|
|
4357
|
-
await store.writeFile(
|
|
4358
|
-
invalidatePath(
|
|
4359
|
-
readCache.set(
|
|
4360
|
-
return { output: `Written: ${
|
|
4262
|
+
await store.writeFile(path13, content);
|
|
4263
|
+
invalidatePath(path13);
|
|
4264
|
+
readCache.set(path13, content);
|
|
4265
|
+
return { output: `Written: ${path13}` };
|
|
4361
4266
|
},
|
|
4362
4267
|
edit_file: async (input) => {
|
|
4363
|
-
const
|
|
4268
|
+
const path13 = input.path;
|
|
4364
4269
|
const oldStr = input.old_string;
|
|
4365
4270
|
const newStr = input.new_string;
|
|
4366
|
-
let content = readCache.get(
|
|
4271
|
+
let content = readCache.get(path13);
|
|
4367
4272
|
if (content === void 0) {
|
|
4368
|
-
const stored = await store.readFile(
|
|
4273
|
+
const stored = await store.readFile(path13);
|
|
4369
4274
|
if (stored === null) {
|
|
4370
4275
|
return {
|
|
4371
|
-
output: `File not found: ${
|
|
4276
|
+
output: `File not found: ${path13}. Did you read the file first?`,
|
|
4372
4277
|
is_error: true
|
|
4373
4278
|
};
|
|
4374
4279
|
}
|
|
@@ -4378,41 +4283,41 @@ function createFileTools(store) {
|
|
|
4378
4283
|
if (lineNumbers.length === 0) {
|
|
4379
4284
|
const lineCount = content.split("\n").length;
|
|
4380
4285
|
return {
|
|
4381
|
-
output: `old_string not found in ${
|
|
4286
|
+
output: `old_string not found in ${path13}. The file has ${lineCount} lines. Did you read the file first?`,
|
|
4382
4287
|
is_error: true
|
|
4383
4288
|
};
|
|
4384
4289
|
}
|
|
4385
4290
|
if (lineNumbers.length > 1) {
|
|
4386
4291
|
const locations = lineNumbers.map((ln) => ` - line ${ln}`).join("\n");
|
|
4387
4292
|
return {
|
|
4388
|
-
output: `old_string matches ${lineNumbers.length} locations in ${
|
|
4293
|
+
output: `old_string matches ${lineNumbers.length} locations in ${path13}:
|
|
4389
4294
|
${locations}
|
|
4390
4295
|
Include more surrounding context to make old_string unique.`,
|
|
4391
4296
|
is_error: true
|
|
4392
4297
|
};
|
|
4393
4298
|
}
|
|
4394
|
-
const result = await store.editFile(
|
|
4299
|
+
const result = await store.editFile(path13, oldStr, newStr);
|
|
4395
4300
|
if (!result.success) {
|
|
4396
4301
|
return { output: result.error, is_error: true };
|
|
4397
4302
|
}
|
|
4398
|
-
invalidatePath(
|
|
4399
|
-
const updated = await store.readFile(
|
|
4400
|
-
if (updated !== null) readCache.set(
|
|
4401
|
-
return { output: `Edited: ${
|
|
4303
|
+
invalidatePath(path13);
|
|
4304
|
+
const updated = await store.readFile(path13);
|
|
4305
|
+
if (updated !== null) readCache.set(path13, updated);
|
|
4306
|
+
return { output: `Edited: ${path13}` };
|
|
4402
4307
|
},
|
|
4403
4308
|
list_directory: async (input) => {
|
|
4404
|
-
const
|
|
4405
|
-
const cached3 = listCache.get(
|
|
4309
|
+
const path13 = input.path;
|
|
4310
|
+
const cached3 = listCache.get(path13);
|
|
4406
4311
|
if (cached3 !== void 0) {
|
|
4407
4312
|
if (cached3.length === 0) {
|
|
4408
|
-
return { output: `Empty or not found: ${
|
|
4313
|
+
return { output: `Empty or not found: ${path13}` };
|
|
4409
4314
|
}
|
|
4410
4315
|
return { output: cached3.join("\n") };
|
|
4411
4316
|
}
|
|
4412
|
-
const entries = await store.listDirectory(
|
|
4413
|
-
listCache.set(
|
|
4317
|
+
const entries = await store.listDirectory(path13);
|
|
4318
|
+
listCache.set(path13, entries);
|
|
4414
4319
|
if (entries.length === 0) {
|
|
4415
|
-
return { output: `Empty or not found: ${
|
|
4320
|
+
return { output: `Empty or not found: ${path13}` };
|
|
4416
4321
|
}
|
|
4417
4322
|
return { output: entries.join("\n") };
|
|
4418
4323
|
}
|
|
@@ -4480,234 +4385,15 @@ var init_files = __esm({
|
|
|
4480
4385
|
});
|
|
4481
4386
|
|
|
4482
4387
|
// ../agent/src/storage/indexer.ts
|
|
4483
|
-
var
|
|
4388
|
+
var MAX_FILE_SIZE;
|
|
4484
4389
|
var init_indexer = __esm({
|
|
4485
4390
|
"../agent/src/storage/indexer.ts"() {
|
|
4486
4391
|
"use strict";
|
|
4487
|
-
CHUNK_SIZE = 500;
|
|
4488
|
-
CHUNK_OVERLAP = 100;
|
|
4489
|
-
BATCH_SIZE = 50;
|
|
4490
4392
|
MAX_FILE_SIZE = 100 * 1024;
|
|
4491
|
-
INDEXABLE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
4492
|
-
".ts",
|
|
4493
|
-
".js",
|
|
4494
|
-
".py",
|
|
4495
|
-
".go",
|
|
4496
|
-
".rs",
|
|
4497
|
-
".java",
|
|
4498
|
-
".tsx",
|
|
4499
|
-
".jsx",
|
|
4500
|
-
".md",
|
|
4501
|
-
".json",
|
|
4502
|
-
".yaml",
|
|
4503
|
-
".yml",
|
|
4504
|
-
".toml",
|
|
4505
|
-
".sql",
|
|
4506
|
-
".sh",
|
|
4507
|
-
".css",
|
|
4508
|
-
".html"
|
|
4509
|
-
]);
|
|
4510
|
-
RepoIndexer = class {
|
|
4511
|
-
config;
|
|
4512
|
-
model;
|
|
4513
|
-
constructor(config) {
|
|
4514
|
-
this.config = config;
|
|
4515
|
-
this.model = config.embeddingModel || "@cf/bge-base-en-v1.5";
|
|
4516
|
-
}
|
|
4517
|
-
/**
|
|
4518
|
-
* Index files into Vectorize. Skips if repo+commit is already indexed.
|
|
4519
|
-
* Returns number of chunks indexed.
|
|
4520
|
-
*/
|
|
4521
|
-
async indexFiles(files) {
|
|
4522
|
-
const existing = await this.config.db.prepare(
|
|
4523
|
-
"SELECT COUNT(*) as count FROM repo_files WHERE repo_id = ? AND commit_sha = ?"
|
|
4524
|
-
).bind(this.config.repoId, this.config.commitSha).first();
|
|
4525
|
-
if (existing && existing.count > 0) {
|
|
4526
|
-
return 0;
|
|
4527
|
-
}
|
|
4528
|
-
const chunks = files.flatMap((f) => this.chunkFile(f.path, f.content));
|
|
4529
|
-
if (chunks.length === 0) return 0;
|
|
4530
|
-
let totalIndexed = 0;
|
|
4531
|
-
for (let i = 0; i < chunks.length; i += BATCH_SIZE) {
|
|
4532
|
-
const batch = chunks.slice(i, i + BATCH_SIZE);
|
|
4533
|
-
const texts = batch.map((c) => c.content);
|
|
4534
|
-
const embedResult = await this.config.ai.run(this.model, { text: texts });
|
|
4535
|
-
const vectors = batch.map((chunk, j2) => ({
|
|
4536
|
-
id: chunk.id,
|
|
4537
|
-
values: embedResult.data[j2],
|
|
4538
|
-
metadata: {
|
|
4539
|
-
path: chunk.path,
|
|
4540
|
-
repo_id: this.config.repoId,
|
|
4541
|
-
start_line: String(chunk.startLine),
|
|
4542
|
-
end_line: String(chunk.endLine)
|
|
4543
|
-
}
|
|
4544
|
-
}));
|
|
4545
|
-
await this.config.vectorize.upsert(vectors);
|
|
4546
|
-
totalIndexed += vectors.length;
|
|
4547
|
-
}
|
|
4548
|
-
const stmts = files.map(
|
|
4549
|
-
(f) => this.config.db.prepare(
|
|
4550
|
-
`INSERT OR REPLACE INTO repo_files (repo_id, commit_sha, path, size, hash)
|
|
4551
|
-
VALUES (?, ?, ?, ?, ?)`
|
|
4552
|
-
).bind(
|
|
4553
|
-
this.config.repoId,
|
|
4554
|
-
this.config.commitSha,
|
|
4555
|
-
f.path,
|
|
4556
|
-
f.content.length,
|
|
4557
|
-
this.simpleHash(f.content)
|
|
4558
|
-
)
|
|
4559
|
-
);
|
|
4560
|
-
for (let i = 0; i < stmts.length; i += 100) {
|
|
4561
|
-
await this.config.db.batch(stmts.slice(i, i + 100));
|
|
4562
|
-
}
|
|
4563
|
-
return totalIndexed;
|
|
4564
|
-
}
|
|
4565
|
-
/**
|
|
4566
|
-
* Delta re-index: index only files that changed.
|
|
4567
|
-
*/
|
|
4568
|
-
async reindexFile(path12, content) {
|
|
4569
|
-
const prefix = `${this.config.repoId}:${path12}:`;
|
|
4570
|
-
const oldIds = Array.from({ length: 100 }, (_2, i) => `${prefix}${i}`);
|
|
4571
|
-
await this.config.vectorize.deleteByIds(oldIds);
|
|
4572
|
-
const chunks = this.chunkFile(path12, content);
|
|
4573
|
-
if (chunks.length === 0) return 0;
|
|
4574
|
-
const texts = chunks.map((c) => c.content);
|
|
4575
|
-
const embedResult = await this.config.ai.run(this.model, { text: texts });
|
|
4576
|
-
const vectors = chunks.map((chunk, j2) => ({
|
|
4577
|
-
id: chunk.id,
|
|
4578
|
-
values: embedResult.data[j2],
|
|
4579
|
-
metadata: {
|
|
4580
|
-
path: chunk.path,
|
|
4581
|
-
repo_id: this.config.repoId,
|
|
4582
|
-
start_line: String(chunk.startLine),
|
|
4583
|
-
end_line: String(chunk.endLine)
|
|
4584
|
-
}
|
|
4585
|
-
}));
|
|
4586
|
-
await this.config.vectorize.upsert(vectors);
|
|
4587
|
-
return vectors.length;
|
|
4588
|
-
}
|
|
4589
|
-
/**
|
|
4590
|
-
* Check if a repo+commit is already indexed.
|
|
4591
|
-
*/
|
|
4592
|
-
async isIndexed() {
|
|
4593
|
-
const result = await this.config.db.prepare(
|
|
4594
|
-
"SELECT COUNT(*) as count FROM repo_files WHERE repo_id = ? AND commit_sha = ?"
|
|
4595
|
-
).bind(this.config.repoId, this.config.commitSha).first();
|
|
4596
|
-
return (result?.count ?? 0) > 0;
|
|
4597
|
-
}
|
|
4598
|
-
/**
|
|
4599
|
-
* Read all files from a FileStore and index those with indexable extensions.
|
|
4600
|
-
* Skips files larger than 100KB.
|
|
4601
|
-
* Returns the number of chunks indexed.
|
|
4602
|
-
*/
|
|
4603
|
-
async indexFromStore(store, basePath = "/") {
|
|
4604
|
-
const storeDb = store.db;
|
|
4605
|
-
const sessionId = store.sessionId;
|
|
4606
|
-
const prefix = basePath === "/" ? "" : basePath;
|
|
4607
|
-
const allFiles = await storeDb.prepare(
|
|
4608
|
-
`SELECT path, content FROM session_files
|
|
4609
|
-
WHERE session_id = ? AND status != 'deleted' AND path LIKE ?`
|
|
4610
|
-
).bind(sessionId, `${prefix}%`).all();
|
|
4611
|
-
const files = [];
|
|
4612
|
-
for (const row of allFiles.results) {
|
|
4613
|
-
const dotIndex = row.path.lastIndexOf(".");
|
|
4614
|
-
if (dotIndex === -1) continue;
|
|
4615
|
-
const ext = row.path.slice(dotIndex);
|
|
4616
|
-
if (!INDEXABLE_EXTENSIONS.has(ext)) continue;
|
|
4617
|
-
if (row.content && row.content.length > MAX_FILE_SIZE) continue;
|
|
4618
|
-
if (!row.content) continue;
|
|
4619
|
-
files.push({ path: row.path, content: row.content });
|
|
4620
|
-
}
|
|
4621
|
-
if (files.length === 0) return 0;
|
|
4622
|
-
return this.indexFiles(files);
|
|
4623
|
-
}
|
|
4624
|
-
chunkFile(path12, content) {
|
|
4625
|
-
if (content.length === 0) return [];
|
|
4626
|
-
const chunks = [];
|
|
4627
|
-
let charPos = 0;
|
|
4628
|
-
let lineNum = 1;
|
|
4629
|
-
let chunkIndex = 0;
|
|
4630
|
-
while (charPos < content.length) {
|
|
4631
|
-
const chunkContent = content.slice(charPos, charPos + CHUNK_SIZE);
|
|
4632
|
-
const startLine = lineNum;
|
|
4633
|
-
const linesInChunk = chunkContent.split("\n").length - 1;
|
|
4634
|
-
const endLine = startLine + linesInChunk;
|
|
4635
|
-
chunks.push({
|
|
4636
|
-
id: `${this.config.repoId}:${path12}:${chunkIndex}`,
|
|
4637
|
-
path: path12,
|
|
4638
|
-
content: chunkContent,
|
|
4639
|
-
startLine,
|
|
4640
|
-
endLine
|
|
4641
|
-
});
|
|
4642
|
-
chunkIndex++;
|
|
4643
|
-
const advance = CHUNK_SIZE - CHUNK_OVERLAP;
|
|
4644
|
-
charPos += advance;
|
|
4645
|
-
const advancedContent = content.slice(charPos - advance, charPos);
|
|
4646
|
-
lineNum += (advancedContent.match(/\n/g) || []).length;
|
|
4647
|
-
}
|
|
4648
|
-
return chunks;
|
|
4649
|
-
}
|
|
4650
|
-
simpleHash(content) {
|
|
4651
|
-
let hash = 0;
|
|
4652
|
-
for (let i = 0; i < content.length; i++) {
|
|
4653
|
-
const char = content.charCodeAt(i);
|
|
4654
|
-
hash = (hash << 5) - hash + char;
|
|
4655
|
-
hash |= 0;
|
|
4656
|
-
}
|
|
4657
|
-
return hash.toString(16);
|
|
4658
|
-
}
|
|
4659
|
-
};
|
|
4660
4393
|
}
|
|
4661
4394
|
});
|
|
4662
4395
|
|
|
4663
4396
|
// ../agent/src/tools/search.ts
|
|
4664
|
-
function createSearchTool(config) {
|
|
4665
|
-
const model = config.embeddingModel || "@cf/bge-base-en-v1.5";
|
|
4666
|
-
return async (input) => {
|
|
4667
|
-
const query = input.query;
|
|
4668
|
-
const limit = input.limit || 10;
|
|
4669
|
-
if (!query || query.trim().length === 0) {
|
|
4670
|
-
return { output: "Search query cannot be empty", is_error: true };
|
|
4671
|
-
}
|
|
4672
|
-
try {
|
|
4673
|
-
let embedResult;
|
|
4674
|
-
try {
|
|
4675
|
-
embedResult = await config.ai.run(model, { text: [query] });
|
|
4676
|
-
} catch {
|
|
4677
|
-
return { output: `Semantic search is not available. Use the bash tool with grep to search the codebase instead.
|
|
4678
|
-
Example: bash -c "grep -rn 'pattern' ."` };
|
|
4679
|
-
}
|
|
4680
|
-
const queryVector = embedResult.data[0];
|
|
4681
|
-
const results = await config.vectorize.query(queryVector, {
|
|
4682
|
-
topK: limit,
|
|
4683
|
-
filter: { repo_id: config.repoId },
|
|
4684
|
-
returnMetadata: "all"
|
|
4685
|
-
});
|
|
4686
|
-
if (results.matches.length === 0) {
|
|
4687
|
-
return { output: "No results found." };
|
|
4688
|
-
}
|
|
4689
|
-
const byFile = /* @__PURE__ */ new Map();
|
|
4690
|
-
for (const match of results.matches) {
|
|
4691
|
-
const path12 = match.metadata?.path ?? "unknown";
|
|
4692
|
-
if (!byFile.has(path12)) byFile.set(path12, []);
|
|
4693
|
-
byFile.get(path12).push({
|
|
4694
|
-
score: match.score,
|
|
4695
|
-
startLine: match.metadata?.start_line ?? "?",
|
|
4696
|
-
endLine: match.metadata?.end_line ?? "?"
|
|
4697
|
-
});
|
|
4698
|
-
}
|
|
4699
|
-
const lines2 = [];
|
|
4700
|
-
for (const [path12, matches] of byFile) {
|
|
4701
|
-
const bestScore = Math.max(...matches.map((m2) => m2.score));
|
|
4702
|
-
const lineRanges = matches.sort((a, b2) => Number(a.startLine) - Number(b2.startLine)).map((m2) => `L${m2.startLine}-${m2.endLine}`).join(", ");
|
|
4703
|
-
lines2.push(`${path12} (score: ${bestScore.toFixed(3)}) \u2014 ${lineRanges}`);
|
|
4704
|
-
}
|
|
4705
|
-
return { output: lines2.join("\n") };
|
|
4706
|
-
} catch (err) {
|
|
4707
|
-
return { output: `Search failed: ${err instanceof Error ? err.message : String(err)}`, is_error: true };
|
|
4708
|
-
}
|
|
4709
|
-
};
|
|
4710
|
-
}
|
|
4711
4397
|
function createHostSearchTool(exec2) {
|
|
4712
4398
|
return async (input) => {
|
|
4713
4399
|
const query = input.query;
|
|
@@ -4748,126 +4434,9 @@ var init_search = __esm({
|
|
|
4748
4434
|
});
|
|
4749
4435
|
|
|
4750
4436
|
// ../agent/src/tools/sentry.ts
|
|
4751
|
-
function formatIssue(issue) {
|
|
4752
|
-
return `[${issue.shortId}] ${issue.title}
|
|
4753
|
-
Level: ${issue.level} | Count: ${issue.count} | Last seen: ${issue.lastSeen}`;
|
|
4754
|
-
}
|
|
4755
|
-
function formatIssueDetail(issue) {
|
|
4756
|
-
const lines2 = [];
|
|
4757
|
-
lines2.push(`Issue: ${issue.shortId} (${issue.id})`);
|
|
4758
|
-
lines2.push(`Title: ${issue.title}`);
|
|
4759
|
-
lines2.push(`Culprit: ${issue.culprit}`);
|
|
4760
|
-
lines2.push(`Level: ${issue.level} | Count: ${issue.count}`);
|
|
4761
|
-
lines2.push(`First seen: ${issue.firstSeen}`);
|
|
4762
|
-
lines2.push(`Last seen: ${issue.lastSeen}`);
|
|
4763
|
-
if (issue.metadata.type || issue.metadata.value) {
|
|
4764
|
-
lines2.push(`
|
|
4765
|
-
Error: ${issue.metadata.type ?? ""}: ${issue.metadata.value ?? ""}`);
|
|
4766
|
-
}
|
|
4767
|
-
if (issue.tags.length > 0) {
|
|
4768
|
-
lines2.push(`
|
|
4769
|
-
Tags:`);
|
|
4770
|
-
for (const tag of issue.tags) {
|
|
4771
|
-
lines2.push(` ${tag.key}: ${tag.value}`);
|
|
4772
|
-
}
|
|
4773
|
-
}
|
|
4774
|
-
if (issue.stackTrace && issue.stackTrace.length > 0) {
|
|
4775
|
-
lines2.push(`
|
|
4776
|
-
Stack trace:`);
|
|
4777
|
-
for (const frame of issue.stackTrace) {
|
|
4778
|
-
lines2.push(` ${frame.filename}:${frame.lineNo} in ${frame.function}`);
|
|
4779
|
-
if (frame.context.length > 0) {
|
|
4780
|
-
for (const [lineNo, code] of frame.context) {
|
|
4781
|
-
const marker = lineNo === frame.lineNo ? ">" : " ";
|
|
4782
|
-
lines2.push(` ${marker} ${lineNo}: ${code}`);
|
|
4783
|
-
}
|
|
4784
|
-
}
|
|
4785
|
-
}
|
|
4786
|
-
}
|
|
4787
|
-
return lines2.join("\n");
|
|
4788
|
-
}
|
|
4789
|
-
function createSentryTools(fetchFn) {
|
|
4790
|
-
const handlers = {
|
|
4791
|
-
sentry_list_issues: async (input) => {
|
|
4792
|
-
const project = input.project;
|
|
4793
|
-
const query = input.query;
|
|
4794
|
-
const params = new URLSearchParams();
|
|
4795
|
-
if (project) params.set("project", project);
|
|
4796
|
-
if (query) params.set("query", query);
|
|
4797
|
-
const qs = params.toString();
|
|
4798
|
-
const path12 = `/api/sentry/issues${qs ? `?${qs}` : ""}`;
|
|
4799
|
-
try {
|
|
4800
|
-
const res = await fetchFn(path12);
|
|
4801
|
-
if (!res.ok) {
|
|
4802
|
-
const text = await res.text();
|
|
4803
|
-
return { output: `Sentry API error (${res.status}): ${text}`, is_error: true };
|
|
4804
|
-
}
|
|
4805
|
-
const issues = await res.json();
|
|
4806
|
-
if (issues.length === 0) {
|
|
4807
|
-
return { output: "No unresolved issues found." };
|
|
4808
|
-
}
|
|
4809
|
-
return { output: issues.map(formatIssue).join("\n\n") };
|
|
4810
|
-
} catch (err) {
|
|
4811
|
-
return { output: `Failed to list Sentry issues: ${err.message}`, is_error: true };
|
|
4812
|
-
}
|
|
4813
|
-
},
|
|
4814
|
-
sentry_get_issue: async (input) => {
|
|
4815
|
-
const issueId = input.issue_id;
|
|
4816
|
-
if (!issueId) {
|
|
4817
|
-
return { output: "Missing required parameter: issue_id", is_error: true };
|
|
4818
|
-
}
|
|
4819
|
-
try {
|
|
4820
|
-
const res = await fetchFn(`/api/sentry/issues/${encodeURIComponent(issueId)}`);
|
|
4821
|
-
if (!res.ok) {
|
|
4822
|
-
const text = await res.text();
|
|
4823
|
-
return { output: `Sentry API error (${res.status}): ${text}`, is_error: true };
|
|
4824
|
-
}
|
|
4825
|
-
const issue = await res.json();
|
|
4826
|
-
return { output: formatIssueDetail(issue) };
|
|
4827
|
-
} catch (err) {
|
|
4828
|
-
return { output: `Failed to get Sentry issue: ${err.message}`, is_error: true };
|
|
4829
|
-
}
|
|
4830
|
-
}
|
|
4831
|
-
};
|
|
4832
|
-
return { handlers };
|
|
4833
|
-
}
|
|
4834
|
-
var SENTRY_LIST_ISSUES_TOOL, SENTRY_GET_ISSUE_TOOL, SENTRY_TOOL_DEFINITIONS;
|
|
4835
4437
|
var init_sentry = __esm({
|
|
4836
4438
|
"../agent/src/tools/sentry.ts"() {
|
|
4837
4439
|
"use strict";
|
|
4838
|
-
SENTRY_LIST_ISSUES_TOOL = {
|
|
4839
|
-
name: "sentry_list_issues",
|
|
4840
|
-
description: "List unresolved Sentry issues. Optionally filter by project slug or search query.",
|
|
4841
|
-
input_schema: {
|
|
4842
|
-
type: "object",
|
|
4843
|
-
properties: {
|
|
4844
|
-
project: {
|
|
4845
|
-
type: "string",
|
|
4846
|
-
description: "Sentry project slug to filter by (optional)"
|
|
4847
|
-
},
|
|
4848
|
-
query: {
|
|
4849
|
-
type: "string",
|
|
4850
|
-
description: "Search query to filter issues (optional)"
|
|
4851
|
-
}
|
|
4852
|
-
},
|
|
4853
|
-
required: []
|
|
4854
|
-
}
|
|
4855
|
-
};
|
|
4856
|
-
SENTRY_GET_ISSUE_TOOL = {
|
|
4857
|
-
name: "sentry_get_issue",
|
|
4858
|
-
description: "Get detailed information about a Sentry issue including its stack trace.",
|
|
4859
|
-
input_schema: {
|
|
4860
|
-
type: "object",
|
|
4861
|
-
properties: {
|
|
4862
|
-
issue_id: {
|
|
4863
|
-
type: "string",
|
|
4864
|
-
description: "The Sentry issue ID"
|
|
4865
|
-
}
|
|
4866
|
-
},
|
|
4867
|
-
required: ["issue_id"]
|
|
4868
|
-
}
|
|
4869
|
-
};
|
|
4870
|
-
SENTRY_TOOL_DEFINITIONS = [SENTRY_LIST_ISSUES_TOOL, SENTRY_GET_ISSUE_TOOL];
|
|
4871
4440
|
}
|
|
4872
4441
|
});
|
|
4873
4442
|
|
|
@@ -4966,83 +4535,9 @@ var init_github = __esm({
|
|
|
4966
4535
|
});
|
|
4967
4536
|
|
|
4968
4537
|
// ../agent/src/tools/preview.ts
|
|
4969
|
-
function formatResult(body) {
|
|
4970
|
-
switch (body.status) {
|
|
4971
|
-
case "ready":
|
|
4972
|
-
return { output: `Preview ready: ${body.url}
|
|
4973
|
-
Framework: ${body.framework ?? "unknown"}` };
|
|
4974
|
-
case "starting":
|
|
4975
|
-
return {
|
|
4976
|
-
output: `Preview server is starting (still compiling). URL will be: ${body.url ?? "unknown"}
|
|
4977
|
-
Check again in a few seconds by calling this tool again.`
|
|
4978
|
-
};
|
|
4979
|
-
case "crashed":
|
|
4980
|
-
return {
|
|
4981
|
-
output: `Preview server crashed.${body.url ? ` URL was: ${body.url}` : ""}${body.exitCode !== void 0 ? ` Exit code: ${body.exitCode}` : ""}${body.logs ? `
|
|
4982
|
-
Logs:
|
|
4983
|
-
${body.logs}` : ""}${body.error ? `
|
|
4984
|
-
${body.error}` : ""}`,
|
|
4985
|
-
is_error: true
|
|
4986
|
-
};
|
|
4987
|
-
default:
|
|
4988
|
-
return { output: body.error ?? "Unknown preview status", is_error: true };
|
|
4989
|
-
}
|
|
4990
|
-
}
|
|
4991
|
-
function createPreviewTool(fetchFn) {
|
|
4992
|
-
return async (_input) => {
|
|
4993
|
-
let startBody;
|
|
4994
|
-
try {
|
|
4995
|
-
const startRes = await fetchFn("/preview", { method: "POST" });
|
|
4996
|
-
startBody = await startRes.json();
|
|
4997
|
-
if (!startRes.ok) {
|
|
4998
|
-
return { output: startBody.error ?? `Failed to start preview (HTTP ${startRes.status})`, is_error: true };
|
|
4999
|
-
}
|
|
5000
|
-
} catch (err) {
|
|
5001
|
-
return { output: `Failed to start preview: ${err.message}`, is_error: true };
|
|
5002
|
-
}
|
|
5003
|
-
if (startBody.status !== "starting") {
|
|
5004
|
-
return formatResult(startBody);
|
|
5005
|
-
}
|
|
5006
|
-
let lastPollError;
|
|
5007
|
-
for (let attempt = 0; attempt < POLL_ATTEMPTS; attempt++) {
|
|
5008
|
-
await new Promise((resolve2) => setTimeout(resolve2, POLL_DELAY_MS));
|
|
5009
|
-
try {
|
|
5010
|
-
const statusRes = await fetchFn("/preview", { method: "GET" });
|
|
5011
|
-
const statusBody = await statusRes.json();
|
|
5012
|
-
lastPollError = void 0;
|
|
5013
|
-
if (statusBody.status === "ready" || statusBody.status === "crashed") {
|
|
5014
|
-
return formatResult(statusBody);
|
|
5015
|
-
}
|
|
5016
|
-
} catch (err) {
|
|
5017
|
-
lastPollError = err instanceof Error ? err.message : String(err);
|
|
5018
|
-
console.error("Preview status poll failed, retrying", { error: lastPollError });
|
|
5019
|
-
}
|
|
5020
|
-
}
|
|
5021
|
-
if (lastPollError) {
|
|
5022
|
-
return {
|
|
5023
|
-
output: `Could not confirm preview status after ${POLL_ATTEMPTS} attempts (last error: ${lastPollError}). The server may still be starting, or status checks may be failing \u2014 try calling this tool again.`,
|
|
5024
|
-
is_error: true
|
|
5025
|
-
};
|
|
5026
|
-
}
|
|
5027
|
-
return formatResult(startBody);
|
|
5028
|
-
};
|
|
5029
|
-
}
|
|
5030
|
-
var PREVIEW_TOOL, PREVIEW_TOOL_DEFINITIONS, POLL_ATTEMPTS, POLL_DELAY_MS;
|
|
5031
4538
|
var init_preview = __esm({
|
|
5032
4539
|
"../agent/src/tools/preview.ts"() {
|
|
5033
4540
|
"use strict";
|
|
5034
|
-
PREVIEW_TOOL = {
|
|
5035
|
-
name: "preview",
|
|
5036
|
-
description: 'Start or check the status of the front-end dev server preview for this workspace and get a live URL to view the current state of the app. Call this after making a front-end change to verify it visually. The result distinguishes "starting" (server launching, still compiling) from "ready" (server is up, safe to view) from "crashed" (server process died \u2014 check logs in the result).',
|
|
5037
|
-
input_schema: {
|
|
5038
|
-
type: "object",
|
|
5039
|
-
properties: {},
|
|
5040
|
-
required: []
|
|
5041
|
-
}
|
|
5042
|
-
};
|
|
5043
|
-
PREVIEW_TOOL_DEFINITIONS = [PREVIEW_TOOL];
|
|
5044
|
-
POLL_ATTEMPTS = 3;
|
|
5045
|
-
POLL_DELAY_MS = 1500;
|
|
5046
4541
|
}
|
|
5047
4542
|
});
|
|
5048
4543
|
|
|
@@ -5109,414 +4604,23 @@ var init_todo = __esm({
|
|
|
5109
4604
|
});
|
|
5110
4605
|
|
|
5111
4606
|
// ../agent/src/tools/codegen.ts
|
|
5112
|
-
async function detectStackType(fileStore) {
|
|
5113
|
-
if (await fileStore.readFile("wrangler.toml") !== null || await fileStore.readFile("wrangler.jsonc") !== null) return "worker";
|
|
5114
|
-
if (await fileStore.readFile("app.json") !== null || await fileStore.readFile("expo.json") !== null) return "mobile";
|
|
5115
|
-
if (await fileStore.readFile("next.config.js") !== null || await fileStore.readFile("next.config.ts") !== null || await fileStore.readFile("next.config.mjs") !== null) return "pages";
|
|
5116
|
-
return "worker";
|
|
5117
|
-
}
|
|
5118
|
-
async function cancelBuild(baseUrl, buildId, authHeader) {
|
|
5119
|
-
try {
|
|
5120
|
-
const res = await fetch(`${baseUrl}/builds/${buildId}/cancel`, {
|
|
5121
|
-
method: "POST",
|
|
5122
|
-
headers: { "Authorization": authHeader }
|
|
5123
|
-
});
|
|
5124
|
-
if (!res.ok) console.error(`[codegen] cancel of build ${buildId} failed: HTTP ${res.status}`);
|
|
5125
|
-
} catch (err) {
|
|
5126
|
-
console.error(`[codegen] cancel of build ${buildId} failed:`, err);
|
|
5127
|
-
}
|
|
5128
|
-
}
|
|
5129
|
-
function createCodegenTools(fileStore, config) {
|
|
5130
|
-
const handlers = {
|
|
5131
|
-
build_feature: async (input, onOutput) => {
|
|
5132
|
-
const title = input.title;
|
|
5133
|
-
const requirements = input.requirements;
|
|
5134
|
-
const context = input.context;
|
|
5135
|
-
const stackTypeOverride = input.stackType;
|
|
5136
|
-
if (!title || !requirements) {
|
|
5137
|
-
return { output: "Missing required parameters: title and requirements", is_error: true };
|
|
5138
|
-
}
|
|
5139
|
-
const stackType = stackTypeOverride ?? await detectStackType(fileStore);
|
|
5140
|
-
const toolId = crypto.randomUUID();
|
|
5141
|
-
if (config.jwtToken) {
|
|
5142
|
-
const baseUrl = "https://api.codemindhq.dev";
|
|
5143
|
-
const authHeader = `Bearer ${config.jwtToken}`;
|
|
5144
|
-
onOutput?.(toolId, "stdout", `[codegen] Submitting build...
|
|
5145
|
-
`);
|
|
5146
|
-
const submitRes = await fetch(`${baseUrl}/builds`, {
|
|
5147
|
-
method: "POST",
|
|
5148
|
-
headers: { "Content-Type": "application/json", "Authorization": authHeader },
|
|
5149
|
-
body: JSON.stringify({
|
|
5150
|
-
product: config.product ?? "spacecode",
|
|
5151
|
-
repoUrl: config.repoUrl,
|
|
5152
|
-
storyTitle: title,
|
|
5153
|
-
acceptanceCriteria: requirements,
|
|
5154
|
-
architectureContext: context ?? "",
|
|
5155
|
-
stackType,
|
|
5156
|
-
skipCommit: true
|
|
5157
|
-
})
|
|
5158
|
-
});
|
|
5159
|
-
if (!submitRes.ok) {
|
|
5160
|
-
const err = await submitRes.text().catch(() => "unknown");
|
|
5161
|
-
throw new Error(`Codegen submit error: ${submitRes.status} ${err}`);
|
|
5162
|
-
}
|
|
5163
|
-
const { id: buildId2 } = await submitRes.json();
|
|
5164
|
-
onOutput?.(toolId, "stdout", `[codegen] Build ${buildId2} queued, waiting...
|
|
5165
|
-
`);
|
|
5166
|
-
const baseDelay = CODEGEN_POLL_INTERVAL_MS;
|
|
5167
|
-
let status = "queued";
|
|
5168
|
-
let consecutiveFailures = 0;
|
|
5169
|
-
for (let i = 0; i < CODEGEN_POLL_MAX_ATTEMPTS && status !== "completed"; i++) {
|
|
5170
|
-
await new Promise((r) => setTimeout(r, baseDelay));
|
|
5171
|
-
const poll = await fetch(`${baseUrl}/builds/${buildId2}`, {
|
|
5172
|
-
headers: { "Authorization": authHeader }
|
|
5173
|
-
});
|
|
5174
|
-
if (!poll.ok) {
|
|
5175
|
-
consecutiveFailures++;
|
|
5176
|
-
if (consecutiveFailures >= MAX_CONSECUTIVE_POLL_FAILURES) {
|
|
5177
|
-
await cancelBuild(baseUrl, buildId2, authHeader);
|
|
5178
|
-
throw new Error(`Codegen build ${buildId2}: polling failed ${consecutiveFailures} times in a row (last HTTP ${poll.status}); build cancelled`);
|
|
5179
|
-
}
|
|
5180
|
-
continue;
|
|
5181
|
-
}
|
|
5182
|
-
consecutiveFailures = 0;
|
|
5183
|
-
const b2 = await poll.json();
|
|
5184
|
-
status = b2.status;
|
|
5185
|
-
if (b2.error) throw new Error(`Codegen failed: ${b2.error}`);
|
|
5186
|
-
if (TERMINAL_FAILURE_STATUSES.has(status)) {
|
|
5187
|
-
throw new Error(`Build ${buildId2} ended with status: ${status}`);
|
|
5188
|
-
}
|
|
5189
|
-
}
|
|
5190
|
-
if (status !== "completed") {
|
|
5191
|
-
await cancelBuild(baseUrl, buildId2, authHeader);
|
|
5192
|
-
throw new Error(`Build ${buildId2} did not finish within ${CODEGEN_POLL_MAX_ATTEMPTS * baseDelay / 1e3}s (last status: ${status}); cancelled`);
|
|
5193
|
-
}
|
|
5194
|
-
const filesRes = await fetch(`${baseUrl}/builds/${buildId2}/files`, {
|
|
5195
|
-
headers: { "Authorization": authHeader }
|
|
5196
|
-
});
|
|
5197
|
-
if (!filesRes.ok) throw new Error(`Failed to fetch files: ${filesRes.status}`);
|
|
5198
|
-
const { files: files2 } = await filesRes.json();
|
|
5199
|
-
if (!files2?.length) throw new Error("Build completed but no files generated");
|
|
5200
|
-
const written2 = [];
|
|
5201
|
-
for (const file of files2) {
|
|
5202
|
-
await fileStore.writeFile(file.path, file.content);
|
|
5203
|
-
written2.push(file.path);
|
|
5204
|
-
}
|
|
5205
|
-
onOutput?.(toolId, "stdout", `[codegen] ${files2.length} files written
|
|
5206
|
-
`);
|
|
5207
|
-
return {
|
|
5208
|
-
output: [
|
|
5209
|
-
`Generated ${files2.length} files for: ${title}`,
|
|
5210
|
-
"",
|
|
5211
|
-
"Files written to workspace:",
|
|
5212
|
-
...written2.map((p) => ` ${p}`),
|
|
5213
|
-
"",
|
|
5214
|
-
"Run tests to verify, then commit when ready."
|
|
5215
|
-
].join("\n")
|
|
5216
|
-
};
|
|
5217
|
-
}
|
|
5218
|
-
if (!config.codegenUrl) {
|
|
5219
|
-
throw new Error("No codegen backend configured (set jwtToken or codegenUrl)");
|
|
5220
|
-
}
|
|
5221
|
-
const res = await fetch(`${config.codegenUrl}/builds?mode=interactive`, {
|
|
5222
|
-
method: "POST",
|
|
5223
|
-
headers: {
|
|
5224
|
-
"Content-Type": "application/json",
|
|
5225
|
-
"Authorization": `Bearer ${config.apiKey}`,
|
|
5226
|
-
"x-tenant-id": config.tenantId ?? ""
|
|
5227
|
-
},
|
|
5228
|
-
body: JSON.stringify({
|
|
5229
|
-
product: config.product,
|
|
5230
|
-
repoUrl: config.repoUrl,
|
|
5231
|
-
storyTitle: title,
|
|
5232
|
-
acceptanceCriteria: requirements,
|
|
5233
|
-
architectureContext: context ?? "",
|
|
5234
|
-
stackType,
|
|
5235
|
-
skipCommit: true
|
|
5236
|
-
})
|
|
5237
|
-
});
|
|
5238
|
-
if (!res.ok) {
|
|
5239
|
-
const err = await res.text().catch(() => "unknown");
|
|
5240
|
-
throw new Error(`Codegen service error: ${res.status} ${err}`);
|
|
5241
|
-
}
|
|
5242
|
-
const reader = res.body?.getReader();
|
|
5243
|
-
if (!reader) throw new Error("No response body");
|
|
5244
|
-
const decoder = new TextDecoder();
|
|
5245
|
-
let buildId = "";
|
|
5246
|
-
let summary = "";
|
|
5247
|
-
let buffer = "";
|
|
5248
|
-
let currentEvent = "";
|
|
5249
|
-
try {
|
|
5250
|
-
while (true) {
|
|
5251
|
-
const { done, value } = await reader.read();
|
|
5252
|
-
if (done) break;
|
|
5253
|
-
buffer += decoder.decode(value, { stream: true });
|
|
5254
|
-
const lines2 = buffer.split("\n");
|
|
5255
|
-
buffer = lines2.pop() ?? "";
|
|
5256
|
-
for (const line of lines2) {
|
|
5257
|
-
if (line.startsWith("event: ")) {
|
|
5258
|
-
currentEvent = line.slice(7).trim();
|
|
5259
|
-
continue;
|
|
5260
|
-
}
|
|
5261
|
-
if (!line.startsWith("data: ")) continue;
|
|
5262
|
-
let data;
|
|
5263
|
-
try {
|
|
5264
|
-
data = JSON.parse(line.slice(6));
|
|
5265
|
-
} catch (parseErr) {
|
|
5266
|
-
throw new Error(`Codegen stream sent malformed event data: ${parseErr instanceof Error ? parseErr.message : String(parseErr)}`);
|
|
5267
|
-
}
|
|
5268
|
-
if (currentEvent === "phase" && onOutput) {
|
|
5269
|
-
const count = data.componentCount ? ` (${data.componentCount} components)` : "";
|
|
5270
|
-
onOutput(toolId, "stdout", `[codegen] ${data.phase}: ${data.status}${count}
|
|
5271
|
-
`);
|
|
5272
|
-
}
|
|
5273
|
-
if (currentEvent === "component" && onOutput) {
|
|
5274
|
-
const status = data.fromLibrary ? "reused from library" : data.status;
|
|
5275
|
-
onOutput(toolId, "stdout", `[codegen] ${data.name}: ${status}
|
|
5276
|
-
`);
|
|
5277
|
-
}
|
|
5278
|
-
if (currentEvent === "error") {
|
|
5279
|
-
throw new Error(`Codegen error: ${data.message}`);
|
|
5280
|
-
}
|
|
5281
|
-
if (currentEvent === "complete") {
|
|
5282
|
-
buildId = data.buildId;
|
|
5283
|
-
summary = `Generated ${data.filesGenerated ?? 0} files (${data.componentsReused ?? 0} reused)`;
|
|
5284
|
-
}
|
|
5285
|
-
currentEvent = "";
|
|
5286
|
-
}
|
|
5287
|
-
}
|
|
5288
|
-
} finally {
|
|
5289
|
-
reader.releaseLock();
|
|
5290
|
-
}
|
|
5291
|
-
if (!buildId) {
|
|
5292
|
-
throw new Error("Codegen stream ended without completion");
|
|
5293
|
-
}
|
|
5294
|
-
const buildRes = await fetch(`${config.codegenUrl}/builds/${buildId}`, {
|
|
5295
|
-
headers: {
|
|
5296
|
-
"Authorization": `Bearer ${config.apiKey}`,
|
|
5297
|
-
"x-tenant-id": config.tenantId ?? ""
|
|
5298
|
-
}
|
|
5299
|
-
});
|
|
5300
|
-
if (!buildRes.ok) {
|
|
5301
|
-
throw new Error(`Failed to fetch build result: ${buildRes.status}`);
|
|
5302
|
-
}
|
|
5303
|
-
const build2 = await buildRes.json();
|
|
5304
|
-
if (!build2.result) {
|
|
5305
|
-
throw new Error("Build completed but no files in result");
|
|
5306
|
-
}
|
|
5307
|
-
let files;
|
|
5308
|
-
try {
|
|
5309
|
-
const parsed = JSON.parse(build2.result);
|
|
5310
|
-
files = parsed.files ?? [];
|
|
5311
|
-
} catch {
|
|
5312
|
-
throw new Error("Failed to parse build result");
|
|
5313
|
-
}
|
|
5314
|
-
if (files.length === 0) {
|
|
5315
|
-
throw new Error("Codegen produced no files");
|
|
5316
|
-
}
|
|
5317
|
-
const written = [];
|
|
5318
|
-
for (const file of files) {
|
|
5319
|
-
await fileStore.writeFile(file.path, file.content);
|
|
5320
|
-
written.push(file.path);
|
|
5321
|
-
}
|
|
5322
|
-
return {
|
|
5323
|
-
output: [
|
|
5324
|
-
summary,
|
|
5325
|
-
"",
|
|
5326
|
-
"Files written to workspace:",
|
|
5327
|
-
...written.map((p) => ` ${p}`),
|
|
5328
|
-
"",
|
|
5329
|
-
"Run tests to verify, then commit when ready."
|
|
5330
|
-
].join("\n")
|
|
5331
|
-
};
|
|
5332
|
-
}
|
|
5333
|
-
};
|
|
5334
|
-
return { handlers };
|
|
5335
|
-
}
|
|
5336
|
-
var BUILD_FEATURE_TOOL, CODEGEN_TOOL_DEFINITIONS, CODEGEN_POLL_INTERVAL_MS, CODEGEN_POLL_MAX_ATTEMPTS, MAX_CONSECUTIVE_POLL_FAILURES, TERMINAL_FAILURE_STATUSES;
|
|
5337
4607
|
var init_codegen = __esm({
|
|
5338
4608
|
"../agent/src/tools/codegen.ts"() {
|
|
5339
4609
|
"use strict";
|
|
5340
|
-
BUILD_FEATURE_TOOL = {
|
|
5341
|
-
name: "build_feature",
|
|
5342
|
-
description: "Generate a complete feature using the platform codegen service. Produces multiple files with tests, leveraging a library of reusable components. Use this for multi-file features, new modules, or significant refactors. Files are written to the workspace \u2014 run tests afterwards to verify. Prefer this over manually writing multiple files.",
|
|
5343
|
-
input_schema: {
|
|
5344
|
-
type: "object",
|
|
5345
|
-
properties: {
|
|
5346
|
-
title: {
|
|
5347
|
-
type: "string",
|
|
5348
|
-
description: 'Feature title (e.g., "Add user authentication with sessions and API keys")'
|
|
5349
|
-
},
|
|
5350
|
-
requirements: {
|
|
5351
|
-
type: "string",
|
|
5352
|
-
description: "Acceptance criteria or detailed requirements for the feature"
|
|
5353
|
-
},
|
|
5354
|
-
context: {
|
|
5355
|
-
type: "string",
|
|
5356
|
-
description: "Architecture context \u2014 existing code patterns, tech stack, conventions."
|
|
5357
|
-
},
|
|
5358
|
-
stackType: {
|
|
5359
|
-
type: "string",
|
|
5360
|
-
enum: ["worker", "mobile", "pages"],
|
|
5361
|
-
description: "Override auto-detected stack type. Usually not needed."
|
|
5362
|
-
}
|
|
5363
|
-
},
|
|
5364
|
-
required: ["title", "requirements"]
|
|
5365
|
-
}
|
|
5366
|
-
};
|
|
5367
|
-
CODEGEN_TOOL_DEFINITIONS = [BUILD_FEATURE_TOOL];
|
|
5368
|
-
CODEGEN_POLL_INTERVAL_MS = 5e3;
|
|
5369
|
-
CODEGEN_POLL_MAX_ATTEMPTS = 120;
|
|
5370
|
-
MAX_CONSECUTIVE_POLL_FAILURES = 5;
|
|
5371
|
-
TERMINAL_FAILURE_STATUSES = /* @__PURE__ */ new Set(["failed", "cancelled", "canceled", "error"]);
|
|
5372
4610
|
}
|
|
5373
4611
|
});
|
|
5374
4612
|
|
|
5375
4613
|
// ../agent/src/sandbox/snapshot-manager.ts
|
|
5376
|
-
var SnapshotManager;
|
|
5377
4614
|
var init_snapshot_manager = __esm({
|
|
5378
4615
|
"../agent/src/sandbox/snapshot-manager.ts"() {
|
|
5379
4616
|
"use strict";
|
|
5380
|
-
SnapshotManager = class {
|
|
5381
|
-
constructor(config) {
|
|
5382
|
-
this.config = config;
|
|
5383
|
-
}
|
|
5384
|
-
/**
|
|
5385
|
-
* Get or create a container for the given repo+lockfile.
|
|
5386
|
-
* If a snapshot exists, restore from it (~1-3s).
|
|
5387
|
-
* If not, create fresh container (~30-60s), then snapshot it for next time.
|
|
5388
|
-
*/
|
|
5389
|
-
async getOrCreateContainer(repoId, lockfileHash, setupFn) {
|
|
5390
|
-
const existing = await this.config.db.prepare(
|
|
5391
|
-
"SELECT backup_handle, image_version FROM repo_snapshots WHERE repo_id = ? AND lockfile_hash = ?"
|
|
5392
|
-
).bind(repoId, lockfileHash).first();
|
|
5393
|
-
if (existing && existing.image_version === this.config.imageVersion) {
|
|
5394
|
-
const sandbox2 = await this.config.sandboxNs.restore(existing.backup_handle);
|
|
5395
|
-
await this.config.db.prepare(
|
|
5396
|
-
"UPDATE repo_snapshots SET last_used_at = ? WHERE repo_id = ? AND lockfile_hash = ?"
|
|
5397
|
-
).bind(Date.now(), repoId, lockfileHash).run();
|
|
5398
|
-
return sandbox2;
|
|
5399
|
-
}
|
|
5400
|
-
const sandbox = await this.config.sandboxNs.create();
|
|
5401
|
-
if (setupFn) {
|
|
5402
|
-
await setupFn(sandbox);
|
|
5403
|
-
}
|
|
5404
|
-
const handle = await sandbox.backup();
|
|
5405
|
-
await this.config.db.prepare(
|
|
5406
|
-
`INSERT OR REPLACE INTO repo_snapshots (repo_id, lockfile_hash, backup_handle, image_version, created_at, last_used_at, ttl_hours)
|
|
5407
|
-
VALUES (?, ?, ?, ?, ?, ?, 168)`
|
|
5408
|
-
).bind(repoId, lockfileHash, handle, this.config.imageVersion, Date.now(), Date.now()).run();
|
|
5409
|
-
return sandbox;
|
|
5410
|
-
}
|
|
5411
|
-
/**
|
|
5412
|
-
* Clean up expired snapshots (older than TTL).
|
|
5413
|
-
*/
|
|
5414
|
-
async cleanupExpired() {
|
|
5415
|
-
const now = Date.now();
|
|
5416
|
-
const result = await this.config.db.prepare(
|
|
5417
|
-
"SELECT repo_id, lockfile_hash FROM repo_snapshots WHERE (last_used_at + ttl_hours * 3600000) < ?"
|
|
5418
|
-
).bind(now).all();
|
|
5419
|
-
if (result.results.length === 0) return 0;
|
|
5420
|
-
const stmts = result.results.map(
|
|
5421
|
-
(r) => this.config.db.prepare(
|
|
5422
|
-
"DELETE FROM repo_snapshots WHERE repo_id = ? AND lockfile_hash = ?"
|
|
5423
|
-
).bind(r.repo_id, r.lockfile_hash)
|
|
5424
|
-
);
|
|
5425
|
-
await this.config.db.batch(stmts);
|
|
5426
|
-
return result.results.length;
|
|
5427
|
-
}
|
|
5428
|
-
};
|
|
5429
4617
|
}
|
|
5430
4618
|
});
|
|
5431
4619
|
|
|
5432
4620
|
// ../agent/src/sandbox/sync.ts
|
|
5433
|
-
async function syncBeforeBash(store, sandbox, workDir) {
|
|
5434
|
-
const dirtyFiles = await store.getDirtyFiles();
|
|
5435
|
-
const errors = [];
|
|
5436
|
-
let pushed = 0;
|
|
5437
|
-
for (const file of dirtyFiles) {
|
|
5438
|
-
try {
|
|
5439
|
-
const fullPath = `${workDir}/${file.path}`;
|
|
5440
|
-
const dir = fullPath.substring(0, fullPath.lastIndexOf("/"));
|
|
5441
|
-
await sandbox.exec(`mkdir -p '${dir}'`);
|
|
5442
|
-
await sandbox.writeFile(fullPath, file.content);
|
|
5443
|
-
pushed++;
|
|
5444
|
-
} catch (err) {
|
|
5445
|
-
errors.push(`Failed to push ${file.path}: ${err instanceof Error ? err.message : String(err)}`);
|
|
5446
|
-
}
|
|
5447
|
-
}
|
|
5448
|
-
return { filesPushed: pushed, errors };
|
|
5449
|
-
}
|
|
5450
|
-
async function syncAfterBash(store, sandbox, workDir) {
|
|
5451
|
-
const errors = [];
|
|
5452
|
-
let pulled = 0;
|
|
5453
|
-
const gitResult = await sandbox.exec(`cd '${workDir}' && git status --short`);
|
|
5454
|
-
if (gitResult.exitCode !== 0) {
|
|
5455
|
-
return { filesPulled: 0, errors: [`git status failed: ${gitResult.stderr}`] };
|
|
5456
|
-
}
|
|
5457
|
-
if (!gitResult.stdout.trim()) {
|
|
5458
|
-
return { filesPulled: 0, errors: [] };
|
|
5459
|
-
}
|
|
5460
|
-
const changedFiles = parseGitStatus(gitResult.stdout);
|
|
5461
|
-
const fileEntries = [];
|
|
5462
|
-
for (const file of changedFiles) {
|
|
5463
|
-
if (file.status === "deleted") {
|
|
5464
|
-
fileEntries.push({ path: file.path, content: "", status: "deleted" });
|
|
5465
|
-
pulled++;
|
|
5466
|
-
continue;
|
|
5467
|
-
}
|
|
5468
|
-
if (isGeneratedPath(file.path)) continue;
|
|
5469
|
-
try {
|
|
5470
|
-
const content = await sandbox.readFile(`${workDir}/${file.path}`);
|
|
5471
|
-
fileEntries.push({
|
|
5472
|
-
path: file.path,
|
|
5473
|
-
content,
|
|
5474
|
-
status: file.status === "new" ? "created" : "modified"
|
|
5475
|
-
});
|
|
5476
|
-
pulled++;
|
|
5477
|
-
} catch (err) {
|
|
5478
|
-
errors.push(`Failed to pull ${file.path}: ${err instanceof Error ? err.message : String(err)}`);
|
|
5479
|
-
}
|
|
5480
|
-
}
|
|
5481
|
-
if (fileEntries.length > 0) {
|
|
5482
|
-
await store.upsertFiles(fileEntries);
|
|
5483
|
-
}
|
|
5484
|
-
return { filesPulled: pulled, errors };
|
|
5485
|
-
}
|
|
5486
|
-
function parseGitStatus(output) {
|
|
5487
|
-
const entries = [];
|
|
5488
|
-
for (const line of output.split("\n")) {
|
|
5489
|
-
if (!line.trim()) continue;
|
|
5490
|
-
const statusCode = line.substring(0, 2);
|
|
5491
|
-
const path12 = line.substring(3).trim();
|
|
5492
|
-
if (!path12) continue;
|
|
5493
|
-
if (statusCode.includes("D")) {
|
|
5494
|
-
entries.push({ path: path12, status: "deleted" });
|
|
5495
|
-
} else if (statusCode === "??" || statusCode.includes("A")) {
|
|
5496
|
-
entries.push({ path: path12, status: "new" });
|
|
5497
|
-
} else {
|
|
5498
|
-
entries.push({ path: path12, status: "modified" });
|
|
5499
|
-
}
|
|
5500
|
-
}
|
|
5501
|
-
return entries;
|
|
5502
|
-
}
|
|
5503
|
-
function isGeneratedPath(path12) {
|
|
5504
|
-
return GENERATED_PATTERNS.some((p) => path12.startsWith(p) || path12.includes(`/${p}`));
|
|
5505
|
-
}
|
|
5506
|
-
var GENERATED_PATTERNS;
|
|
5507
4621
|
var init_sync = __esm({
|
|
5508
4622
|
"../agent/src/sandbox/sync.ts"() {
|
|
5509
4623
|
"use strict";
|
|
5510
|
-
GENERATED_PATTERNS = [
|
|
5511
|
-
"node_modules/",
|
|
5512
|
-
"dist/",
|
|
5513
|
-
".cache/",
|
|
5514
|
-
".next/",
|
|
5515
|
-
"__pycache__/",
|
|
5516
|
-
".pytest_cache/",
|
|
5517
|
-
"coverage/",
|
|
5518
|
-
".git/"
|
|
5519
|
-
];
|
|
5520
4624
|
}
|
|
5521
4625
|
});
|
|
5522
4626
|
|
|
@@ -5927,17 +5031,11 @@ import * as path2 from "node:path";
|
|
|
5927
5031
|
function truncate(e) {
|
|
5928
5032
|
return e.subject.length > MAX_SUBJECT ? { ...e, subject: e.subject.slice(0, MAX_SUBJECT) + "...[truncated]" } : e;
|
|
5929
5033
|
}
|
|
5930
|
-
var MAX_SUBJECT,
|
|
5034
|
+
var MAX_SUBJECT, FileAuditLog;
|
|
5931
5035
|
var init_audit_log = __esm({
|
|
5932
5036
|
"../agent/src/host/audit-log.ts"() {
|
|
5933
5037
|
"use strict";
|
|
5934
5038
|
MAX_SUBJECT = 4096;
|
|
5935
|
-
MemoryAuditLog = class {
|
|
5936
|
-
entries = [];
|
|
5937
|
-
async record(entry) {
|
|
5938
|
-
this.entries.push(truncate(entry));
|
|
5939
|
-
}
|
|
5940
|
-
};
|
|
5941
5039
|
FileAuditLog = class {
|
|
5942
5040
|
constructor(file) {
|
|
5943
5041
|
this.file = file;
|
|
@@ -5951,89 +5049,23 @@ var init_audit_log = __esm({
|
|
|
5951
5049
|
});
|
|
5952
5050
|
|
|
5953
5051
|
// ../agent/src/host/cloud-host.ts
|
|
5954
|
-
var CloudHost;
|
|
5955
5052
|
var init_cloud_host = __esm({
|
|
5956
5053
|
"../agent/src/host/cloud-host.ts"() {
|
|
5957
5054
|
"use strict";
|
|
5958
|
-
CloudHost = class {
|
|
5959
|
-
kind = "cloud";
|
|
5960
|
-
files;
|
|
5961
|
-
execFn;
|
|
5962
|
-
constructor(config) {
|
|
5963
|
-
this.files = config.files;
|
|
5964
|
-
this.execFn = config.exec;
|
|
5965
|
-
}
|
|
5966
|
-
exec(command, onOutput) {
|
|
5967
|
-
return this.execFn(command, onOutput);
|
|
5968
|
-
}
|
|
5969
|
-
};
|
|
5970
5055
|
}
|
|
5971
5056
|
});
|
|
5972
5057
|
|
|
5973
5058
|
// ../agent/src/replay-buffer.ts
|
|
5974
|
-
var ReplayBuffer;
|
|
5975
5059
|
var init_replay_buffer = __esm({
|
|
5976
5060
|
"../agent/src/replay-buffer.ts"() {
|
|
5977
5061
|
"use strict";
|
|
5978
|
-
ReplayBuffer = class {
|
|
5979
|
-
buffer = [];
|
|
5980
|
-
maxSize;
|
|
5981
|
-
constructor(maxSize = 1e3) {
|
|
5982
|
-
this.maxSize = maxSize;
|
|
5983
|
-
}
|
|
5984
|
-
/** Add an event, evicting the oldest if at capacity. */
|
|
5985
|
-
push(event) {
|
|
5986
|
-
if (this.buffer.length >= this.maxSize) {
|
|
5987
|
-
this.buffer.shift();
|
|
5988
|
-
}
|
|
5989
|
-
this.buffer.push(event);
|
|
5990
|
-
}
|
|
5991
|
-
/** Return all events with seq > lastSeq, in order. */
|
|
5992
|
-
getAfter(lastSeq) {
|
|
5993
|
-
return this.buffer.filter((e) => e.seq > lastSeq);
|
|
5994
|
-
}
|
|
5995
|
-
/** Current number of buffered events. */
|
|
5996
|
-
get size() {
|
|
5997
|
-
return this.buffer.length;
|
|
5998
|
-
}
|
|
5999
|
-
/** Lowest seq still in the buffer, or 0 if empty. */
|
|
6000
|
-
get oldestSeq() {
|
|
6001
|
-
return this.buffer.length > 0 ? this.buffer[0].seq : 0;
|
|
6002
|
-
}
|
|
6003
|
-
/** Highest seq in the buffer, or 0 if empty. */
|
|
6004
|
-
get newestSeq() {
|
|
6005
|
-
return this.buffer.length > 0 ? this.buffer[this.buffer.length - 1].seq : 0;
|
|
6006
|
-
}
|
|
6007
|
-
/** Clear the buffer. */
|
|
6008
|
-
clear() {
|
|
6009
|
-
this.buffer = [];
|
|
6010
|
-
}
|
|
6011
|
-
};
|
|
6012
5062
|
}
|
|
6013
5063
|
});
|
|
6014
5064
|
|
|
6015
5065
|
// ../agent/src/sandbox/lockfile-hash.ts
|
|
6016
|
-
async function sha256(content) {
|
|
6017
|
-
const data = new TextEncoder().encode(content);
|
|
6018
|
-
const hashBuffer = await crypto.subtle.digest("SHA-256", data);
|
|
6019
|
-
const hashArray = new Uint8Array(hashBuffer);
|
|
6020
|
-
return Array.from(hashArray).map((b2) => b2.toString(16).padStart(2, "0")).join("");
|
|
6021
|
-
}
|
|
6022
|
-
async function computeLockfileHash(readFile10) {
|
|
6023
|
-
for (const name of LOCKFILES) {
|
|
6024
|
-
const content = await readFile10(name);
|
|
6025
|
-
if (content) {
|
|
6026
|
-
const hash = await sha256(content);
|
|
6027
|
-
return hash.slice(0, 12);
|
|
6028
|
-
}
|
|
6029
|
-
}
|
|
6030
|
-
return "none";
|
|
6031
|
-
}
|
|
6032
|
-
var LOCKFILES;
|
|
6033
5066
|
var init_lockfile_hash = __esm({
|
|
6034
5067
|
"../agent/src/sandbox/lockfile-hash.ts"() {
|
|
6035
5068
|
"use strict";
|
|
6036
|
-
LOCKFILES = ["package-lock.json", "yarn.lock", "pnpm-lock.yaml", "bun.lockb"];
|
|
6037
5069
|
}
|
|
6038
5070
|
});
|
|
6039
5071
|
|
|
@@ -6518,12 +5550,12 @@ function frontmatterDescription(text) {
|
|
|
6518
5550
|
const line = m2[1].split("\n").find((l) => l.startsWith("description:"));
|
|
6519
5551
|
return line ? line.slice("description:".length).trim().slice(0, 300) : "";
|
|
6520
5552
|
}
|
|
6521
|
-
async function readOptional(fs6,
|
|
5553
|
+
async function readOptional(fs6, path13, report) {
|
|
6522
5554
|
try {
|
|
6523
|
-
return await fs6.readFile(
|
|
5555
|
+
return await fs6.readFile(path13);
|
|
6524
5556
|
} catch (err) {
|
|
6525
5557
|
if (isEnoent(err)) return null;
|
|
6526
|
-
report.errors.push({ path:
|
|
5558
|
+
report.errors.push({ path: path13, message: err instanceof Error ? err.message : String(err) });
|
|
6527
5559
|
return null;
|
|
6528
5560
|
}
|
|
6529
5561
|
}
|
|
@@ -6554,17 +5586,17 @@ async function readClaudeAssets({ fs: fs6, home, projectDirs }) {
|
|
|
6554
5586
|
report.notes.push(`Memory read from ${memDir} (namespaced as "${key}.*")`);
|
|
6555
5587
|
const seen = /* @__PURE__ */ new Set();
|
|
6556
5588
|
for (const entry of entries.filter((e) => e.endsWith(".md")).sort()) {
|
|
6557
|
-
const
|
|
6558
|
-
const text = await readOptional(fs6,
|
|
5589
|
+
const path13 = `${memDir}/${entry}`;
|
|
5590
|
+
const text = await readOptional(fs6, path13, report);
|
|
6559
5591
|
if (text === null) continue;
|
|
6560
5592
|
const base = entry === "MEMORY.md" ? "index" : entry.slice(0, -3).replace(/[^A-Za-z0-9._-]/g, "_");
|
|
6561
5593
|
const name = `${key}.${base}`;
|
|
6562
5594
|
if (!NAME_RE3.test(name)) {
|
|
6563
|
-
report.errors.push({ path:
|
|
5595
|
+
report.errors.push({ path: path13, message: `cannot derive a valid asset name from "${entry}"` });
|
|
6564
5596
|
continue;
|
|
6565
5597
|
}
|
|
6566
5598
|
if (seen.has(name)) {
|
|
6567
|
-
report.errors.push({ path:
|
|
5599
|
+
report.errors.push({ path: path13, message: `name collision on "${name}"` });
|
|
6568
5600
|
continue;
|
|
6569
5601
|
}
|
|
6570
5602
|
seen.add(name);
|
|
@@ -6573,7 +5605,7 @@ async function readClaudeAssets({ fs: fs6, home, projectDirs }) {
|
|
|
6573
5605
|
name,
|
|
6574
5606
|
description: entry === "MEMORY.md" ? "Memory index" : frontmatterDescription(text),
|
|
6575
5607
|
content: text,
|
|
6576
|
-
source:
|
|
5608
|
+
source: path13
|
|
6577
5609
|
});
|
|
6578
5610
|
}
|
|
6579
5611
|
return report;
|
|
@@ -6592,28 +5624,28 @@ async function readCommands(fs6, home, report) {
|
|
|
6592
5624
|
}
|
|
6593
5625
|
const seen = /* @__PURE__ */ new Set();
|
|
6594
5626
|
for (const entry of [...entries].sort()) {
|
|
6595
|
-
const
|
|
5627
|
+
const path13 = `${dir}/${entry}`;
|
|
6596
5628
|
if (!entry.endsWith(".md")) {
|
|
6597
|
-
if (!entry.startsWith(".")) report.notes.push(`Skipped ${
|
|
5629
|
+
if (!entry.startsWith(".")) report.notes.push(`Skipped ${path13}: only top-level .md command files are imported (subdirectories are not supported)`);
|
|
6598
5630
|
continue;
|
|
6599
5631
|
}
|
|
6600
5632
|
const name = entry.slice(0, -3);
|
|
6601
5633
|
if (!COMMAND_NAME_RE.test(name)) {
|
|
6602
|
-
report.errors.push({ path:
|
|
5634
|
+
report.errors.push({ path: path13, message: `command name "${name}" must start with a letter and use only letters, digits, - and _` });
|
|
6603
5635
|
continue;
|
|
6604
5636
|
}
|
|
6605
5637
|
if (seen.has(name.toLowerCase())) {
|
|
6606
|
-
report.errors.push({ path:
|
|
5638
|
+
report.errors.push({ path: path13, message: `command name collision on "${name}" (names are case-insensitive)` });
|
|
6607
5639
|
continue;
|
|
6608
5640
|
}
|
|
6609
|
-
const text = await readOptional(fs6,
|
|
5641
|
+
const text = await readOptional(fs6, path13, report);
|
|
6610
5642
|
if (text === null) continue;
|
|
6611
5643
|
if (Buffer.byteLength(text, "utf8") > MAX_ASSET_BYTES) {
|
|
6612
|
-
report.errors.push({ path:
|
|
5644
|
+
report.errors.push({ path: path13, message: `skipped: ${Buffer.byteLength(text, "utf8")} bytes is over the ${MAX_ASSET_BYTES} byte asset limit` });
|
|
6613
5645
|
continue;
|
|
6614
5646
|
}
|
|
6615
5647
|
seen.add(name.toLowerCase());
|
|
6616
|
-
report.assets.push({ kind: "command", name, description: parseCommandFile(text).description, content: text, source:
|
|
5648
|
+
report.assets.push({ kind: "command", name, description: parseCommandFile(text).description, content: text, source: path13 });
|
|
6617
5649
|
}
|
|
6618
5650
|
}
|
|
6619
5651
|
async function listFiles(fs6, dir, rel, out, report) {
|
|
@@ -6666,8 +5698,8 @@ async function readSkills(fs6, home, report) {
|
|
|
6666
5698
|
const siblings = [];
|
|
6667
5699
|
const slugs = /* @__PURE__ */ new Set();
|
|
6668
5700
|
for (const rel of files.filter((f) => f !== "SKILL.md")) {
|
|
6669
|
-
const
|
|
6670
|
-
const content = await readOptional(fs6,
|
|
5701
|
+
const path13 = `${root}/${dir}/${rel}`;
|
|
5702
|
+
const content = await readOptional(fs6, path13, report);
|
|
6671
5703
|
if (content === null) continue;
|
|
6672
5704
|
const size = Buffer.byteLength(content, "utf8");
|
|
6673
5705
|
const name = siblingAssetName(parsed.name, rel);
|
|
@@ -6676,33 +5708,33 @@ async function readSkills(fs6, home, report) {
|
|
|
6676
5708
|
continue;
|
|
6677
5709
|
}
|
|
6678
5710
|
slugs.add(name);
|
|
6679
|
-
siblings.push({ kind: "skill", name, description: `${SIBLING_PREFIX}${rel}`, content, source:
|
|
5711
|
+
siblings.push({ kind: "skill", name, description: `${SIBLING_PREFIX}${rel}`, content, source: path13 });
|
|
6680
5712
|
}
|
|
6681
5713
|
if (siblings.some((s) => isScriptPath(s.description.slice(SIBLING_PREFIX.length)))) report.notes.push(`Skill ${parsed.name}: ships scripts, not runnable until permitted (they run through bash and its permission rules)`);
|
|
6682
5714
|
report.assets.push({ kind: "skill", name: parsed.name, description: parsed.description, content: text, source: skillPath }, ...siblings);
|
|
6683
5715
|
}
|
|
6684
5716
|
}
|
|
6685
5717
|
async function readMcpServers(fs6, home, projectDirs, report) {
|
|
6686
|
-
const
|
|
6687
|
-
const text = await readOptional(fs6,
|
|
5718
|
+
const path13 = `${home}/.claude.json`;
|
|
5719
|
+
const text = await readOptional(fs6, path13, report);
|
|
6688
5720
|
if (text === null) return;
|
|
6689
5721
|
const { servers, errors } = normalizeClaudeJsonMcp(text, projectDirs);
|
|
6690
|
-
for (const e of errors) report.errors.push({ path:
|
|
5722
|
+
for (const e of errors) report.errors.push({ path: path13, message: e });
|
|
6691
5723
|
const seen = /* @__PURE__ */ new Set();
|
|
6692
5724
|
for (const s of servers) {
|
|
6693
5725
|
if (!NAME_RE3.test(s.name)) {
|
|
6694
|
-
report.errors.push({ path:
|
|
5726
|
+
report.errors.push({ path: path13, message: `MCP server name "${s.name}" must use only letters, digits, . _ - (max 100)` });
|
|
6695
5727
|
continue;
|
|
6696
5728
|
}
|
|
6697
5729
|
if (seen.has(s.name.toLowerCase())) {
|
|
6698
|
-
report.errors.push({ path:
|
|
5730
|
+
report.errors.push({ path: path13, message: `MCP server name collision on "${s.name}" (names are case-insensitive)` });
|
|
6699
5731
|
continue;
|
|
6700
5732
|
}
|
|
6701
5733
|
seen.add(s.name.toLowerCase());
|
|
6702
5734
|
const { literalKeys, ...def } = s;
|
|
6703
5735
|
const content = JSON.stringify(def, null, 2);
|
|
6704
5736
|
if (Buffer.byteLength(content, "utf8") > MAX_ASSET_BYTES) {
|
|
6705
|
-
report.errors.push({ path:
|
|
5737
|
+
report.errors.push({ path: path13, message: `MCP server "${s.name}" skipped: over the ${MAX_ASSET_BYTES} byte asset limit` });
|
|
6706
5738
|
continue;
|
|
6707
5739
|
}
|
|
6708
5740
|
if (literalKeys.length > 0) {
|
|
@@ -6716,7 +5748,7 @@ async function readMcpServers(fs6, home, projectDirs, report) {
|
|
|
6716
5748
|
name: s.name,
|
|
6717
5749
|
description: s.transport === "stdio" ? `stdio: ${s.command}` : `http: ${s.url}`,
|
|
6718
5750
|
content,
|
|
6719
|
-
source: `${
|
|
5751
|
+
source: `${path13} (${s.scope})`
|
|
6720
5752
|
});
|
|
6721
5753
|
}
|
|
6722
5754
|
}
|
|
@@ -6739,10 +5771,10 @@ var init_claude_sources = __esm({
|
|
|
6739
5771
|
// ../agent/src/import/profile-client.ts
|
|
6740
5772
|
function createProfileClient({ baseUrl, apiKey, fetch: doFetch, timeoutMs = 8e3 }) {
|
|
6741
5773
|
const root = baseUrl.replace(/\/+$/, "");
|
|
6742
|
-
async function call(method,
|
|
5774
|
+
async function call(method, path13, init = {}) {
|
|
6743
5775
|
let res;
|
|
6744
5776
|
try {
|
|
6745
|
-
res = await doFetch(`${root}/api/profile${
|
|
5777
|
+
res = await doFetch(`${root}/api/profile${path13}`, {
|
|
6746
5778
|
method,
|
|
6747
5779
|
headers: { Authorization: `Bearer ${apiKey}`, ...init.body !== void 0 ? { "content-type": "application/json" } : {}, ...init.headers },
|
|
6748
5780
|
...init.body !== void 0 ? { body: JSON.stringify(init.body) } : {},
|
|
@@ -6759,7 +5791,7 @@ function createProfileClient({ baseUrl, apiKey, fetch: doFetch, timeoutMs = 8e3
|
|
|
6759
5791
|
body = void 0;
|
|
6760
5792
|
}
|
|
6761
5793
|
const detail = typeof body === "object" && body !== null && "error" in body ? String(body.error) : res.statusText;
|
|
6762
|
-
throw new ProfileApiError(res.status, `profile ${method} ${
|
|
5794
|
+
throw new ProfileApiError(res.status, `profile ${method} ${path13} -> ${res.status} ${detail}`, body);
|
|
6763
5795
|
}
|
|
6764
5796
|
const enc = encodeURIComponent;
|
|
6765
5797
|
return {
|
|
@@ -6874,7 +5906,7 @@ function createMemoryReadTool(client, projectKey2) {
|
|
|
6874
5906
|
}
|
|
6875
5907
|
};
|
|
6876
5908
|
}
|
|
6877
|
-
var MEMORY_READ_TOOL,
|
|
5909
|
+
var MEMORY_READ_TOOL, SAFE;
|
|
6878
5910
|
var init_memory_tool = __esm({
|
|
6879
5911
|
"../agent/src/import/memory-tool.ts"() {
|
|
6880
5912
|
"use strict";
|
|
@@ -6887,7 +5919,6 @@ var init_memory_tool = __esm({
|
|
|
6887
5919
|
required: ["name"]
|
|
6888
5920
|
}
|
|
6889
5921
|
};
|
|
6890
|
-
MEMORY_TOOL_DEFINITIONS = [MEMORY_READ_TOOL];
|
|
6891
5922
|
SAFE = /^[A-Za-z0-9._-]{1,100}$/;
|
|
6892
5923
|
}
|
|
6893
5924
|
});
|
|
@@ -6969,9 +6000,6 @@ function splitSections(text) {
|
|
|
6969
6000
|
const preamble = starts[0] === 0 ? "" : lines2.slice(0, starts[0]).join("\n") + "\n";
|
|
6970
6001
|
return { preamble, sections: sections2 };
|
|
6971
6002
|
}
|
|
6972
|
-
function joinSections(split) {
|
|
6973
|
-
return split.preamble + split.sections.map((s) => s.text).join("");
|
|
6974
|
-
}
|
|
6975
6003
|
var init_instruction_sections = __esm({
|
|
6976
6004
|
"../agent/src/import/instruction-sections.ts"() {
|
|
6977
6005
|
"use strict";
|
|
@@ -7140,7 +6168,7 @@ function createInstructionsReadTool(client) {
|
|
|
7140
6168
|
}
|
|
7141
6169
|
};
|
|
7142
6170
|
}
|
|
7143
|
-
var INSTRUCTIONS_READ_TOOL,
|
|
6171
|
+
var INSTRUCTIONS_READ_TOOL, SAFE2;
|
|
7144
6172
|
var init_instructions_tool = __esm({
|
|
7145
6173
|
"../agent/src/import/instructions-tool.ts"() {
|
|
7146
6174
|
"use strict";
|
|
@@ -7154,7 +6182,6 @@ var init_instructions_tool = __esm({
|
|
|
7154
6182
|
required: ["name"]
|
|
7155
6183
|
}
|
|
7156
6184
|
};
|
|
7157
|
-
INSTRUCTIONS_TOOL_DEFINITIONS = [INSTRUCTIONS_READ_TOOL];
|
|
7158
6185
|
SAFE2 = /^[a-z0-9-]{1,40}$/;
|
|
7159
6186
|
}
|
|
7160
6187
|
});
|
|
@@ -7255,7 +6282,7 @@ var init_trust = __esm({
|
|
|
7255
6282
|
// ../agent/src/import/source-changes.ts
|
|
7256
6283
|
function fingerprintSources(files, hash) {
|
|
7257
6284
|
const fingerprint = {};
|
|
7258
|
-
for (const [
|
|
6285
|
+
for (const [path13, content] of Object.entries(files)) fingerprint[path13] = hash(content);
|
|
7259
6286
|
return fingerprint;
|
|
7260
6287
|
}
|
|
7261
6288
|
function diffSources(before, after) {
|
|
@@ -7274,6 +6301,70 @@ var init_source_changes = __esm({
|
|
|
7274
6301
|
}
|
|
7275
6302
|
});
|
|
7276
6303
|
|
|
6304
|
+
// ../agent/src/import/mcp-tool-cache.ts
|
|
6305
|
+
function serverCacheKey(def, hash) {
|
|
6306
|
+
return hash(JSON.stringify([def.name, def.command, def.args, [...def.envNames].sort()]));
|
|
6307
|
+
}
|
|
6308
|
+
function cleanTool(raw) {
|
|
6309
|
+
if (!isPlainObject(raw) || typeof raw.name !== "string" || raw.name === "" || typeof raw.description !== "string" || !isPlainObject(raw.inputSchema)) return null;
|
|
6310
|
+
return { name: raw.name, description: raw.description.slice(0, MAX_DESCRIPTION), inputSchema: raw.inputSchema };
|
|
6311
|
+
}
|
|
6312
|
+
function parseToolCache(text) {
|
|
6313
|
+
if (text === null || text === "") return { cache: {} };
|
|
6314
|
+
let raw;
|
|
6315
|
+
try {
|
|
6316
|
+
raw = JSON.parse(text);
|
|
6317
|
+
} catch (err) {
|
|
6318
|
+
if (!(err instanceof SyntaxError)) throw err;
|
|
6319
|
+
return { cache: {}, error: "tool cache ignored: not valid JSON" };
|
|
6320
|
+
}
|
|
6321
|
+
if (!isPlainObject(raw) || raw.version !== 1 || !isPlainObject(raw.servers)) return { cache: {}, error: "tool cache ignored: unsupported format" };
|
|
6322
|
+
const cache4 = {};
|
|
6323
|
+
for (const key of Object.keys(raw.servers)) {
|
|
6324
|
+
if (Object.keys(cache4).length >= MAX_SERVERS) break;
|
|
6325
|
+
const entry = raw.servers[key];
|
|
6326
|
+
if (!isPlainObject(entry) || !Array.isArray(entry.tools)) continue;
|
|
6327
|
+
const tools = [];
|
|
6328
|
+
for (const t of entry.tools) {
|
|
6329
|
+
const clean = cleanTool(t);
|
|
6330
|
+
if (clean) tools.push(clean);
|
|
6331
|
+
if (tools.length >= MAX_TOOLS_PER_SERVER) break;
|
|
6332
|
+
}
|
|
6333
|
+
Object.defineProperty(cache4, key, { value: { tools }, enumerable: true, writable: true, configurable: true });
|
|
6334
|
+
}
|
|
6335
|
+
return { cache: cache4 };
|
|
6336
|
+
}
|
|
6337
|
+
function serializeToolCache(cache4) {
|
|
6338
|
+
return JSON.stringify({ version: 1, servers: cache4 });
|
|
6339
|
+
}
|
|
6340
|
+
function getCachedTools(cache4, key) {
|
|
6341
|
+
return own(cache4, key) ? cache4[key].tools : void 0;
|
|
6342
|
+
}
|
|
6343
|
+
function sameTools(a, b2) {
|
|
6344
|
+
return JSON.stringify(a) === JSON.stringify(b2);
|
|
6345
|
+
}
|
|
6346
|
+
function putCachedTools(cache4, key, tools) {
|
|
6347
|
+
const existing = getCachedTools(cache4, key);
|
|
6348
|
+
if (existing && sameTools(existing, tools)) return cache4;
|
|
6349
|
+
const next = {};
|
|
6350
|
+
for (const k2 of Object.keys(cache4)) if (k2 !== key) Object.defineProperty(next, k2, { value: cache4[k2], enumerable: true, writable: true, configurable: true });
|
|
6351
|
+
Object.defineProperty(next, key, { value: { tools }, enumerable: true, writable: true, configurable: true });
|
|
6352
|
+
const keys = Object.keys(next);
|
|
6353
|
+
for (const k2 of keys.slice(0, Math.max(0, keys.length - MAX_SERVERS))) delete next[k2];
|
|
6354
|
+
return next;
|
|
6355
|
+
}
|
|
6356
|
+
var MAX_TOOLS_PER_SERVER, MAX_SERVERS, MAX_DESCRIPTION, own, isPlainObject;
|
|
6357
|
+
var init_mcp_tool_cache = __esm({
|
|
6358
|
+
"../agent/src/import/mcp-tool-cache.ts"() {
|
|
6359
|
+
"use strict";
|
|
6360
|
+
MAX_TOOLS_PER_SERVER = 200;
|
|
6361
|
+
MAX_SERVERS = 500;
|
|
6362
|
+
MAX_DESCRIPTION = 1024;
|
|
6363
|
+
own = (o, k2) => Object.hasOwn(o, k2);
|
|
6364
|
+
isPlainObject = (v2) => typeof v2 === "object" && v2 !== null && !Array.isArray(v2);
|
|
6365
|
+
}
|
|
6366
|
+
});
|
|
6367
|
+
|
|
7277
6368
|
// ../agent/src/import/imports-config.ts
|
|
7278
6369
|
function allOffConfig() {
|
|
7279
6370
|
return {
|
|
@@ -7289,23 +6380,23 @@ function allOffConfig() {
|
|
|
7289
6380
|
models: {}
|
|
7290
6381
|
};
|
|
7291
6382
|
}
|
|
7292
|
-
function
|
|
6383
|
+
function isPlainObject2(value) {
|
|
7293
6384
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
7294
6385
|
}
|
|
7295
|
-
function parseImportsConfig(text,
|
|
6386
|
+
function parseImportsConfig(text, path13) {
|
|
7296
6387
|
if (text === null) return { config: allOffConfig() };
|
|
7297
|
-
const fail = (reason) => ({ config: allOffConfig(), error: `${
|
|
6388
|
+
const fail = (reason) => ({ config: allOffConfig(), error: `${path13}: ${reason}` });
|
|
7298
6389
|
let parsed;
|
|
7299
6390
|
try {
|
|
7300
6391
|
parsed = JSON.parse(text);
|
|
7301
6392
|
} catch {
|
|
7302
6393
|
return fail("invalid JSON");
|
|
7303
6394
|
}
|
|
7304
|
-
if (!
|
|
6395
|
+
if (!isPlainObject2(parsed)) return fail("top-level JSON must be an object");
|
|
7305
6396
|
if (parsed.version !== 1) return fail(`unsupported version ${String(parsed.version)}`);
|
|
7306
6397
|
const config = allOffConfig();
|
|
7307
6398
|
if (parsed.categories !== void 0) {
|
|
7308
|
-
if (!
|
|
6399
|
+
if (!isPlainObject2(parsed.categories)) return fail("categories must be an object");
|
|
7309
6400
|
for (const [key, value] of Object.entries(parsed.categories)) {
|
|
7310
6401
|
if (!IMPORT_CATEGORIES.includes(key)) return fail(`unknown category key "${key}"`);
|
|
7311
6402
|
if (typeof value !== "boolean") return fail(`category "${key}" must be a boolean`);
|
|
@@ -7313,7 +6404,7 @@ function parseImportsConfig(text, path12) {
|
|
|
7313
6404
|
}
|
|
7314
6405
|
}
|
|
7315
6406
|
if (parsed.models !== void 0) {
|
|
7316
|
-
if (!
|
|
6407
|
+
if (!isPlainObject2(parsed.models)) return fail("models must be an object");
|
|
7317
6408
|
for (const key of MODEL_KEYS) {
|
|
7318
6409
|
const value = parsed.models[key];
|
|
7319
6410
|
if (value === void 0) continue;
|
|
@@ -7323,9 +6414,6 @@ function parseImportsConfig(text, path12) {
|
|
|
7323
6414
|
}
|
|
7324
6415
|
return { config };
|
|
7325
6416
|
}
|
|
7326
|
-
function serializeImportsConfig(config) {
|
|
7327
|
-
return JSON.stringify({ version: 1, categories: config.categories, models: config.models }, null, 2);
|
|
7328
|
-
}
|
|
7329
6417
|
var IMPORT_CATEGORIES, MODEL_KEYS;
|
|
7330
6418
|
var init_imports_config = __esm({
|
|
7331
6419
|
"../agent/src/import/imports-config.ts"() {
|
|
@@ -7918,135 +7006,6 @@ var init_mcp_client = __esm({
|
|
|
7918
7006
|
});
|
|
7919
7007
|
|
|
7920
7008
|
// ../agent/src/index.ts
|
|
7921
|
-
var src_exports = {};
|
|
7922
|
-
__export(src_exports, {
|
|
7923
|
-
AdaptPlanError: () => AdaptPlanError,
|
|
7924
|
-
BASH_TOOL_DEFINITION: () => BASH_TOOL_DEFINITION,
|
|
7925
|
-
BUILD_FEATURE_TOOL: () => BUILD_FEATURE_TOOL,
|
|
7926
|
-
CALL_TIMEOUT_MS: () => CALL_TIMEOUT_MS,
|
|
7927
|
-
CODEGEN_TOOL_DEFINITIONS: () => CODEGEN_TOOL_DEFINITIONS,
|
|
7928
|
-
CONNECT_TIMEOUT_MS: () => CONNECT_TIMEOUT_MS,
|
|
7929
|
-
CREATE_PR_TOOL: () => CREATE_PR_TOOL,
|
|
7930
|
-
CloudHost: () => CloudHost,
|
|
7931
|
-
DEFAULT_MAX_CONTEXT_TOKENS: () => DEFAULT_MAX_CONTEXT_TOKENS,
|
|
7932
|
-
DEFAULT_PROFILE_BUDGET_CHARS: () => DEFAULT_PROFILE_BUDGET_CHARS,
|
|
7933
|
-
EDIT_FILE_TOOL: () => EDIT_FILE_TOOL,
|
|
7934
|
-
FILE_TOOL_DEFINITIONS: () => FILE_TOOL_DEFINITIONS,
|
|
7935
|
-
FileAuditLog: () => FileAuditLog,
|
|
7936
|
-
FileStore: () => FileStore,
|
|
7937
|
-
GITHUB_TOOL_DEFINITIONS: () => GITHUB_TOOL_DEFINITIONS,
|
|
7938
|
-
IMPORT_CATEGORIES: () => IMPORT_CATEGORIES,
|
|
7939
|
-
INSTRUCTIONS_READ_TOOL: () => INSTRUCTIONS_READ_TOOL,
|
|
7940
|
-
INSTRUCTIONS_TOOL_DEFINITIONS: () => INSTRUCTIONS_TOOL_DEFINITIONS,
|
|
7941
|
-
LIST_DIRECTORY_TOOL: () => LIST_DIRECTORY_TOOL,
|
|
7942
|
-
LocalHost: () => LocalHost,
|
|
7943
|
-
MAX_ASSET_BYTES: () => MAX_ASSET_BYTES,
|
|
7944
|
-
MAX_CORE_CHARS: () => MAX_CORE_CHARS,
|
|
7945
|
-
MCP_PROTOCOL_VERSION: () => MCP_PROTOCOL_VERSION,
|
|
7946
|
-
MEMORY_READ_TOOL: () => MEMORY_READ_TOOL,
|
|
7947
|
-
MEMORY_TOOL_DEFINITIONS: () => MEMORY_TOOL_DEFINITIONS,
|
|
7948
|
-
McpProtocolError: () => McpProtocolError,
|
|
7949
|
-
MemoryAuditLog: () => MemoryAuditLog,
|
|
7950
|
-
PERMISSION_MODES: () => PERMISSION_MODES,
|
|
7951
|
-
PREVIEW_TOOL: () => PREVIEW_TOOL,
|
|
7952
|
-
PREVIEW_TOOL_DEFINITIONS: () => PREVIEW_TOOL_DEFINITIONS,
|
|
7953
|
-
PendingRequests: () => PendingRequests,
|
|
7954
|
-
PermissionState: () => PermissionState,
|
|
7955
|
-
PolicyViolation: () => PolicyViolation,
|
|
7956
|
-
ProfileApiError: () => ProfileApiError,
|
|
7957
|
-
READ_FILE_TOOL: () => READ_FILE_TOOL,
|
|
7958
|
-
RESULT_CAP_CHARS: () => RESULT_CAP_CHARS,
|
|
7959
|
-
ReplayBuffer: () => ReplayBuffer,
|
|
7960
|
-
RepoIndexer: () => RepoIndexer,
|
|
7961
|
-
RpcAbortError: () => RpcAbortError,
|
|
7962
|
-
RpcClosedError: () => RpcClosedError,
|
|
7963
|
-
RpcError: () => RpcError,
|
|
7964
|
-
RpcTimeoutError: () => RpcTimeoutError,
|
|
7965
|
-
SEARCH_TOOL: () => SEARCH_TOOL,
|
|
7966
|
-
SENTRY_GET_ISSUE_TOOL: () => SENTRY_GET_ISSUE_TOOL,
|
|
7967
|
-
SENTRY_LIST_ISSUES_TOOL: () => SENTRY_LIST_ISSUES_TOOL,
|
|
7968
|
-
SENTRY_TOOL_DEFINITIONS: () => SENTRY_TOOL_DEFINITIONS,
|
|
7969
|
-
SIBLING_PREFIX: () => SIBLING_PREFIX,
|
|
7970
|
-
SKILLS_BUDGET_CHARS: () => SKILLS_BUDGET_CHARS,
|
|
7971
|
-
SKILL_TOOL: () => SKILL_TOOL,
|
|
7972
|
-
SnapshotManager: () => SnapshotManager,
|
|
7973
|
-
TODO_TOOL: () => TODO_TOOL,
|
|
7974
|
-
TODO_TOOL_DEFINITIONS: () => TODO_TOOL_DEFINITIONS,
|
|
7975
|
-
TOOL_NAME_MAP: () => TOOL_NAME_MAP,
|
|
7976
|
-
TrustError: () => TrustError,
|
|
7977
|
-
TrustStore: () => TrustStore,
|
|
7978
|
-
WRITE_FILE_TOOL: () => WRITE_FILE_TOOL,
|
|
7979
|
-
allOffConfig: () => allOffConfig,
|
|
7980
|
-
applyPlan: () => applyPlan,
|
|
7981
|
-
buildCommandRegistry: () => buildCommandRegistry,
|
|
7982
|
-
buildProfileSection: () => buildProfileSection,
|
|
7983
|
-
buildRuleSet: () => buildRuleSet,
|
|
7984
|
-
buildSkillsSection: () => buildSkillsSection,
|
|
7985
|
-
buildSystemPrompt: () => buildSystemPrompt,
|
|
7986
|
-
buildTranscriptForSummarization: () => buildTranscriptForSummarization,
|
|
7987
|
-
checkCoverage: () => checkCoverage,
|
|
7988
|
-
classifyCommand: () => classifyCommand,
|
|
7989
|
-
classifyRpcMessage: () => classifyRpcMessage,
|
|
7990
|
-
claudeProjectSlug: () => claudeProjectSlug,
|
|
7991
|
-
computeLockfileHash: () => computeLockfileHash,
|
|
7992
|
-
createBashTool: () => createBashTool,
|
|
7993
|
-
createCodegenTools: () => createCodegenTools,
|
|
7994
|
-
createFileTools: () => createFileTools,
|
|
7995
|
-
createGitHubTools: () => createGitHubTools,
|
|
7996
|
-
createHostSearchTool: () => createHostSearchTool,
|
|
7997
|
-
createHostToolExecutor: () => createHostToolExecutor,
|
|
7998
|
-
createInstructionsReadTool: () => createInstructionsReadTool,
|
|
7999
|
-
createMcpClient: () => createMcpClient,
|
|
8000
|
-
createMemoryReadTool: () => createMemoryReadTool,
|
|
8001
|
-
createPreviewTool: () => createPreviewTool,
|
|
8002
|
-
createProfileClient: () => createProfileClient,
|
|
8003
|
-
createProfileSkillSource: () => createProfileSkillSource,
|
|
8004
|
-
createRpcSession: () => createRpcSession,
|
|
8005
|
-
createSearchTool: () => createSearchTool,
|
|
8006
|
-
createSentryTools: () => createSentryTools,
|
|
8007
|
-
createSimpleRouter: () => createSimpleRouter,
|
|
8008
|
-
createSkillTool: () => createSkillTool,
|
|
8009
|
-
createTodoTool: () => createTodoTool,
|
|
8010
|
-
defaultModeFor: () => defaultModeFor,
|
|
8011
|
-
diffSources: () => diffSources,
|
|
8012
|
-
estimateTokens: () => estimateTokens,
|
|
8013
|
-
estimateTotalTokens: () => estimateTotalTokens,
|
|
8014
|
-
expandCommand: () => expandCommand,
|
|
8015
|
-
expandRefs: () => expandRefs,
|
|
8016
|
-
expandServerEnv: () => expandServerEnv,
|
|
8017
|
-
extractRuleLines: () => extractRuleLines,
|
|
8018
|
-
findUnsupported: () => findUnsupported,
|
|
8019
|
-
fingerprintSources: () => fingerprintSources,
|
|
8020
|
-
flattenMessageText: () => flattenMessageText,
|
|
8021
|
-
formatSkillBody: () => formatSkillBody,
|
|
8022
|
-
formatSourceChanges: () => formatSourceChanges,
|
|
8023
|
-
formatToolResult: () => formatToolResult,
|
|
8024
|
-
guardHost: () => guardHost,
|
|
8025
|
-
isReadOnlyCommand: () => isReadOnlyCommand,
|
|
8026
|
-
joinSections: () => joinSections,
|
|
8027
|
-
listMcpSpawns: () => listMcpSpawns,
|
|
8028
|
-
loadProfile: () => loadProfile,
|
|
8029
|
-
normalizeClaudeJsonMcp: () => normalizeClaudeJsonMcp,
|
|
8030
|
-
parseCommandFile: () => parseCommandFile,
|
|
8031
|
-
parseGitStatus: () => parseGitStatus,
|
|
8032
|
-
parseImportsConfig: () => parseImportsConfig,
|
|
8033
|
-
parseSkillFile: () => parseSkillFile,
|
|
8034
|
-
parseTodos: () => parseTodos,
|
|
8035
|
-
prepareAdaptation: () => prepareAdaptation,
|
|
8036
|
-
projectKey: () => projectKey,
|
|
8037
|
-
readClaudeAssets: () => readClaudeAssets,
|
|
8038
|
-
renderProjectCard: () => renderProjectCard,
|
|
8039
|
-
runAgentLoop: () => runAgentLoop,
|
|
8040
|
-
safeSkillPath: () => safeSkillPath,
|
|
8041
|
-
serializeImportsConfig: () => serializeImportsConfig,
|
|
8042
|
-
siblingAssetName: () => siblingAssetName,
|
|
8043
|
-
splitCommand: () => splitCommand,
|
|
8044
|
-
splitSections: () => splitSections,
|
|
8045
|
-
summarizeConversation: () => summarizeConversation,
|
|
8046
|
-
syncAfterBash: () => syncAfterBash,
|
|
8047
|
-
syncBeforeBash: () => syncBeforeBash,
|
|
8048
|
-
translateToolNames: () => translateToolNames
|
|
8049
|
-
});
|
|
8050
7009
|
var init_src = __esm({
|
|
8051
7010
|
"../agent/src/index.ts"() {
|
|
8052
7011
|
init_loop();
|
|
@@ -8086,6 +7045,7 @@ var init_src = __esm({
|
|
|
8086
7045
|
init_instructions_tool();
|
|
8087
7046
|
init_trust();
|
|
8088
7047
|
init_source_changes();
|
|
7048
|
+
init_mcp_tool_cache();
|
|
8089
7049
|
init_imports_config();
|
|
8090
7050
|
init_permission_rules();
|
|
8091
7051
|
init_command_args();
|
|
@@ -8108,14 +7068,9 @@ var init_src = __esm({
|
|
|
8108
7068
|
});
|
|
8109
7069
|
|
|
8110
7070
|
// src/api-key.ts
|
|
8111
|
-
var api_key_exports = {};
|
|
8112
|
-
__export(api_key_exports, {
|
|
8113
|
-
MISSING_KEY_MESSAGE: () => MISSING_KEY_MESSAGE,
|
|
8114
|
-
resolveApiKey: () => resolveApiKey
|
|
8115
|
-
});
|
|
8116
7071
|
import { join } from "node:path";
|
|
8117
7072
|
async function resolveApiKey(deps) {
|
|
8118
|
-
const { env: env3, readFile:
|
|
7073
|
+
const { env: env3, readFile: readFile12, homedir: homedir3 } = deps;
|
|
8119
7074
|
for (const name of ["SPACECODE_API_KEY", "ANTHROPIC_API_KEY"]) {
|
|
8120
7075
|
const value = env3[name]?.trim();
|
|
8121
7076
|
if (value) return value;
|
|
@@ -8124,7 +7079,7 @@ async function resolveApiKey(deps) {
|
|
|
8124
7079
|
const file = join(dir, "auth.json");
|
|
8125
7080
|
let raw;
|
|
8126
7081
|
try {
|
|
8127
|
-
raw = await
|
|
7082
|
+
raw = await readFile12(file);
|
|
8128
7083
|
} catch (err) {
|
|
8129
7084
|
if (err.code === "ENOENT") return null;
|
|
8130
7085
|
throw new Error(`Cannot read ${file}: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -8157,26 +7112,26 @@ __export(import_gate_exports, {
|
|
|
8157
7112
|
importGateStatus: () => importGateStatus,
|
|
8158
7113
|
nodeTrustFs: () => nodeTrustFs,
|
|
8159
7114
|
readRepoSources: () => readRepoSources,
|
|
8160
|
-
sha256: () =>
|
|
7115
|
+
sha256: () => sha256,
|
|
8161
7116
|
writeTextAtomic: () => writeTextAtomic
|
|
8162
7117
|
});
|
|
8163
7118
|
import { createHash, randomUUID } from "node:crypto";
|
|
8164
7119
|
import { chmod, mkdir, readFile, readdir, realpath, rename, writeFile } from "node:fs/promises";
|
|
8165
7120
|
import { dirname as dirname3, join as join2 } from "node:path";
|
|
8166
|
-
async function readOrNull(
|
|
7121
|
+
async function readOrNull(path13) {
|
|
8167
7122
|
try {
|
|
8168
|
-
return await readFile(
|
|
7123
|
+
return await readFile(path13, "utf8");
|
|
8169
7124
|
} catch (err) {
|
|
8170
7125
|
if (err.code === "ENOENT") return null;
|
|
8171
7126
|
throw err;
|
|
8172
7127
|
}
|
|
8173
7128
|
}
|
|
8174
|
-
async function writeTextAtomic(
|
|
8175
|
-
await mkdir(dirname3(
|
|
8176
|
-
const tmp = `${
|
|
7129
|
+
async function writeTextAtomic(path13, text) {
|
|
7130
|
+
await mkdir(dirname3(path13), { recursive: true });
|
|
7131
|
+
const tmp = `${path13}.${process.pid}.${randomUUID()}.tmp`;
|
|
8177
7132
|
await writeFile(tmp, text, { mode: 384 });
|
|
8178
7133
|
await chmod(tmp, 384);
|
|
8179
|
-
await rename(tmp,
|
|
7134
|
+
await rename(tmp, path13);
|
|
8180
7135
|
}
|
|
8181
7136
|
async function readRepoSources(cwd2) {
|
|
8182
7137
|
const files = {};
|
|
@@ -8230,7 +7185,7 @@ function nodeTrustFs() {
|
|
|
8230
7185
|
function createNodeTrustStore(configDir) {
|
|
8231
7186
|
let store = trustStores.get(configDir);
|
|
8232
7187
|
if (!store) {
|
|
8233
|
-
store = new TrustStore({ fs: nodeTrustFs(), path: join2(configDir, "trust.json"), hash:
|
|
7188
|
+
store = new TrustStore({ fs: nodeTrustFs(), path: join2(configDir, "trust.json"), hash: sha256 });
|
|
8234
7189
|
trustStores.set(configDir, store);
|
|
8235
7190
|
}
|
|
8236
7191
|
return store;
|
|
@@ -8262,7 +7217,7 @@ async function importGateStatus(deps) {
|
|
|
8262
7217
|
}
|
|
8263
7218
|
return lines2;
|
|
8264
7219
|
}
|
|
8265
|
-
var REPO_SOURCE_FILES, MAX_REPO_SKILL_FILES, MAX_REPO_SKILL_BYTES,
|
|
7220
|
+
var REPO_SOURCE_FILES, MAX_REPO_SKILL_FILES, MAX_REPO_SKILL_BYTES, sha256, trustStores;
|
|
8266
7221
|
var init_import_gate = __esm({
|
|
8267
7222
|
"src/import-gate.ts"() {
|
|
8268
7223
|
"use strict";
|
|
@@ -8270,7 +7225,7 @@ var init_import_gate = __esm({
|
|
|
8270
7225
|
REPO_SOURCE_FILES = [".mcp.json", ".claude/settings.json", ".claude/settings.local.json", "CLAUDE.local.md"];
|
|
8271
7226
|
MAX_REPO_SKILL_FILES = 200;
|
|
8272
7227
|
MAX_REPO_SKILL_BYTES = 256 * 1024;
|
|
8273
|
-
|
|
7228
|
+
sha256 = (text) => createHash("sha256").update(text).digest("hex");
|
|
8274
7229
|
trustStores = /* @__PURE__ */ new Map();
|
|
8275
7230
|
}
|
|
8276
7231
|
});
|
|
@@ -8456,12 +7411,6 @@ var init_mcp_loader = __esm({
|
|
|
8456
7411
|
});
|
|
8457
7412
|
|
|
8458
7413
|
// src/profile-runtime.ts
|
|
8459
|
-
var profile_runtime_exports = {};
|
|
8460
|
-
__export(profile_runtime_exports, {
|
|
8461
|
-
DEFAULT_BACKEND_URL: () => DEFAULT_BACKEND_URL,
|
|
8462
|
-
backendUrl: () => backendUrl,
|
|
8463
|
-
prepareProfile: () => prepareProfile
|
|
8464
|
-
});
|
|
8465
7414
|
import { join as join5 } from "node:path";
|
|
8466
7415
|
function backendUrl(env3) {
|
|
8467
7416
|
return env3.SPACECODE_BACKEND_URL?.trim() || env3.SPACECODE_API_URL?.trim() || DEFAULT_BACKEND_URL;
|
|
@@ -8904,6 +7853,14 @@ var init_mcp_manager = __esm({
|
|
|
8904
7853
|
servers = /* @__PURE__ */ new Map();
|
|
8905
7854
|
turnSignal;
|
|
8906
7855
|
startPromise = null;
|
|
7856
|
+
primePromise = null;
|
|
7857
|
+
cache = {};
|
|
7858
|
+
/** Cached tools of approved, startable servers, usable until the live list replaces them. */
|
|
7859
|
+
cachedTools = /* @__PURE__ */ new Map();
|
|
7860
|
+
/** Settles when a server has connected or failed, so a call on a cached tool can wait for it. */
|
|
7861
|
+
connected = /* @__PURE__ */ new Map();
|
|
7862
|
+
cacheNotes = [];
|
|
7863
|
+
cacheWrite = Promise.resolve();
|
|
8907
7864
|
/** Server states in definition order, so output does not depend on which server connected first. */
|
|
8908
7865
|
ordered() {
|
|
8909
7866
|
return this.deps.defs.map((d) => this.servers.get(d.name)).filter((s) => s !== void 0);
|
|
@@ -8912,11 +7869,95 @@ var init_mcp_manager = __esm({
|
|
|
8912
7869
|
beginTurn(signal) {
|
|
8913
7870
|
this.turnSignal = signal;
|
|
8914
7871
|
}
|
|
8915
|
-
/**
|
|
7872
|
+
/**
|
|
7873
|
+
* Loads cached tool lists for servers that could start right now (stdio, approved on this machine, environment set),
|
|
7874
|
+
* without spawning anything. Await it before building the first toolset. Never throws; a bad cache is noted in /status.
|
|
7875
|
+
*/
|
|
7876
|
+
prime() {
|
|
7877
|
+
this.primePromise ??= this.primeOnce();
|
|
7878
|
+
return this.primePromise;
|
|
7879
|
+
}
|
|
7880
|
+
async primeOnce() {
|
|
7881
|
+
const tc = this.deps.toolCache;
|
|
7882
|
+
if (!tc) return;
|
|
7883
|
+
try {
|
|
7884
|
+
const parsed = parseToolCache(await tc.read());
|
|
7885
|
+
this.cache = parsed.cache;
|
|
7886
|
+
if (parsed.error) this.cacheNotes.push(parsed.error);
|
|
7887
|
+
} catch (err) {
|
|
7888
|
+
this.cacheNotes.push(`tool cache not read (${err instanceof Error ? err.message : String(err)})`);
|
|
7889
|
+
return;
|
|
7890
|
+
}
|
|
7891
|
+
for (const def of this.deps.defs) {
|
|
7892
|
+
try {
|
|
7893
|
+
if (def.transport !== "stdio") continue;
|
|
7894
|
+
const envNames = approvalEnv(def.env);
|
|
7895
|
+
if (!await this.deps.isStdioApproved({ command: def.command, args: def.args, envNames })) continue;
|
|
7896
|
+
if (!expandServerEnv({ command: def.command, args: def.args, env: def.env }, this.deps.env).ok) continue;
|
|
7897
|
+
const hit = getCachedTools(this.cache, serverCacheKey({ name: def.name, command: def.command, args: def.args, envNames }, tc.hash));
|
|
7898
|
+
if (hit) this.cachedTools.set(def.name, this.usable(def.name, hit).tools);
|
|
7899
|
+
} catch (err) {
|
|
7900
|
+
this.cacheNotes.push(`${def.name}: cached tools skipped (${err instanceof Error ? err.message : String(err)})`);
|
|
7901
|
+
}
|
|
7902
|
+
}
|
|
7903
|
+
}
|
|
7904
|
+
/**
|
|
7905
|
+
* Starts servers in the background. A server whose tools came from the cache is NOT started here: it starts on its first call
|
|
7906
|
+
* (`begin`), so an unused server never spawns. Every other server starts now, because its tools (or why it is skipped) are unknown.
|
|
7907
|
+
* Resolves when those have connected or failed; callers need not await it.
|
|
7908
|
+
*/
|
|
8916
7909
|
start() {
|
|
8917
|
-
this.startPromise ??= Promise.all(this.deps.defs.map((def) => this.
|
|
7910
|
+
this.startPromise ??= Promise.all(this.deps.defs.filter((def) => !this.cachedTools.has(def.name)).map((def) => this.begin(def))).then(() => void 0);
|
|
8918
7911
|
return this.startPromise;
|
|
8919
7912
|
}
|
|
7913
|
+
/** Starts one server once; later callers share the same attempt. */
|
|
7914
|
+
begin(def) {
|
|
7915
|
+
let p = this.connected.get(def.name);
|
|
7916
|
+
if (!p) {
|
|
7917
|
+
p = this.startOne(def);
|
|
7918
|
+
this.connected.set(def.name, p);
|
|
7919
|
+
}
|
|
7920
|
+
return p;
|
|
7921
|
+
}
|
|
7922
|
+
/** Tools whose full names are valid and unique, plus the ones that are not. */
|
|
7923
|
+
usable(server, listed) {
|
|
7924
|
+
const tools = [];
|
|
7925
|
+
const skipped = [];
|
|
7926
|
+
const seen = /* @__PURE__ */ new Set();
|
|
7927
|
+
for (const tool of listed) {
|
|
7928
|
+
const full = mcpToolName(server, tool.name);
|
|
7929
|
+
if (!TOOL_NAME_RE.test(full) || seen.has(full)) skipped.push(tool.name);
|
|
7930
|
+
else {
|
|
7931
|
+
seen.add(full);
|
|
7932
|
+
tools.push(tool);
|
|
7933
|
+
}
|
|
7934
|
+
}
|
|
7935
|
+
return { tools, skipped };
|
|
7936
|
+
}
|
|
7937
|
+
/** Live tools once connected; cached ones while a server is still connecting (or not started yet). */
|
|
7938
|
+
toolsOf(def) {
|
|
7939
|
+
const s = this.servers.get(def.name);
|
|
7940
|
+
if (s?.status === "ready") return s.tools;
|
|
7941
|
+
if (s === void 0 || s.status === "connecting") return this.cachedTools.get(def.name) ?? [];
|
|
7942
|
+
return [];
|
|
7943
|
+
}
|
|
7944
|
+
/** Stores the live list for the next start. Writes are serialized; a failure becomes a /status line. */
|
|
7945
|
+
saveToolCache(def, tools) {
|
|
7946
|
+
const tc = this.deps.toolCache;
|
|
7947
|
+
if (!tc) return Promise.resolve();
|
|
7948
|
+
this.cacheWrite = this.cacheWrite.then(async () => {
|
|
7949
|
+
try {
|
|
7950
|
+
const key = serverCacheKey({ name: def.name, command: def.command, args: def.args, envNames: approvalEnv(def.env) }, tc.hash);
|
|
7951
|
+
const next = putCachedTools(this.cache, key, tools.map((t) => ({ name: t.name, description: t.description, inputSchema: t.inputSchema })));
|
|
7952
|
+
if (next === this.cache) return;
|
|
7953
|
+
await tc.write(serializeToolCache(next));
|
|
7954
|
+
this.cache = next;
|
|
7955
|
+
} catch (err) {
|
|
7956
|
+
this.cacheNotes.push(`${def.name}: tool list not cached (${err instanceof Error ? err.message : String(err)})`);
|
|
7957
|
+
}
|
|
7958
|
+
});
|
|
7959
|
+
return this.cacheWrite;
|
|
7960
|
+
}
|
|
8920
7961
|
async startOne(def) {
|
|
8921
7962
|
const name = def.name;
|
|
8922
7963
|
const skip = (reason) => void this.servers.set(name, { name, def, status: "skipped", reason });
|
|
@@ -8967,19 +8008,11 @@ var init_mcp_manager = __esm({
|
|
|
8967
8008
|
});
|
|
8968
8009
|
await client.initialize();
|
|
8969
8010
|
const listed = await client.listTools();
|
|
8970
|
-
const tools =
|
|
8971
|
-
const skippedTools = [];
|
|
8972
|
-
const seen = /* @__PURE__ */ new Set();
|
|
8973
|
-
for (const tool of listed.tools) {
|
|
8974
|
-
const full = mcpToolName(name, tool.name);
|
|
8975
|
-
if (!TOOL_NAME_RE.test(full) || seen.has(full)) skippedTools.push(tool.name);
|
|
8976
|
-
else {
|
|
8977
|
-
seen.add(full);
|
|
8978
|
-
tools.push(tool);
|
|
8979
|
-
}
|
|
8980
|
-
}
|
|
8011
|
+
const { tools, skipped: skippedTools } = this.usable(name, listed.tools);
|
|
8981
8012
|
if (listed.skipped > 0) skippedTools.push(`${listed.skipped} unnamed`);
|
|
8982
8013
|
this.servers.set(name, { name, def, status: "ready", client, transport, session: session2, tools, skippedTools });
|
|
8014
|
+
this.cachedTools.delete(name);
|
|
8015
|
+
await this.saveToolCache(def, tools);
|
|
8983
8016
|
} catch (err) {
|
|
8984
8017
|
await transport?.close().catch(() => void 0);
|
|
8985
8018
|
fail(err instanceof Error ? err.message : String(err));
|
|
@@ -8988,22 +8021,30 @@ var init_mcp_manager = __esm({
|
|
|
8988
8021
|
/** Tools of every connected server, as the model sees them. */
|
|
8989
8022
|
tools() {
|
|
8990
8023
|
const out = [];
|
|
8991
|
-
for (const
|
|
8992
|
-
|
|
8993
|
-
|
|
8994
|
-
out.push({ name: mcpToolName(s.name, t.name), description: `[MCP server ${s.name}] ${t.description}`.slice(0, 1024), input_schema: t.inputSchema });
|
|
8024
|
+
for (const def of this.deps.defs) {
|
|
8025
|
+
for (const t of this.toolsOf(def)) {
|
|
8026
|
+
out.push({ name: mcpToolName(def.name, t.name), description: `[MCP server ${def.name}] ${t.description}`.slice(0, 1024), input_schema: t.inputSchema });
|
|
8995
8027
|
}
|
|
8996
8028
|
}
|
|
8997
8029
|
return out;
|
|
8998
8030
|
}
|
|
8999
8031
|
handlers() {
|
|
9000
8032
|
const out = {};
|
|
9001
|
-
for (const
|
|
9002
|
-
|
|
9003
|
-
for (const t of s.tools) out[mcpToolName(s.name, t.name)] = (input) => this.call(s.name, t.name, input);
|
|
8033
|
+
for (const def of this.deps.defs) {
|
|
8034
|
+
for (const t of this.toolsOf(def)) out[mcpToolName(def.name, t.name)] = (input) => this.callWhenReady(def.name, t.name, input);
|
|
9004
8035
|
}
|
|
9005
8036
|
return out;
|
|
9006
8037
|
}
|
|
8038
|
+
/** A call on a cached tool starts its server if it is not running yet, waits for it to connect (bounded by the connect timeout) and then goes through the normal checks. */
|
|
8039
|
+
async callWhenReady(server, tool, input) {
|
|
8040
|
+
const def = this.deps.defs.find((d) => d.name === server);
|
|
8041
|
+
if (def) await this.begin(def);
|
|
8042
|
+
const state = this.servers.get(server);
|
|
8043
|
+
if (state?.status === "ready" && !state.tools.some((t) => t.name === tool)) {
|
|
8044
|
+
return { output: `MCP server "${server}" no longer offers ${tool}; its tool list changed since it was cached.`, is_error: true };
|
|
8045
|
+
}
|
|
8046
|
+
return this.call(server, tool, input);
|
|
8047
|
+
}
|
|
9007
8048
|
async call(server, tool, input) {
|
|
9008
8049
|
const full = mcpToolName(server, tool);
|
|
9009
8050
|
const state = this.servers.get(server);
|
|
@@ -9045,11 +8086,18 @@ var init_mcp_manager = __esm({
|
|
|
9045
8086
|
/** One line per server for /status; reasons never include env values or stderr. */
|
|
9046
8087
|
statusLines() {
|
|
9047
8088
|
const lines2 = [];
|
|
9048
|
-
for (const
|
|
8089
|
+
for (const def of this.deps.defs) {
|
|
8090
|
+
const s = this.servers.get(def.name);
|
|
8091
|
+
if (s === void 0) {
|
|
8092
|
+
const cached3 = this.cachedTools.get(def.name);
|
|
8093
|
+
if (cached3) lines2.push(`mcp: ${def.name} not started, ${cached3.length} tools from cache (starts on first use)`);
|
|
8094
|
+
continue;
|
|
8095
|
+
}
|
|
9049
8096
|
if (s.status === "ready") lines2.push(`mcp: ${s.name} ready, ${s.tools.length} tools${s.skippedTools.length ? ` (not usable: ${s.skippedTools.join(", ")})` : ""}`);
|
|
9050
|
-
else if (s.status === "connecting") lines2.push(`mcp: ${s.name} connecting
|
|
8097
|
+
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)` : ""}`);
|
|
9051
8098
|
else lines2.push(`mcp: ${s.name} NOT available (${s.reason})`);
|
|
9052
8099
|
}
|
|
8100
|
+
for (const n of this.cacheNotes) lines2.push(`mcp: ${n}`);
|
|
9053
8101
|
return lines2;
|
|
9054
8102
|
}
|
|
9055
8103
|
/** For /tools: names grouped by server. */
|
|
@@ -9066,13 +8114,9 @@ var init_mcp_manager = __esm({
|
|
|
9066
8114
|
});
|
|
9067
8115
|
|
|
9068
8116
|
// src/mcp/approve-mcp.ts
|
|
9069
|
-
var approve_mcp_exports = {};
|
|
9070
|
-
__export(approve_mcp_exports, {
|
|
9071
|
-
runApproveMcp: () => runApproveMcp
|
|
9072
|
-
});
|
|
9073
8117
|
async function runApproveMcp(deps) {
|
|
9074
8118
|
const { defs, statusLines } = await loadMcpDefs(deps);
|
|
9075
|
-
for (const l of statusLines) deps.log(l);
|
|
8119
|
+
if (!deps.quiet) for (const l of statusLines) deps.log(l);
|
|
9076
8120
|
const stdio = defs.filter((d) => d.transport === "stdio");
|
|
9077
8121
|
const todo = [];
|
|
9078
8122
|
for (const d of stdio) {
|
|
@@ -9080,13 +8124,15 @@ async function runApproveMcp(deps) {
|
|
|
9080
8124
|
if (await deps.trust.stdioStatus(key) === "unapproved") todo.push(d);
|
|
9081
8125
|
}
|
|
9082
8126
|
if (todo.length === 0) {
|
|
9083
|
-
deps.log(stdio.length === 0 ? "No stdio MCP servers to approve." : "Every stdio MCP server is already approved on this machine.");
|
|
8127
|
+
if (!deps.quiet) deps.log(stdio.length === 0 ? "No stdio MCP servers to approve." : "Every stdio MCP server is already approved on this machine.");
|
|
9084
8128
|
return 0;
|
|
9085
8129
|
}
|
|
9086
8130
|
if (!deps.ask) {
|
|
9087
8131
|
deps.log(`${todo.length} MCP server(s) need approval, but this is not an interactive terminal: nothing was approved.`);
|
|
9088
8132
|
return 1;
|
|
9089
8133
|
}
|
|
8134
|
+
if (deps.quiet) deps.log(`
|
|
8135
|
+
${todo.length} imported MCP server(s) want to run a command on this machine. Nothing spawns until you approve it (you can also run \`spacecode approve-mcp\` later).`);
|
|
9090
8136
|
let approved = 0;
|
|
9091
8137
|
for (const d of todo) {
|
|
9092
8138
|
deps.log(`
|
|
@@ -9114,19 +8160,65 @@ var init_approve_mcp = __esm({
|
|
|
9114
8160
|
}
|
|
9115
8161
|
});
|
|
9116
8162
|
|
|
8163
|
+
// src/mcp/approve-mcp-node.ts
|
|
8164
|
+
var approve_mcp_node_exports = {};
|
|
8165
|
+
__export(approve_mcp_node_exports, {
|
|
8166
|
+
runApproveMcpOnNode: () => runApproveMcpOnNode
|
|
8167
|
+
});
|
|
8168
|
+
import os from "node:os";
|
|
8169
|
+
import path3 from "node:path";
|
|
8170
|
+
import { readFile as readFile2 } from "node:fs/promises";
|
|
8171
|
+
async function runApproveMcpOnNode(opts) {
|
|
8172
|
+
const configDir = process.env.SPACECODE_CONFIG_DIR?.trim() || path3.join(os.homedir(), ".spacecode");
|
|
8173
|
+
const apiKey = await resolveApiKey({ env: process.env, readFile: (p) => readFile2(p, "utf-8"), homedir: os.homedir }).catch(() => null);
|
|
8174
|
+
const interactive = Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
8175
|
+
const { createInterface } = interactive ? await import("node:readline/promises") : { createInterface: void 0 };
|
|
8176
|
+
const rl = createInterface ? createInterface({ input: process.stdin, output: process.stdout }) : void 0;
|
|
8177
|
+
try {
|
|
8178
|
+
return await runApproveMcp({
|
|
8179
|
+
configDir,
|
|
8180
|
+
cwd: process.cwd(),
|
|
8181
|
+
readFile: (p) => readFile2(p, "utf-8"),
|
|
8182
|
+
client: apiKey ? createProfileClient({ baseUrl: backendUrl(process.env), apiKey, fetch }) : null,
|
|
8183
|
+
trust: createNodeTrustStore(configDir),
|
|
8184
|
+
...rl ? { ask: async (q2) => {
|
|
8185
|
+
try {
|
|
8186
|
+
return await rl.question(q2);
|
|
8187
|
+
} catch {
|
|
8188
|
+
return null;
|
|
8189
|
+
}
|
|
8190
|
+
} } : {},
|
|
8191
|
+
log: (line) => console.log(line),
|
|
8192
|
+
quiet: opts.quiet
|
|
8193
|
+
});
|
|
8194
|
+
} finally {
|
|
8195
|
+
rl?.close();
|
|
8196
|
+
}
|
|
8197
|
+
}
|
|
8198
|
+
var init_approve_mcp_node = __esm({
|
|
8199
|
+
"src/mcp/approve-mcp-node.ts"() {
|
|
8200
|
+
"use strict";
|
|
8201
|
+
init_src();
|
|
8202
|
+
init_approve_mcp();
|
|
8203
|
+
init_import_gate();
|
|
8204
|
+
init_api_key();
|
|
8205
|
+
init_profile_runtime();
|
|
8206
|
+
}
|
|
8207
|
+
});
|
|
8208
|
+
|
|
9117
8209
|
// src/repo-trust-prompt.ts
|
|
9118
8210
|
var repo_trust_prompt_exports = {};
|
|
9119
8211
|
__export(repo_trust_prompt_exports, {
|
|
9120
8212
|
describeRepoSources: () => describeRepoSources,
|
|
9121
8213
|
promptRepoTrust: () => promptRepoTrust
|
|
9122
8214
|
});
|
|
9123
|
-
function ruleLists(
|
|
8215
|
+
function ruleLists(path13, text, lines2, warnings) {
|
|
9124
8216
|
let data;
|
|
9125
8217
|
try {
|
|
9126
8218
|
data = JSON.parse(text);
|
|
9127
8219
|
} catch (err) {
|
|
9128
8220
|
if (!(err instanceof SyntaxError)) throw err;
|
|
9129
|
-
warnings.push(`${
|
|
8221
|
+
warnings.push(`${path13}: not valid JSON, nothing from it loads`);
|
|
9130
8222
|
return;
|
|
9131
8223
|
}
|
|
9132
8224
|
const perms = typeof data === "object" && data !== null ? data.permissions : void 0;
|
|
@@ -9134,7 +8226,7 @@ function ruleLists(path12, text, lines2, warnings) {
|
|
|
9134
8226
|
for (const effect of ["allow", "deny", "ask"]) {
|
|
9135
8227
|
const list2 = perms[effect];
|
|
9136
8228
|
if (!Array.isArray(list2)) continue;
|
|
9137
|
-
for (const rule of list2) if (typeof rule === "string") lines2.push(` ${effect}: ${show2(rule)} (${
|
|
8229
|
+
for (const rule of list2) if (typeof rule === "string") lines2.push(` ${effect}: ${show2(rule)} (${path13})`);
|
|
9138
8230
|
}
|
|
9139
8231
|
}
|
|
9140
8232
|
function describeRepoSources(files) {
|
|
@@ -9380,7 +8472,7 @@ __export(base_exports, {
|
|
|
9380
8472
|
synchronizedOutput: () => synchronizedOutput
|
|
9381
8473
|
});
|
|
9382
8474
|
import process2 from "node:process";
|
|
9383
|
-
import
|
|
8475
|
+
import os2 from "node:os";
|
|
9384
8476
|
var ESC, OSC, BEL, SEP, isTerminalApp, isWindows2, isTmux, cwdFunction, wrapOsc, cursorTo, cursorMove, cursorUp, cursorDown, cursorForward, cursorBackward, cursorLeft, cursorSavePosition, cursorRestorePosition, cursorGetPosition, cursorNextLine, cursorPrevLine, cursorHide, cursorShow, eraseLines, eraseEndLine, eraseStartLine, eraseLine, eraseDown, eraseUp, eraseScreen, scrollUp, scrollDown, clearScreen, clearViewport, isOldWindows, clearTerminal, enterAlternativeScreen, exitAlternativeScreen, beginSynchronizedOutput, endSynchronizedOutput, synchronizedOutput, beep, link, image, iTerm, ConEmu, setCwd;
|
|
9385
8477
|
var init_base = __esm({
|
|
9386
8478
|
"../../node_modules/ansi-escapes/base.js"() {
|
|
@@ -9463,7 +8555,7 @@ var init_base = __esm({
|
|
|
9463
8555
|
if (isBrowser || !isWindows2) {
|
|
9464
8556
|
return false;
|
|
9465
8557
|
}
|
|
9466
|
-
const parts =
|
|
8558
|
+
const parts = os2.release().split(".");
|
|
9467
8559
|
const major = Number(parts[0]);
|
|
9468
8560
|
const build2 = Number(parts[2] ?? 0);
|
|
9469
8561
|
if (major < 10) {
|
|
@@ -31743,10 +30835,10 @@ var require_react_reconciler_development = __commonJS({
|
|
|
31743
30835
|
var setErrorHandler = null;
|
|
31744
30836
|
var setSuspenseHandler = null;
|
|
31745
30837
|
{
|
|
31746
|
-
var copyWithDeleteImpl = function(obj,
|
|
31747
|
-
var key =
|
|
30838
|
+
var copyWithDeleteImpl = function(obj, path13, index2) {
|
|
30839
|
+
var key = path13[index2];
|
|
31748
30840
|
var updated = isArray(obj) ? obj.slice() : assign({}, obj);
|
|
31749
|
-
if (index2 + 1 ===
|
|
30841
|
+
if (index2 + 1 === path13.length) {
|
|
31750
30842
|
if (isArray(updated)) {
|
|
31751
30843
|
updated.splice(key, 1);
|
|
31752
30844
|
} else {
|
|
@@ -31754,11 +30846,11 @@ var require_react_reconciler_development = __commonJS({
|
|
|
31754
30846
|
}
|
|
31755
30847
|
return updated;
|
|
31756
30848
|
}
|
|
31757
|
-
updated[key] = copyWithDeleteImpl(obj[key],
|
|
30849
|
+
updated[key] = copyWithDeleteImpl(obj[key], path13, index2 + 1);
|
|
31758
30850
|
return updated;
|
|
31759
30851
|
};
|
|
31760
|
-
var copyWithDelete = function(obj,
|
|
31761
|
-
return copyWithDeleteImpl(obj,
|
|
30852
|
+
var copyWithDelete = function(obj, path13) {
|
|
30853
|
+
return copyWithDeleteImpl(obj, path13, 0);
|
|
31762
30854
|
};
|
|
31763
30855
|
var copyWithRenameImpl = function(obj, oldPath, newPath, index2) {
|
|
31764
30856
|
var oldKey = oldPath[index2];
|
|
@@ -31796,17 +30888,17 @@ var require_react_reconciler_development = __commonJS({
|
|
|
31796
30888
|
}
|
|
31797
30889
|
return copyWithRenameImpl(obj, oldPath, newPath, 0);
|
|
31798
30890
|
};
|
|
31799
|
-
var copyWithSetImpl = function(obj,
|
|
31800
|
-
if (index2 >=
|
|
30891
|
+
var copyWithSetImpl = function(obj, path13, index2, value) {
|
|
30892
|
+
if (index2 >= path13.length) {
|
|
31801
30893
|
return value;
|
|
31802
30894
|
}
|
|
31803
|
-
var key =
|
|
30895
|
+
var key = path13[index2];
|
|
31804
30896
|
var updated = isArray(obj) ? obj.slice() : assign({}, obj);
|
|
31805
|
-
updated[key] = copyWithSetImpl(obj[key],
|
|
30897
|
+
updated[key] = copyWithSetImpl(obj[key], path13, index2 + 1, value);
|
|
31806
30898
|
return updated;
|
|
31807
30899
|
};
|
|
31808
|
-
var copyWithSet = function(obj,
|
|
31809
|
-
return copyWithSetImpl(obj,
|
|
30900
|
+
var copyWithSet = function(obj, path13, value) {
|
|
30901
|
+
return copyWithSetImpl(obj, path13, 0, value);
|
|
31810
30902
|
};
|
|
31811
30903
|
var findHook = function(fiber, id) {
|
|
31812
30904
|
var currentHook2 = fiber.memoizedState;
|
|
@@ -31816,10 +30908,10 @@ var require_react_reconciler_development = __commonJS({
|
|
|
31816
30908
|
}
|
|
31817
30909
|
return currentHook2;
|
|
31818
30910
|
};
|
|
31819
|
-
overrideHookState = function(fiber, id,
|
|
30911
|
+
overrideHookState = function(fiber, id, path13, value) {
|
|
31820
30912
|
var hook = findHook(fiber, id);
|
|
31821
30913
|
if (hook !== null) {
|
|
31822
|
-
var newState = copyWithSet(hook.memoizedState,
|
|
30914
|
+
var newState = copyWithSet(hook.memoizedState, path13, value);
|
|
31823
30915
|
hook.memoizedState = newState;
|
|
31824
30916
|
hook.baseState = newState;
|
|
31825
30917
|
fiber.memoizedProps = assign({}, fiber.memoizedProps);
|
|
@@ -31829,10 +30921,10 @@ var require_react_reconciler_development = __commonJS({
|
|
|
31829
30921
|
}
|
|
31830
30922
|
}
|
|
31831
30923
|
};
|
|
31832
|
-
overrideHookStateDeletePath = function(fiber, id,
|
|
30924
|
+
overrideHookStateDeletePath = function(fiber, id, path13) {
|
|
31833
30925
|
var hook = findHook(fiber, id);
|
|
31834
30926
|
if (hook !== null) {
|
|
31835
|
-
var newState = copyWithDelete(hook.memoizedState,
|
|
30927
|
+
var newState = copyWithDelete(hook.memoizedState, path13);
|
|
31836
30928
|
hook.memoizedState = newState;
|
|
31837
30929
|
hook.baseState = newState;
|
|
31838
30930
|
fiber.memoizedProps = assign({}, fiber.memoizedProps);
|
|
@@ -31855,8 +30947,8 @@ var require_react_reconciler_development = __commonJS({
|
|
|
31855
30947
|
}
|
|
31856
30948
|
}
|
|
31857
30949
|
};
|
|
31858
|
-
overrideProps = function(fiber,
|
|
31859
|
-
fiber.pendingProps = copyWithSet(fiber.memoizedProps,
|
|
30950
|
+
overrideProps = function(fiber, path13, value) {
|
|
30951
|
+
fiber.pendingProps = copyWithSet(fiber.memoizedProps, path13, value);
|
|
31860
30952
|
if (fiber.alternate) {
|
|
31861
30953
|
fiber.alternate.pendingProps = fiber.pendingProps;
|
|
31862
30954
|
}
|
|
@@ -31865,8 +30957,8 @@ var require_react_reconciler_development = __commonJS({
|
|
|
31865
30957
|
scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
|
|
31866
30958
|
}
|
|
31867
30959
|
};
|
|
31868
|
-
overridePropsDeletePath = function(fiber,
|
|
31869
|
-
fiber.pendingProps = copyWithDelete(fiber.memoizedProps,
|
|
30960
|
+
overridePropsDeletePath = function(fiber, path13) {
|
|
30961
|
+
fiber.pendingProps = copyWithDelete(fiber.memoizedProps, path13);
|
|
31870
30962
|
if (fiber.alternate) {
|
|
31871
30963
|
fiber.alternate.pendingProps = fiber.pendingProps;
|
|
31872
30964
|
}
|
|
@@ -37722,7 +36814,7 @@ var init_ansi_styles2 = __esm({
|
|
|
37722
36814
|
|
|
37723
36815
|
// ../../node_modules/chalk/source/vendor/supports-color/index.js
|
|
37724
36816
|
import process4 from "node:process";
|
|
37725
|
-
import
|
|
36817
|
+
import os3 from "node:os";
|
|
37726
36818
|
import tty from "node:tty";
|
|
37727
36819
|
function hasFlag(flag, argv = globalThis.Deno ? globalThis.Deno.args : process4.argv) {
|
|
37728
36820
|
const prefix = flag.startsWith("-") ? "" : flag.length === 1 ? "-" : "--";
|
|
@@ -37780,7 +36872,7 @@ function _supportsColor(haveStream, { streamIsTTY, sniffFlags = true } = {}) {
|
|
|
37780
36872
|
return min;
|
|
37781
36873
|
}
|
|
37782
36874
|
if (process4.platform === "win32") {
|
|
37783
|
-
const osRelease =
|
|
36875
|
+
const osRelease = os3.release().split(".");
|
|
37784
36876
|
if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) {
|
|
37785
36877
|
return Number(osRelease[2]) >= 14931 ? 3 : 2;
|
|
37786
36878
|
}
|
|
@@ -39571,8 +38663,8 @@ var init_ErrorOverview = __esm({
|
|
|
39571
38663
|
init_dist3();
|
|
39572
38664
|
init_Box();
|
|
39573
38665
|
init_Text();
|
|
39574
|
-
cleanupPath = (
|
|
39575
|
-
return
|
|
38666
|
+
cleanupPath = (path13) => {
|
|
38667
|
+
return path13?.replace(`file://${cwd()}/`, "");
|
|
39576
38668
|
};
|
|
39577
38669
|
stackUtils = new import_stack_utils.default({
|
|
39578
38670
|
cwd: cwd(),
|
|
@@ -42811,7 +41903,7 @@ var require_has_flag = __commonJS({
|
|
|
42811
41903
|
var require_supports_colors = __commonJS({
|
|
42812
41904
|
"../../node_modules/@colors/colors/lib/system/supports-colors.js"(exports, module) {
|
|
42813
41905
|
"use strict";
|
|
42814
|
-
var
|
|
41906
|
+
var os7 = __require("os");
|
|
42815
41907
|
var hasFlag2 = require_has_flag();
|
|
42816
41908
|
var env3 = process.env;
|
|
42817
41909
|
var forceColor = void 0;
|
|
@@ -42849,7 +41941,7 @@ var require_supports_colors = __commonJS({
|
|
|
42849
41941
|
}
|
|
42850
41942
|
var min = forceColor ? 1 : 0;
|
|
42851
41943
|
if (process.platform === "win32") {
|
|
42852
|
-
var osRelease =
|
|
41944
|
+
var osRelease = os7.release().split(".");
|
|
42853
41945
|
if (Number(process.versions.node.split(".")[0]) >= 8 && Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) {
|
|
42854
41946
|
return Number(osRelease[2]) >= 14931 ? 3 : 2;
|
|
42855
41947
|
}
|
|
@@ -44353,7 +43445,7 @@ var require_core = __commonJS({
|
|
|
44353
43445
|
return match && match.index === 0;
|
|
44354
43446
|
}
|
|
44355
43447
|
var BACKREF_RE = /\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./;
|
|
44356
|
-
function
|
|
43448
|
+
function join15(regexps, separator = "|") {
|
|
44357
43449
|
let numCaptures = 0;
|
|
44358
43450
|
return regexps.map((regex2) => {
|
|
44359
43451
|
numCaptures += 1;
|
|
@@ -44657,7 +43749,7 @@ var require_core = __commonJS({
|
|
|
44657
43749
|
this.exec = () => null;
|
|
44658
43750
|
}
|
|
44659
43751
|
const terminators = this.regexes.map((el) => el[1]);
|
|
44660
|
-
this.matcherRe = langRe(
|
|
43752
|
+
this.matcherRe = langRe(join15(terminators), true);
|
|
44661
43753
|
this.lastIndex = 0;
|
|
44662
43754
|
}
|
|
44663
43755
|
/** @param {string} s */
|
|
@@ -86934,15 +86026,15 @@ var require_route = __commonJS({
|
|
|
86934
86026
|
};
|
|
86935
86027
|
}
|
|
86936
86028
|
function wrapConversion(toModel, graph) {
|
|
86937
|
-
const
|
|
86029
|
+
const path13 = [graph[toModel].parent, toModel];
|
|
86938
86030
|
let fn = conversions[graph[toModel].parent][toModel];
|
|
86939
86031
|
let cur = graph[toModel].parent;
|
|
86940
86032
|
while (graph[cur].parent) {
|
|
86941
|
-
|
|
86033
|
+
path13.unshift(graph[cur].parent);
|
|
86942
86034
|
fn = link2(conversions[graph[cur].parent][cur], fn);
|
|
86943
86035
|
cur = graph[cur].parent;
|
|
86944
86036
|
}
|
|
86945
|
-
fn.conversion =
|
|
86037
|
+
fn.conversion = path13;
|
|
86946
86038
|
return fn;
|
|
86947
86039
|
}
|
|
86948
86040
|
module.exports = function(fromModel) {
|
|
@@ -87182,7 +86274,7 @@ var require_has_flag2 = __commonJS({
|
|
|
87182
86274
|
var require_supports_color = __commonJS({
|
|
87183
86275
|
"../../node_modules/supports-color/index.js"(exports, module) {
|
|
87184
86276
|
"use strict";
|
|
87185
|
-
var
|
|
86277
|
+
var os7 = __require("os");
|
|
87186
86278
|
var tty2 = __require("tty");
|
|
87187
86279
|
var hasFlag2 = require_has_flag2();
|
|
87188
86280
|
var { env: env3 } = process;
|
|
@@ -87230,7 +86322,7 @@ var require_supports_color = __commonJS({
|
|
|
87230
86322
|
return min;
|
|
87231
86323
|
}
|
|
87232
86324
|
if (process.platform === "win32") {
|
|
87233
|
-
const osRelease =
|
|
86325
|
+
const osRelease = os7.release().split(".");
|
|
87234
86326
|
if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) {
|
|
87235
86327
|
return Number(osRelease[2]) >= 14931 ? 3 : 2;
|
|
87236
86328
|
}
|
|
@@ -102163,9 +101255,9 @@ var init_credential_filter = __esm({
|
|
|
102163
101255
|
});
|
|
102164
101256
|
|
|
102165
101257
|
// src/session/session-store.ts
|
|
102166
|
-
import { chmod as chmod2, mkdir as mkdir2, open, readdir as readdir2, readFile as
|
|
101258
|
+
import { chmod as chmod2, mkdir as mkdir2, open, readdir as readdir2, readFile as readFile3, rename as rename2, rm, stat, writeFile as writeFile2 } from "node:fs/promises";
|
|
102167
101259
|
import { randomBytes } from "node:crypto";
|
|
102168
|
-
import
|
|
101260
|
+
import path4 from "node:path";
|
|
102169
101261
|
function redactCredentials(value) {
|
|
102170
101262
|
if (typeof value === "string") return containsCredential(value) ? REDACTED : value;
|
|
102171
101263
|
if (Array.isArray(value)) return value.map(redactCredentials);
|
|
@@ -102240,7 +101332,7 @@ var init_session_store = __esm({
|
|
|
102240
101332
|
}
|
|
102241
101333
|
fileFor(id) {
|
|
102242
101334
|
if (!ID.test(id)) throw new SessionError(this.dir, `Invalid session id ${JSON.stringify(id)}`);
|
|
102243
|
-
return
|
|
101335
|
+
return path4.join(this.dir, `${id}.jsonl`);
|
|
102244
101336
|
}
|
|
102245
101337
|
async save(session2) {
|
|
102246
101338
|
const file = this.fileFor(session2.meta.id);
|
|
@@ -102269,7 +101361,7 @@ var init_session_store = __esm({
|
|
|
102269
101361
|
if (size > MAX_SESSION_BYTES) {
|
|
102270
101362
|
throw new SessionError(file, `Session is ${size} bytes, over the ${MAX_SESSION_BYTES}-byte cap; not loaded`);
|
|
102271
101363
|
}
|
|
102272
|
-
text = await
|
|
101364
|
+
text = await readFile3(file, "utf8");
|
|
102273
101365
|
} catch (err) {
|
|
102274
101366
|
if (err instanceof SessionError) throw err;
|
|
102275
101367
|
throw new SessionError(file, `Could not read session: ${err instanceof Error ? err.message : String(err)}`, { cause: err });
|
|
@@ -102293,7 +101385,7 @@ var init_session_store = __esm({
|
|
|
102293
101385
|
try {
|
|
102294
101386
|
sessions.push((await this.load(name.slice(0, -".jsonl".length))).meta);
|
|
102295
101387
|
} catch (err) {
|
|
102296
|
-
errors.push(err instanceof SessionError ? err : new SessionError(
|
|
101388
|
+
errors.push(err instanceof SessionError ? err : new SessionError(path4.join(this.dir, name), String(err), { cause: err }));
|
|
102297
101389
|
}
|
|
102298
101390
|
}
|
|
102299
101391
|
sessions.sort((a, b2) => b2.updatedAt.localeCompare(a.updatedAt));
|
|
@@ -103367,17 +102459,17 @@ function isSubsequence(needle, hay) {
|
|
|
103367
102459
|
for (let j2 = 0; j2 < hay.length && i < needle.length; j2++) if (hay[j2] === needle[i]) i++;
|
|
103368
102460
|
return i === needle.length;
|
|
103369
102461
|
}
|
|
103370
|
-
function tier(
|
|
103371
|
-
const base =
|
|
102462
|
+
function tier(path13, q2) {
|
|
102463
|
+
const base = path13.slice(path13.lastIndexOf("/") + 1);
|
|
103372
102464
|
if (base.startsWith(q2)) return 0;
|
|
103373
102465
|
if (base.includes(q2)) return 1;
|
|
103374
|
-
if (
|
|
102466
|
+
if (path13.includes(q2)) return 2;
|
|
103375
102467
|
return 3;
|
|
103376
102468
|
}
|
|
103377
102469
|
function rankPaths(paths, query, limit = 8) {
|
|
103378
102470
|
if (query === "") return paths.slice(0, limit);
|
|
103379
102471
|
const q2 = query.toLowerCase();
|
|
103380
|
-
return paths.map((
|
|
102472
|
+
return paths.map((path13) => ({ path: path13, lower: path13.toLowerCase() })).filter(({ lower }) => isSubsequence(q2, lower)).map(({ path: path13, lower }) => ({ path: path13, rank: tier(lower, q2) })).sort((a, b2) => a.rank - b2.rank || a.path.length - b2.path.length || (a.path < b2.path ? -1 : a.path > b2.path ? 1 : 0)).slice(0, limit).map((m2) => m2.path);
|
|
103381
102473
|
}
|
|
103382
102474
|
var init_mention_rank = __esm({
|
|
103383
102475
|
"src/editor/mention-rank.ts"() {
|
|
@@ -103394,9 +102486,9 @@ function activeMention(text, cursor) {
|
|
|
103394
102486
|
const explicit = token[1] === "!";
|
|
103395
102487
|
return { start, query: token.slice(explicit ? 2 : 1), explicit };
|
|
103396
102488
|
}
|
|
103397
|
-
function insertMention(text, cursor, mention,
|
|
103398
|
-
if (/\s/.test(
|
|
103399
|
-
const inserted = `@${mention.explicit ? "!" : ""}${
|
|
102489
|
+
function insertMention(text, cursor, mention, path13) {
|
|
102490
|
+
if (/\s/.test(path13)) return { text, cursor };
|
|
102491
|
+
const inserted = `@${mention.explicit ? "!" : ""}${path13}`;
|
|
103400
102492
|
const head = text.slice(0, mention.start) + inserted;
|
|
103401
102493
|
const next = text[cursor];
|
|
103402
102494
|
if (next !== void 0 && /\s/.test(next)) return { text: head + text.slice(cursor), cursor: head.length + 1 };
|
|
@@ -103766,20 +102858,20 @@ var init_history_store = __esm({
|
|
|
103766
102858
|
});
|
|
103767
102859
|
|
|
103768
102860
|
// src/editor/history-file-io.ts
|
|
103769
|
-
import { chmod as chmod3, mkdir as mkdir3, readFile as
|
|
103770
|
-
import
|
|
102861
|
+
import { chmod as chmod3, mkdir as mkdir3, readFile as readFile4, rename as rename3, writeFile as writeFile3 } from "node:fs/promises";
|
|
102862
|
+
import path5 from "node:path";
|
|
103771
102863
|
function createFileHistoryIO(file) {
|
|
103772
102864
|
return {
|
|
103773
102865
|
async read() {
|
|
103774
102866
|
try {
|
|
103775
|
-
return await
|
|
102867
|
+
return await readFile4(file, "utf8");
|
|
103776
102868
|
} catch (err) {
|
|
103777
102869
|
if (err.code === "ENOENT") return null;
|
|
103778
102870
|
throw err;
|
|
103779
102871
|
}
|
|
103780
102872
|
},
|
|
103781
102873
|
async write(content) {
|
|
103782
|
-
await mkdir3(
|
|
102874
|
+
await mkdir3(path5.dirname(file), { recursive: true, mode: 448 });
|
|
103783
102875
|
const tmp = `${file}.${process.pid}.tmp`;
|
|
103784
102876
|
await writeFile3(tmp, content, { mode: 384 });
|
|
103785
102877
|
await chmod3(tmp, 384);
|
|
@@ -103941,10 +103033,10 @@ var init_session_commands = __esm({
|
|
|
103941
103033
|
});
|
|
103942
103034
|
|
|
103943
103035
|
// src/local-toolset.ts
|
|
103944
|
-
import * as
|
|
103945
|
-
import * as
|
|
103036
|
+
import * as os4 from "node:os";
|
|
103037
|
+
import * as path6 from "node:path";
|
|
103946
103038
|
function createLocalToolset(opts) {
|
|
103947
|
-
const auditFile = opts.auditFile ??
|
|
103039
|
+
const auditFile = opts.auditFile ?? path6.join(os4.homedir(), ".spacecode", "audit.jsonl");
|
|
103948
103040
|
const host = guardHost(new LocalHost(opts.cwd), {
|
|
103949
103041
|
audit: new FileAuditLog(auditFile),
|
|
103950
103042
|
mode: opts.yolo ? "yolo" : "enforce",
|
|
@@ -103974,7 +103066,7 @@ var init_local_toolset = __esm({
|
|
|
103974
103066
|
|
|
103975
103067
|
// src/project-card-scan.ts
|
|
103976
103068
|
import { execFile as execFile3 } from "node:child_process";
|
|
103977
|
-
import { readdir as readdir3, readFile as
|
|
103069
|
+
import { readdir as readdir3, readFile as readFile5 } from "node:fs/promises";
|
|
103978
103070
|
import { join as join7 } from "node:path";
|
|
103979
103071
|
function digestLines(md) {
|
|
103980
103072
|
return md.split("\n").map((l) => l.trim()).filter((l) => l && !/^#\s/.test(l) && !/^[|\s:-]+$/.test(l) && !/^\|\s*(area|task|priority|date|issue|id)\b/i.test(l)).slice(0, 12);
|
|
@@ -104075,13 +103167,13 @@ var init_project_card_scan = __esm({
|
|
|
104075
103167
|
MAX_BACKLOG = 6;
|
|
104076
103168
|
DIGEST_FILES = ["CLAUDE.md", "TODO.md", "BUGS.md", "ISSUES.md", "CHANGELOG.md", "README.md"];
|
|
104077
103169
|
nodeScanIo = {
|
|
104078
|
-
async readdir(
|
|
104079
|
-
const entries = await readdir3(
|
|
103170
|
+
async readdir(path13) {
|
|
103171
|
+
const entries = await readdir3(path13, { withFileTypes: true });
|
|
104080
103172
|
return entries.map((e) => ({ name: e.name, isDir: e.isDirectory() }));
|
|
104081
103173
|
},
|
|
104082
|
-
async readFile(
|
|
103174
|
+
async readFile(path13) {
|
|
104083
103175
|
try {
|
|
104084
|
-
return await
|
|
103176
|
+
return await readFile5(path13, "utf8");
|
|
104085
103177
|
} catch (err) {
|
|
104086
103178
|
if (err.code === "ENOENT") return null;
|
|
104087
103179
|
throw err;
|
|
@@ -104434,9 +103526,9 @@ var init_mention_resolver = __esm({
|
|
|
104434
103526
|
|
|
104435
103527
|
// src/editor/node-mention-fs.ts
|
|
104436
103528
|
import { open as open2, realpath as realpath2, stat as stat2 } from "node:fs/promises";
|
|
104437
|
-
import
|
|
103529
|
+
import path7 from "node:path";
|
|
104438
103530
|
function isInside(root, target) {
|
|
104439
|
-
return target === root || target.startsWith(root.endsWith(
|
|
103531
|
+
return target === root || target.startsWith(root.endsWith(path7.sep) ? root : root + path7.sep);
|
|
104440
103532
|
}
|
|
104441
103533
|
function createNodeMentionFS(cwd2) {
|
|
104442
103534
|
let rootPromise;
|
|
@@ -104481,10 +103573,10 @@ var init_node_mention_fs = __esm({
|
|
|
104481
103573
|
|
|
104482
103574
|
// src/editor/node-read-dir.ts
|
|
104483
103575
|
import { readdir as readdir4 } from "node:fs/promises";
|
|
104484
|
-
import
|
|
103576
|
+
import path8 from "node:path";
|
|
104485
103577
|
function createNodeReadDir(cwd2) {
|
|
104486
103578
|
return async (rel) => {
|
|
104487
|
-
const entries = await readdir4(
|
|
103579
|
+
const entries = await readdir4(path8.join(cwd2, rel), { withFileTypes: true });
|
|
104488
103580
|
return entries.map((e) => ({ name: e.name, isFile: e.isFile(), isDirectory: e.isDirectory() }));
|
|
104489
103581
|
};
|
|
104490
103582
|
}
|
|
@@ -104495,11 +103587,11 @@ var init_node_read_dir = __esm({
|
|
|
104495
103587
|
});
|
|
104496
103588
|
|
|
104497
103589
|
// src/editor/node-mentions.ts
|
|
104498
|
-
import { readFile as
|
|
104499
|
-
import
|
|
103590
|
+
import { readFile as readFile6 } from "node:fs/promises";
|
|
103591
|
+
import path9 from "node:path";
|
|
104500
103592
|
async function readGitignore(cwd2) {
|
|
104501
103593
|
try {
|
|
104502
|
-
return createGitignoreMatcher(await
|
|
103594
|
+
return createGitignoreMatcher(await readFile6(path9.join(cwd2, ".gitignore"), "utf8"));
|
|
104503
103595
|
} catch (err) {
|
|
104504
103596
|
if (err.code === "ENOENT") return () => false;
|
|
104505
103597
|
throw err;
|
|
@@ -104609,7 +103701,7 @@ var init_recording_chunks = __esm({
|
|
|
104609
103701
|
|
|
104610
103702
|
// src/recording/recorder.ts
|
|
104611
103703
|
import { createHash as createHash4 } from "node:crypto";
|
|
104612
|
-
import { mkdir as mkdir4, readFile as
|
|
103704
|
+
import { mkdir as mkdir4, readFile as readFile7, rename as rename4, rm as rm2, writeFile as writeFile4, readdir as readdir5, stat as stat3 } from "node:fs/promises";
|
|
104613
103705
|
import { join as join9 } from "node:path";
|
|
104614
103706
|
function projectHash(root) {
|
|
104615
103707
|
return createHash4("sha256").update(root).digest("hex");
|
|
@@ -104633,7 +103725,7 @@ function createRecorder(deps) {
|
|
|
104633
103725
|
if (cached3) return cached3;
|
|
104634
103726
|
let st;
|
|
104635
103727
|
try {
|
|
104636
|
-
st = JSON.parse(await
|
|
103728
|
+
st = JSON.parse(await readFile7(stateFile(sessionId), "utf8"));
|
|
104637
103729
|
} catch (e) {
|
|
104638
103730
|
if (e.code !== "ENOENT") {
|
|
104639
103731
|
const bad = join9(deps.dir, "bad");
|
|
@@ -104828,15 +103920,15 @@ var init_recorder = __esm({
|
|
|
104828
103920
|
});
|
|
104829
103921
|
|
|
104830
103922
|
// src/recording/recording-runtime.ts
|
|
104831
|
-
import { mkdir as mkdir5, readFile as
|
|
103923
|
+
import { mkdir as mkdir5, readFile as readFile8, writeFile as writeFile5, stat as stat4 } from "node:fs/promises";
|
|
104832
103924
|
import { join as join10, sep as sep2 } from "node:path";
|
|
104833
103925
|
function matchesExclude(pattern, root) {
|
|
104834
103926
|
const re3 = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, "\0").replace(/\*/g, `[^${sep2 === "\\" ? "\\\\" : "/"}]*`).replace(/\u0000/g, ".*");
|
|
104835
103927
|
return new RegExp(`^${re3}(?:[/\\\\].*)?$`).test(root);
|
|
104836
103928
|
}
|
|
104837
|
-
async function exists(
|
|
103929
|
+
async function exists(path13) {
|
|
104838
103930
|
try {
|
|
104839
|
-
await stat4(
|
|
103931
|
+
await stat4(path13);
|
|
104840
103932
|
return true;
|
|
104841
103933
|
} catch (e) {
|
|
104842
103934
|
if (e.code === "ENOENT") return false;
|
|
@@ -104853,7 +103945,7 @@ function createRecordingRuntime(deps) {
|
|
|
104853
103945
|
if (deps.env.SPACECODE_RECORD?.trim().toLowerCase() === "off") return "SPACECODE_RECORD=off";
|
|
104854
103946
|
let cfg = {};
|
|
104855
103947
|
try {
|
|
104856
|
-
cfg = JSON.parse(await
|
|
103948
|
+
cfg = JSON.parse(await readFile8(join10(deps.configDir, "config.json"), "utf8"));
|
|
104857
103949
|
} catch (e) {
|
|
104858
103950
|
if (e.code !== "ENOENT") return `~/.spacecode/config.json is unreadable (${e instanceof Error ? e.message : String(e)}); fix it to record`;
|
|
104859
103951
|
}
|
|
@@ -105001,12 +104093,37 @@ var init_skills_command = __esm({
|
|
|
105001
104093
|
}
|
|
105002
104094
|
});
|
|
105003
104095
|
|
|
105004
|
-
// src/
|
|
105005
|
-
import { readFile as
|
|
104096
|
+
// src/mcp/tool-cache-file.ts
|
|
104097
|
+
import { readFile as readFile9 } from "node:fs/promises";
|
|
105006
104098
|
import { join as join11 } from "node:path";
|
|
105007
|
-
|
|
104099
|
+
function nodeToolCache(configDir) {
|
|
104100
|
+
const path13 = join11(configDir, "mcp-tools-cache.json");
|
|
104101
|
+
return {
|
|
104102
|
+
read: async () => {
|
|
104103
|
+
try {
|
|
104104
|
+
return await readFile9(path13, "utf8");
|
|
104105
|
+
} catch (err) {
|
|
104106
|
+
if (err.code === "ENOENT") return null;
|
|
104107
|
+
throw err;
|
|
104108
|
+
}
|
|
104109
|
+
},
|
|
104110
|
+
write: (text) => writeTextAtomic(path13, text),
|
|
104111
|
+
hash: sha256
|
|
104112
|
+
};
|
|
104113
|
+
}
|
|
104114
|
+
var init_tool_cache_file = __esm({
|
|
104115
|
+
"src/mcp/tool-cache-file.ts"() {
|
|
104116
|
+
"use strict";
|
|
104117
|
+
init_import_gate();
|
|
104118
|
+
}
|
|
104119
|
+
});
|
|
104120
|
+
|
|
104121
|
+
// src/permission-rules-loader.ts
|
|
104122
|
+
import { readFile as readFile10 } from "node:fs/promises";
|
|
104123
|
+
import { join as join12 } from "node:path";
|
|
104124
|
+
async function readOrNull2(path13) {
|
|
105008
104125
|
try {
|
|
105009
|
-
return await
|
|
104126
|
+
return await readFile10(path13, "utf8");
|
|
105010
104127
|
} catch (err) {
|
|
105011
104128
|
if (err.code === "ENOENT") return null;
|
|
105012
104129
|
throw err;
|
|
@@ -105014,7 +104131,7 @@ async function readOrNull2(path12) {
|
|
|
105014
104131
|
}
|
|
105015
104132
|
async function loadPermissionRules(deps) {
|
|
105016
104133
|
const none = (line) => ({ rules: void 0, defaultMode: void 0, statusLines: [line] });
|
|
105017
|
-
const configPath =
|
|
104134
|
+
const configPath = join12(deps.configDir, "imports.json");
|
|
105018
104135
|
try {
|
|
105019
104136
|
const loaded = parseImportsConfig(await readOrNull2(configPath), configPath);
|
|
105020
104137
|
if (loaded.error) return none("permission rules: off (imports.json unreadable)");
|
|
@@ -105023,13 +104140,13 @@ async function loadPermissionRules(deps) {
|
|
|
105023
104140
|
return none(`permission rules: off (imports.json not read: ${err instanceof Error ? err.message : String(err)})`);
|
|
105024
104141
|
}
|
|
105025
104142
|
const lines2 = [];
|
|
105026
|
-
const files = [{ scope: "user", path:
|
|
104143
|
+
const files = [{ scope: "user", path: join12(deps.homeDir, ".claude", "settings.json") }];
|
|
105027
104144
|
try {
|
|
105028
104145
|
const sources2 = await readRepoSources(deps.cwd);
|
|
105029
104146
|
if (sources2[".claude/settings.json"] !== void 0 || sources2[".claude/settings.local.json"] !== void 0) {
|
|
105030
104147
|
const status = await createNodeTrustStore(deps.configDir).repoStatus(deps.cwd, sources2);
|
|
105031
104148
|
if (status === "trusted" || status === "session") {
|
|
105032
|
-
files.push({ scope: "repo", path:
|
|
104149
|
+
files.push({ scope: "repo", path: join12(deps.cwd, ".claude", "settings.json") }, { scope: "local", path: join12(deps.cwd, ".claude", "settings.local.json") });
|
|
105033
104150
|
} else lines2.push(`permission rules: repo settings NOT loaded (${status === "never" ? "marked never trust" : "untrusted repo"})`);
|
|
105034
104151
|
}
|
|
105035
104152
|
} catch (err) {
|
|
@@ -105073,7 +104190,7 @@ var init_permission_rules_loader = __esm({
|
|
|
105073
104190
|
// src/import-reload.ts
|
|
105074
104191
|
async function checkRepoSources(deps, loaded) {
|
|
105075
104192
|
const files = await readRepoSources(deps.cwd);
|
|
105076
|
-
const current = fingerprintSources(files,
|
|
104193
|
+
const current = fingerprintSources(files, sha256);
|
|
105077
104194
|
const status = Object.keys(files).length === 0 ? "untrusted" : await createNodeTrustStore(deps.configDir).repoStatus(deps.cwd, files);
|
|
105078
104195
|
return { current, changes: diffSources(loaded, current), trusted: status === "trusted" || status === "session" };
|
|
105079
104196
|
}
|
|
@@ -105094,7 +104211,7 @@ async function describeReload(deps, loaded) {
|
|
|
105094
104211
|
async function trustCurrentSources(deps) {
|
|
105095
104212
|
const files = await readRepoSources(deps.cwd);
|
|
105096
104213
|
await createNodeTrustStore(deps.configDir).setRepo(deps.cwd, files, "trusted");
|
|
105097
|
-
return fingerprintSources(files,
|
|
104214
|
+
return fingerprintSources(files, sha256);
|
|
105098
104215
|
}
|
|
105099
104216
|
var init_import_reload = __esm({
|
|
105100
104217
|
"src/import-reload.ts"() {
|
|
@@ -105106,7 +104223,7 @@ var init_import_reload = __esm({
|
|
|
105106
104223
|
});
|
|
105107
104224
|
|
|
105108
104225
|
// src/command-loader.ts
|
|
105109
|
-
import { join as
|
|
104226
|
+
import { join as join13 } from "node:path";
|
|
105110
104227
|
async function userCommands(deps, lines2) {
|
|
105111
104228
|
try {
|
|
105112
104229
|
const apiKey = await resolveApiKey({ env: deps.env, readFile: deps.readFile, homedir: deps.homedir });
|
|
@@ -105161,7 +104278,7 @@ async function repoCommands(deps, lines2) {
|
|
|
105161
104278
|
}
|
|
105162
104279
|
}
|
|
105163
104280
|
async function loadCommands(deps) {
|
|
105164
|
-
const configPath =
|
|
104281
|
+
const configPath = join13(deps.configDir, "imports.json");
|
|
105165
104282
|
try {
|
|
105166
104283
|
const loaded = parseImportsConfig(await deps.readFile(configPath).catch((err) => {
|
|
105167
104284
|
if (err.code === "ENOENT") return null;
|
|
@@ -105194,9 +104311,9 @@ var init_command_loader = __esm({
|
|
|
105194
104311
|
});
|
|
105195
104312
|
|
|
105196
104313
|
// src/useReplController.ts
|
|
105197
|
-
import { readFile as
|
|
104314
|
+
import { readFile as readFile11, writeFile as writeFile6, mkdir as mkdir6 } from "node:fs/promises";
|
|
105198
104315
|
import { homedir as homedir2 } from "node:os";
|
|
105199
|
-
import { join as
|
|
104316
|
+
import { join as join14 } from "node:path";
|
|
105200
104317
|
import { execFile as execFile4 } from "node:child_process";
|
|
105201
104318
|
function useReplController({ repoName: repoName2, yolo: yolo2, mentions: mentionsOpt, sessions, resumeLatest: resumeLatest2, onViewTurns, onViewResume, isEscOwned }) {
|
|
105202
104319
|
const { exit } = use_app_default();
|
|
@@ -105219,7 +104336,7 @@ function useReplController({ repoName: repoName2, yolo: yolo2, mentions: mention
|
|
|
105219
104336
|
}, [turnStartedAt]);
|
|
105220
104337
|
const projectRoot = (0, import_react30.useMemo)(() => process.cwd(), []);
|
|
105221
104338
|
const promptTemplates = (0, import_react30.useCallback)(
|
|
105222
|
-
() => loadPromptTemplates({ env: process.env, readFile: (p) =>
|
|
104339
|
+
() => loadPromptTemplates({ env: process.env, readFile: (p) => readFile11(p, "utf-8"), writeFile: (p, d) => writeFile6(p, d, "utf-8"), mkdir: (p) => mkdir6(p, { recursive: true }).then(() => void 0), homedir: homedir2, fetch }),
|
|
105223
104340
|
[]
|
|
105224
104341
|
);
|
|
105225
104342
|
(0, import_react30.useEffect)(() => {
|
|
@@ -105235,11 +104352,11 @@ function useReplController({ repoName: repoName2, yolo: yolo2, mentions: mention
|
|
|
105235
104352
|
const recordingRef = (0, import_react30.useRef)(null);
|
|
105236
104353
|
const recording = () => recordingRef.current ??= createRecordingRuntime({
|
|
105237
104354
|
env: process.env,
|
|
105238
|
-
configDir: process.env.SPACECODE_CONFIG_DIR?.trim() ||
|
|
104355
|
+
configDir: process.env.SPACECODE_CONFIG_DIR?.trim() || join14(homedir2(), ".spacecode"),
|
|
105239
104356
|
projectRoot,
|
|
105240
104357
|
fetch,
|
|
105241
104358
|
baseUrl: backendUrl(process.env),
|
|
105242
|
-
resolveApiKey: () => resolveApiKey({ env: process.env, readFile: (p) =>
|
|
104359
|
+
resolveApiKey: () => resolveApiKey({ env: process.env, readFile: (p) => readFile11(p, "utf-8"), homedir: homedir2 }).catch(() => null),
|
|
105243
104360
|
log: (m2) => console.error(`[agent-ui] ${m2}`)
|
|
105244
104361
|
});
|
|
105245
104362
|
const resumeListRef = (0, import_react30.useRef)([]);
|
|
@@ -105263,10 +104380,10 @@ function useReplController({ repoName: repoName2, yolo: yolo2, mentions: mention
|
|
|
105263
104380
|
(0, import_react30.useEffect)(() => {
|
|
105264
104381
|
let live2 = true;
|
|
105265
104382
|
loadCommands({
|
|
105266
|
-
configDir: process.env.SPACECODE_CONFIG_DIR?.trim() ||
|
|
104383
|
+
configDir: process.env.SPACECODE_CONFIG_DIR?.trim() || join14(homedir2(), ".spacecode"),
|
|
105267
104384
|
cwd: process.cwd(),
|
|
105268
104385
|
env: process.env,
|
|
105269
|
-
readFile: (p) =>
|
|
104386
|
+
readFile: (p) => readFile11(p, "utf-8"),
|
|
105270
104387
|
homedir: homedir2,
|
|
105271
104388
|
fetch,
|
|
105272
104389
|
builtins: SLASH_COMMANDS.map((c) => c.name)
|
|
@@ -105484,7 +104601,7 @@ function useReplController({ repoName: repoName2, yolo: yolo2, mentions: mention
|
|
|
105484
104601
|
notice(formatContextReport({ contextTokens: estimateTotalTokens("", history), maxContextTokens: DEFAULT_MAX_CONTEXT_TOKENS }));
|
|
105485
104602
|
return;
|
|
105486
104603
|
case "import": {
|
|
105487
|
-
const configDir = process.env.SPACECODE_CONFIG_DIR?.trim() ||
|
|
104604
|
+
const configDir = process.env.SPACECODE_CONFIG_DIR?.trim() || join14(homedir2(), ".spacecode");
|
|
105488
104605
|
const deps = { configDir, cwd: process.cwd() };
|
|
105489
104606
|
const [sub, ...rest] = slash.args.split(/\s+/).filter(Boolean);
|
|
105490
104607
|
if (sub !== "reload" || rest.length > 1 || rest[0] !== void 0 && rest[0] !== "trust") {
|
|
@@ -105492,12 +104609,12 @@ function useReplController({ repoName: repoName2, yolo: yolo2, mentions: mention
|
|
|
105492
104609
|
return;
|
|
105493
104610
|
}
|
|
105494
104611
|
try {
|
|
105495
|
-
const baseline = loadedSourcesRef.current ?? fingerprintSources(await readRepoSources(process.cwd()),
|
|
104612
|
+
const baseline = loadedSourcesRef.current ?? fingerprintSources(await readRepoSources(process.cwd()), sha256);
|
|
105496
104613
|
if (rest[0] === "trust") {
|
|
105497
104614
|
loadedSourcesRef.current = await trustCurrentSources(deps);
|
|
105498
104615
|
const loaded = await loadPermissionRules({ configDir, homeDir: homedir2(), cwd: process.cwd() });
|
|
105499
104616
|
rulesRef.current = loaded.rules;
|
|
105500
|
-
const cmds = await loadCommands({ configDir, cwd: process.cwd(), env: process.env, readFile: (p) =>
|
|
104617
|
+
const cmds = await loadCommands({ configDir, cwd: process.cwd(), env: process.env, readFile: (p) => readFile11(p, "utf-8"), homedir: homedir2, fetch, builtins: SLASH_COMMANDS.map((c) => c.name) });
|
|
105501
104618
|
setCommandsLoaded(cmds);
|
|
105502
104619
|
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"));
|
|
105503
104620
|
} else notice((await describeReload(deps, baseline)).join("\n"));
|
|
@@ -105577,7 +104694,7 @@ function useReplController({ repoName: repoName2, yolo: yolo2, mentions: mention
|
|
|
105577
104694
|
}
|
|
105578
104695
|
setIsRunning(true);
|
|
105579
104696
|
try {
|
|
105580
|
-
const apiKey2 = await resolveApiKey({ env: process.env, readFile: (p) =>
|
|
104697
|
+
const apiKey2 = await resolveApiKey({ env: process.env, readFile: (p) => readFile11(p, "utf-8"), homedir: homedir2 });
|
|
105581
104698
|
if (!apiKey2) {
|
|
105582
104699
|
notice(MISSING_KEY_MESSAGE);
|
|
105583
104700
|
return;
|
|
@@ -105599,7 +104716,7 @@ function useReplController({ repoName: repoName2, yolo: yolo2, mentions: mention
|
|
|
105599
104716
|
}
|
|
105600
104717
|
case "model": {
|
|
105601
104718
|
try {
|
|
105602
|
-
const apiKey2 = await resolveApiKey({ env: process.env, readFile: (p) =>
|
|
104719
|
+
const apiKey2 = await resolveApiKey({ env: process.env, readFile: (p) => readFile11(p, "utf-8"), homedir: homedir2 });
|
|
105603
104720
|
if (!apiKey2) {
|
|
105604
104721
|
notice(MISSING_KEY_MESSAGE);
|
|
105605
104722
|
return;
|
|
@@ -105634,7 +104751,7 @@ ${list2.map((c) => ` /${c.name} (${c.scope})${c.description ? ` ${c.descripti
|
|
|
105634
104751
|
}
|
|
105635
104752
|
case "skills": {
|
|
105636
104753
|
const state = profileRef.current;
|
|
105637
|
-
const apiKey2 = await resolveApiKey({ env: process.env, readFile: (p) =>
|
|
104754
|
+
const apiKey2 = await resolveApiKey({ env: process.env, readFile: (p) => readFile11(p, "utf-8"), homedir: homedir2 }).catch(() => null);
|
|
105638
104755
|
const client = apiKey2 ? createProfileClient({ baseUrl: backendUrl(process.env), apiKey: apiKey2, fetch }) : null;
|
|
105639
104756
|
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)"] }));
|
|
105640
104757
|
return;
|
|
@@ -105656,7 +104773,7 @@ ${list2.map((c) => ` /${c.name} (${c.scope})${c.description ? ` ${c.descripti
|
|
|
105656
104773
|
try {
|
|
105657
104774
|
apiKey = await resolveApiKey({
|
|
105658
104775
|
env: process.env,
|
|
105659
|
-
readFile: (p) =>
|
|
104776
|
+
readFile: (p) => readFile11(p, "utf-8"),
|
|
105660
104777
|
homedir: homedir2
|
|
105661
104778
|
});
|
|
105662
104779
|
} catch (err) {
|
|
@@ -105692,7 +104809,7 @@ ${list2.map((c) => ` /${c.name} (${c.scope})${c.description ? ` ${c.descripti
|
|
|
105692
104809
|
let keepForRetry = false;
|
|
105693
104810
|
try {
|
|
105694
104811
|
if (loadedSourcesRef.current) {
|
|
105695
|
-
const configDir = process.env.SPACECODE_CONFIG_DIR?.trim() ||
|
|
104812
|
+
const configDir = process.env.SPACECODE_CONFIG_DIR?.trim() || join14(homedir2(), ".spacecode");
|
|
105696
104813
|
try {
|
|
105697
104814
|
const check = await checkRepoSources({ configDir, cwd: process.cwd() }, loadedSourcesRef.current);
|
|
105698
104815
|
if (check.changes.changed) {
|
|
@@ -105709,7 +104826,7 @@ ${list2.map((c) => ` /${c.name} (${c.scope})${c.description ? ` ${c.descripti
|
|
|
105709
104826
|
}
|
|
105710
104827
|
profilePromiseRef.current ??= prepareProfile({
|
|
105711
104828
|
env: process.env,
|
|
105712
|
-
readFile: (p) =>
|
|
104829
|
+
readFile: (p) => readFile11(p, "utf-8"),
|
|
105713
104830
|
writeFile: (p, d) => writeFile6(p, d, { encoding: "utf-8", mode: 384 }),
|
|
105714
104831
|
mkdir: async (p) => {
|
|
105715
104832
|
await mkdir6(p, { recursive: true });
|
|
@@ -105719,8 +104836,8 @@ ${list2.map((c) => ` /${c.name} (${c.scope})${c.description ? ` ${c.descripti
|
|
|
105719
104836
|
cwd: process.cwd(),
|
|
105720
104837
|
gitRoot: gitTopLevel,
|
|
105721
104838
|
importGate: async () => {
|
|
105722
|
-
const configDir = process.env.SPACECODE_CONFIG_DIR?.trim() ||
|
|
105723
|
-
const fingerprint = fingerprintSources(await readRepoSources(process.cwd()),
|
|
104839
|
+
const configDir = process.env.SPACECODE_CONFIG_DIR?.trim() || join14(homedir2(), ".spacecode");
|
|
104840
|
+
const fingerprint = fingerprintSources(await readRepoSources(process.cwd()), sha256);
|
|
105724
104841
|
const gate = await importGateStatus({ configDir, cwd: process.cwd(), interactive: Boolean(process.stdin.isTTY) });
|
|
105725
104842
|
const loaded = await loadPermissionRules({ configDir, homeDir: homedir2(), cwd: process.cwd() });
|
|
105726
104843
|
rulesRef.current = loaded.rules;
|
|
@@ -105731,7 +104848,7 @@ ${list2.map((c) => ` /${c.name} (${c.scope})${c.description ? ` ${c.descripti
|
|
|
105731
104848
|
});
|
|
105732
104849
|
profileRef.current = await profilePromiseRef.current;
|
|
105733
104850
|
if (mcpRef.current === null && (profileRef.current?.mcpDefs?.length ?? 0) > 0) {
|
|
105734
|
-
const configDir = process.env.SPACECODE_CONFIG_DIR?.trim() ||
|
|
104851
|
+
const configDir = process.env.SPACECODE_CONFIG_DIR?.trim() || join14(homedir2(), ".spacecode");
|
|
105735
104852
|
const trust = createNodeTrustStore(configDir);
|
|
105736
104853
|
const manager = new McpManager({
|
|
105737
104854
|
defs: profileRef.current.mcpDefs,
|
|
@@ -105741,10 +104858,12 @@ ${list2.map((c) => ` /${c.name} (${c.scope})${c.description ? ` ${c.descripti
|
|
|
105741
104858
|
...rulesRef.current ? { rules: rulesRef.current } : {},
|
|
105742
104859
|
permissions,
|
|
105743
104860
|
confirm: askUser,
|
|
105744
|
-
audit: new FileAuditLog(
|
|
105745
|
-
clientVersion: process.env.SPACECODE_VERSION ?? "0"
|
|
104861
|
+
audit: new FileAuditLog(join14(homedir2(), ".spacecode", "audit.jsonl")),
|
|
104862
|
+
clientVersion: process.env.SPACECODE_VERSION ?? "0",
|
|
104863
|
+
toolCache: nodeToolCache(configDir)
|
|
105746
104864
|
});
|
|
105747
104865
|
mcpRef.current = manager;
|
|
104866
|
+
await manager.prime();
|
|
105748
104867
|
void manager.start();
|
|
105749
104868
|
}
|
|
105750
104869
|
const toolset = createToolset();
|
|
@@ -105882,6 +105001,7 @@ var init_useReplController = __esm({
|
|
|
105882
105001
|
init_skills_command();
|
|
105883
105002
|
init_src();
|
|
105884
105003
|
init_mcp_manager();
|
|
105004
|
+
init_tool_cache_file();
|
|
105885
105005
|
init_import_gate();
|
|
105886
105006
|
init_permission_rules_loader();
|
|
105887
105007
|
init_import_reload();
|
|
@@ -106013,12 +105133,12 @@ var init_TurnViewer = __esm({
|
|
|
106013
105133
|
});
|
|
106014
105134
|
|
|
106015
105135
|
// src/render/resume-rows.ts
|
|
106016
|
-
import
|
|
105136
|
+
import path10 from "node:path";
|
|
106017
105137
|
function orderSessions(sessions, projectRoot) {
|
|
106018
105138
|
return [...sessions.filter((s) => s.projectRoot === projectRoot), ...sessions.filter((s) => s.projectRoot !== projectRoot)];
|
|
106019
105139
|
}
|
|
106020
105140
|
function formatResumeRow(meta, now, columns, sep3 = "\xB7", ellipsis = "\u2026") {
|
|
106021
|
-
const project =
|
|
105141
|
+
const project = path10.basename(meta.projectRoot) || meta.projectRoot;
|
|
106022
105142
|
const tail = [meta.turns !== void 0 ? `${meta.turns} turn${meta.turns === 1 ? "" : "s"}` : null, formatCost(estimateCostUsd(meta.tokens.input, meta.tokens.output))].filter(Boolean).join(` ${sep3} `);
|
|
106023
105143
|
const head = `${formatAge(meta.updatedAt, now)} ${sep3} ${project} ${sep3} `;
|
|
106024
105144
|
const fixed = head.length + 2 + ` ${sep3} ${tail}`.length;
|
|
@@ -106163,14 +105283,14 @@ var App_exports = {};
|
|
|
106163
105283
|
__export(App_exports, {
|
|
106164
105284
|
App: () => App2
|
|
106165
105285
|
});
|
|
106166
|
-
import
|
|
106167
|
-
import
|
|
105286
|
+
import os5 from "node:os";
|
|
105287
|
+
import path11 from "node:path";
|
|
106168
105288
|
function App2({ repoName: repoName2, onboardingMode: onboardingMode2, launchCount: launchCount2, yolo: yolo2, history, sessions, resumeLatest: resumeLatest2, git: gitRunner, interactivePicker = true, fullscreen: fullscreen2 = false }) {
|
|
106169
105289
|
const promptHistory = (0, import_react34.useMemo)(
|
|
106170
|
-
() => history ?? new PromptHistory(createFileHistoryIO(
|
|
105290
|
+
() => history ?? new PromptHistory(createFileHistoryIO(path11.join(os5.homedir(), ".spacecode", "history"))),
|
|
106171
105291
|
[history]
|
|
106172
105292
|
);
|
|
106173
|
-
const sessionStore = (0, import_react34.useMemo)(() => sessions ?? new SessionStore(
|
|
105293
|
+
const sessionStore = (0, import_react34.useMemo)(() => sessions ?? new SessionStore(path11.join(os5.homedir(), ".spacecode", "sessions")), [sessions]);
|
|
106174
105294
|
const runGit = (0, import_react34.useMemo)(() => gitRunner ?? createNodeGitRunner(process.cwd()), [gitRunner]);
|
|
106175
105295
|
const startedAtRef = (0, import_react34.useRef)(Date.now());
|
|
106176
105296
|
const [viewingRaw, setViewing] = (0, import_react34.useState)(false);
|
|
@@ -106260,7 +105380,7 @@ function App2({ repoName: repoName2, onboardingMode: onboardingMode2, launchCoun
|
|
|
106260
105380
|
viewing && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(TurnViewer, { turns: viewerTurns, steps: turnSteps, rows: rows - 2, columns, onClose: () => setViewing(false), onEscape: swallowEsc }),
|
|
106261
105381
|
picking && resumeFound && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ResumePicker, { sessions: resumeFound.sessions, errors: resumeFound.errors, projectRoot: process.cwd(), now: /* @__PURE__ */ new Date(), rows: rows - 2, columns, preview: resume.preview, onResume: resume.load, onDelete: resume.remove, onClose: () => setResumeFound(null), onEscape: swallowEsc }),
|
|
106262
105382
|
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(Box_default, { flexDirection: "column", flexGrow: 1, display: hidden ? "none" : "flex", children: [
|
|
106263
|
-
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Box_default, { flexShrink: 0, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Header, { project: repoName2 ??
|
|
105383
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Box_default, { flexShrink: 0, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Header, { project: repoName2 ?? path11.basename(process.cwd()), git: gitState, turns: turns.length, elapsedMs: Date.now() - startedAtRef.current, columns }) }),
|
|
106264
105384
|
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(Transcript, { scroll, onMeasure, children: [
|
|
106265
105385
|
notices.map((n, i) => typeof n === "string" ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Text, { ...paint("action"), children: n }, i) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ErrorBlock, { view: n.error }, i)),
|
|
106266
105386
|
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(WelcomeBanner, { repoName: repoName2, onboardingMode: onboardingMode2, launchCount: launchCount2 }),
|
|
@@ -106290,7 +105410,7 @@ function App2({ repoName: repoName2, onboardingMode: onboardingMode2, launchCoun
|
|
|
106290
105410
|
] });
|
|
106291
105411
|
}
|
|
106292
105412
|
return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(Box_default, { flexDirection: "column", children: [
|
|
106293
|
-
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Header, { project: repoName2 ??
|
|
105413
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Header, { project: repoName2 ?? path11.basename(process.cwd()), git: gitState, turns: turns.length, elapsedMs: Date.now() - startedAtRef.current, columns }),
|
|
106294
105414
|
notices.map((n, i) => typeof n === "string" ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Text, { ...paint("action"), children: n }, i) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ErrorBlock, { view: n.error }, i)),
|
|
106295
105415
|
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Static, { items: [BANNER, ...turns], children: (turn) => typeof turn === "symbol" ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(WelcomeBanner, { repoName: repoName2, onboardingMode: onboardingMode2, launchCount: launchCount2 }, "banner") : /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(Box_default, { flexDirection: "column", children: [
|
|
106296
105416
|
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(Text, { ...paint("secondary"), children: [
|
|
@@ -106384,8 +105504,8 @@ init_color_policy();
|
|
|
106384
105504
|
applyColorPolicy(process.env, Boolean(process.stdout.isTTY));
|
|
106385
105505
|
|
|
106386
105506
|
// src/index.tsx
|
|
106387
|
-
import
|
|
106388
|
-
import
|
|
105507
|
+
import os6 from "node:os";
|
|
105508
|
+
import path12 from "node:path";
|
|
106389
105509
|
import fs5 from "node:fs";
|
|
106390
105510
|
import util2 from "node:util";
|
|
106391
105511
|
|
|
@@ -106471,7 +105591,7 @@ function clearOnResize(stdout) {
|
|
|
106471
105591
|
var import_jsx_runtime14 = __toESM(require_jsx_runtime(), 1);
|
|
106472
105592
|
if (process.argv[2] === "import-claude") {
|
|
106473
105593
|
const { runImportClaude: runImportClaude2 } = await Promise.resolve().then(() => (init_import_claude(), import_claude_exports));
|
|
106474
|
-
const { readFile:
|
|
105594
|
+
const { readFile: readFile12, readdir: readdir6 } = await import("node:fs/promises");
|
|
106475
105595
|
const { execFile: execFile5 } = await import("node:child_process");
|
|
106476
105596
|
const gitRoot = () => new Promise((resolve2) => {
|
|
106477
105597
|
execFile5("git", ["rev-parse", "--show-toplevel"], { cwd: process.cwd() }, (err, stdout) => resolve2(err ? null : stdout.trim() || null));
|
|
@@ -106479,9 +105599,9 @@ if (process.argv[2] === "import-claude") {
|
|
|
106479
105599
|
const code = await runImportClaude2({
|
|
106480
105600
|
argv: process.argv.slice(2),
|
|
106481
105601
|
env: process.env,
|
|
106482
|
-
fs: { readFile: (p) =>
|
|
106483
|
-
readFile: (p) =>
|
|
106484
|
-
homedir:
|
|
105602
|
+
fs: { readFile: (p) => readFile12(p, "utf-8"), readdir: (p) => readdir6(p) },
|
|
105603
|
+
readFile: (p) => readFile12(p, "utf-8"),
|
|
105604
|
+
homedir: os6.homedir,
|
|
106485
105605
|
cwd: process.cwd(),
|
|
106486
105606
|
gitRoot,
|
|
106487
105607
|
fetch,
|
|
@@ -106500,37 +105620,8 @@ if (process.argv[2] === "import-claude") {
|
|
|
106500
105620
|
process.exit(code);
|
|
106501
105621
|
}
|
|
106502
105622
|
if (process.argv[2] === "approve-mcp") {
|
|
106503
|
-
const {
|
|
106504
|
-
|
|
106505
|
-
const { resolveApiKey: resolveApiKey2 } = await Promise.resolve().then(() => (init_api_key(), api_key_exports));
|
|
106506
|
-
const { backendUrl: backendUrl2 } = await Promise.resolve().then(() => (init_profile_runtime(), profile_runtime_exports));
|
|
106507
|
-
const { createProfileClient: createProfileClient2 } = await Promise.resolve().then(() => (init_src(), src_exports));
|
|
106508
|
-
const { readFile: readFile10 } = await import("node:fs/promises");
|
|
106509
|
-
const configDir = process.env.SPACECODE_CONFIG_DIR?.trim() || path11.join(os5.homedir(), ".spacecode");
|
|
106510
|
-
const apiKey = await resolveApiKey2({ env: process.env, readFile: (p) => readFile10(p, "utf-8"), homedir: os5.homedir }).catch(() => null);
|
|
106511
|
-
const interactive = Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
106512
|
-
const { createInterface } = interactive ? await import("node:readline/promises") : { createInterface: void 0 };
|
|
106513
|
-
const rl = createInterface ? createInterface({ input: process.stdin, output: process.stdout }) : void 0;
|
|
106514
|
-
try {
|
|
106515
|
-
const code = await runApproveMcp2({
|
|
106516
|
-
configDir,
|
|
106517
|
-
cwd: process.cwd(),
|
|
106518
|
-
readFile: (p) => readFile10(p, "utf-8"),
|
|
106519
|
-
client: apiKey ? createProfileClient2({ baseUrl: backendUrl2(process.env), apiKey, fetch }) : null,
|
|
106520
|
-
trust: createNodeTrustStore2(configDir),
|
|
106521
|
-
...rl ? { ask: async (q2) => {
|
|
106522
|
-
try {
|
|
106523
|
-
return await rl.question(q2);
|
|
106524
|
-
} catch {
|
|
106525
|
-
return null;
|
|
106526
|
-
}
|
|
106527
|
-
} } : {},
|
|
106528
|
-
log: (line) => console.log(line)
|
|
106529
|
-
});
|
|
106530
|
-
process.exitCode = code;
|
|
106531
|
-
} finally {
|
|
106532
|
-
rl?.close();
|
|
106533
|
-
}
|
|
105623
|
+
const { runApproveMcpOnNode: runApproveMcpOnNode2 } = await Promise.resolve().then(() => (init_approve_mcp_node(), approve_mcp_node_exports));
|
|
105624
|
+
process.exitCode = await runApproveMcpOnNode2({ quiet: false });
|
|
106534
105625
|
process.exit(process.exitCode ?? 0);
|
|
106535
105626
|
}
|
|
106536
105627
|
var repoName = process.env.REPO_NAME || process.cwd().split("/").pop() || void 0;
|
|
@@ -106547,7 +105638,7 @@ if (process.stdin.isTTY && process.stdout.isTTY) {
|
|
|
106547
105638
|
const { promptRepoTrust: promptRepoTrust2 } = await Promise.resolve().then(() => (init_repo_trust_prompt(), repo_trust_prompt_exports));
|
|
106548
105639
|
const files = await readRepoSources2(process.cwd());
|
|
106549
105640
|
if (Object.keys(files).length > 0) {
|
|
106550
|
-
const store = createNodeTrustStore2(process.env.SPACECODE_CONFIG_DIR?.trim() ||
|
|
105641
|
+
const store = createNodeTrustStore2(process.env.SPACECODE_CONFIG_DIR?.trim() || path12.join(os6.homedir(), ".spacecode"));
|
|
106551
105642
|
if (await store.repoStatus(process.cwd(), files) === "untrusted") {
|
|
106552
105643
|
const { createInterface } = await import("node:readline/promises");
|
|
106553
105644
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
@@ -106575,6 +105666,15 @@ if (process.stdin.isTTY && process.stdout.isTTY) {
|
|
|
106575
105666
|
`);
|
|
106576
105667
|
}
|
|
106577
105668
|
}
|
|
105669
|
+
if (process.stdin.isTTY && process.stdout.isTTY) {
|
|
105670
|
+
try {
|
|
105671
|
+
const { runApproveMcpOnNode: runApproveMcpOnNode2 } = await Promise.resolve().then(() => (init_approve_mcp_node(), approve_mcp_node_exports));
|
|
105672
|
+
await runApproveMcpOnNode2({ quiet: true });
|
|
105673
|
+
} catch (err) {
|
|
105674
|
+
process.stderr.write(`MCP approval check failed (${err instanceof Error ? err.message : String(err)}); imported MCP servers stay unapproved.
|
|
105675
|
+
`);
|
|
105676
|
+
}
|
|
105677
|
+
}
|
|
106578
105678
|
var restoreCiVariables = hideCiVariables(process.env, Boolean(process.stdout.isTTY));
|
|
106579
105679
|
var render3;
|
|
106580
105680
|
var App3;
|
|
@@ -106586,9 +105686,9 @@ try {
|
|
|
106586
105686
|
var fullscreen = shouldUseFullscreen(process.env, process.stdout, Boolean(process.stdin.isTTY));
|
|
106587
105687
|
var session;
|
|
106588
105688
|
if (fullscreen) {
|
|
106589
|
-
const dir =
|
|
105689
|
+
const dir = path12.join(os6.homedir(), ".spacecode");
|
|
106590
105690
|
fs5.mkdirSync(dir, { recursive: true });
|
|
106591
|
-
const restoreConsole = redirectConsole(
|
|
105691
|
+
const restoreConsole = redirectConsole(path12.join(dir, "repl.log"));
|
|
106592
105692
|
session = enterFullscreen(process.stdout, process);
|
|
106593
105693
|
session.onLeave(restoreConsole);
|
|
106594
105694
|
session.onLeave(clearOnResize(process.stdout));
|