spacecode 2.10.1 → 2.10.3

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/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(path12) {
4333
- readCache.delete(path12);
4334
- const parent = path12.includes("/") ? path12.slice(0, path12.lastIndexOf("/") + 1) : "";
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 path12 = input.path;
4343
- const cached3 = readCache.get(path12);
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(path12);
4252
+ const content = await store.readFile(path13);
4348
4253
  if (content === null) {
4349
- return { output: `File not found: ${path12}`, is_error: true };
4254
+ return { output: `File not found: ${path13}`, is_error: true };
4350
4255
  }
4351
- readCache.set(path12, content);
4256
+ readCache.set(path13, content);
4352
4257
  return { output: content };
4353
4258
  },
4354
4259
  write_file: async (input) => {
4355
- const path12 = input.path;
4260
+ const path13 = input.path;
4356
4261
  const content = input.content;
4357
- await store.writeFile(path12, content);
4358
- invalidatePath(path12);
4359
- readCache.set(path12, content);
4360
- return { output: `Written: ${path12}` };
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 path12 = input.path;
4268
+ const path13 = input.path;
4364
4269
  const oldStr = input.old_string;
4365
4270
  const newStr = input.new_string;
4366
- let content = readCache.get(path12);
4271
+ let content = readCache.get(path13);
4367
4272
  if (content === void 0) {
4368
- const stored = await store.readFile(path12);
4273
+ const stored = await store.readFile(path13);
4369
4274
  if (stored === null) {
4370
4275
  return {
4371
- output: `File not found: ${path12}. Did you read the file first?`,
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 ${path12}. The file has ${lineCount} lines. Did you read the file first?`,
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 ${path12}:
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(path12, oldStr, newStr);
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(path12);
4399
- const updated = await store.readFile(path12);
4400
- if (updated !== null) readCache.set(path12, updated);
4401
- return { output: `Edited: ${path12}` };
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 path12 = input.path;
4405
- const cached3 = listCache.get(path12);
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: ${path12}` };
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(path12);
4413
- listCache.set(path12, entries);
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: ${path12}` };
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 CHUNK_SIZE, CHUNK_OVERLAP, BATCH_SIZE, MAX_FILE_SIZE, INDEXABLE_EXTENSIONS, RepoIndexer;
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, MemoryAuditLog, FileAuditLog;
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(readFile11) {
6023
- for (const name of LOCKFILES) {
6024
- const content = await readFile11(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, path12, report) {
5553
+ async function readOptional(fs6, path13, report) {
6522
5554
  try {
6523
- return await fs6.readFile(path12);
5555
+ return await fs6.readFile(path13);
6524
5556
  } catch (err) {
6525
5557
  if (isEnoent(err)) return null;
6526
- report.errors.push({ path: path12, message: err instanceof Error ? err.message : String(err) });
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 path12 = `${memDir}/${entry}`;
6558
- const text = await readOptional(fs6, path12, report);
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: path12, message: `cannot derive a valid asset name from "${entry}"` });
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: path12, message: `name collision on "${name}"` });
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: path12
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 path12 = `${dir}/${entry}`;
5627
+ const path13 = `${dir}/${entry}`;
6596
5628
  if (!entry.endsWith(".md")) {
6597
- if (!entry.startsWith(".")) report.notes.push(`Skipped ${path12}: only top-level .md command files are imported (subdirectories are not supported)`);
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: path12, message: `command name "${name}" must start with a letter and use only letters, digits, - and _` });
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: path12, message: `command name collision on "${name}" (names are case-insensitive)` });
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, path12, report);
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: path12, message: `skipped: ${Buffer.byteLength(text, "utf8")} bytes is over the ${MAX_ASSET_BYTES} byte asset limit` });
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: path12 });
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 path12 = `${root}/${dir}/${rel}`;
6670
- const content = await readOptional(fs6, path12, report);
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: path12 });
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 path12 = `${home}/.claude.json`;
6687
- const text = await readOptional(fs6, path12, report);
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: path12, message: e });
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: path12, message: `MCP server name "${s.name}" must use only letters, digits, . _ - (max 100)` });
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: path12, message: `MCP server name collision on "${s.name}" (names are case-insensitive)` });
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: path12, message: `MCP server "${s.name}" skipped: over the ${MAX_ASSET_BYTES} byte asset limit` });
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: `${path12} (${s.scope})`
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, path12, init = {}) {
5774
+ async function call(method, path13, init = {}) {
6743
5775
  let res;
6744
5776
  try {
6745
- res = await doFetch(`${root}/api/profile${path12}`, {
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} ${path12} -> ${res.status} ${detail}`, body);
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, MEMORY_TOOL_DEFINITIONS, SAFE;
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, INSTRUCTIONS_TOOL_DEFINITIONS, SAFE2;
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 [path12, content] of Object.entries(files)) fingerprint[path12] = hash(content);
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) {
@@ -7356,9 +6383,9 @@ function allOffConfig() {
7356
6383
  function isPlainObject2(value) {
7357
6384
  return typeof value === "object" && value !== null && !Array.isArray(value);
7358
6385
  }
7359
- function parseImportsConfig(text, path12) {
6386
+ function parseImportsConfig(text, path13) {
7360
6387
  if (text === null) return { config: allOffConfig() };
7361
- const fail = (reason) => ({ config: allOffConfig(), error: `${path12}: ${reason}` });
6388
+ const fail = (reason) => ({ config: allOffConfig(), error: `${path13}: ${reason}` });
7362
6389
  let parsed;
7363
6390
  try {
7364
6391
  parsed = JSON.parse(text);
@@ -7387,9 +6414,6 @@ function parseImportsConfig(text, path12) {
7387
6414
  }
7388
6415
  return { config };
7389
6416
  }
7390
- function serializeImportsConfig(config) {
7391
- return JSON.stringify({ version: 1, categories: config.categories, models: config.models }, null, 2);
7392
- }
7393
6417
  var IMPORT_CATEGORIES, MODEL_KEYS;
7394
6418
  var init_imports_config = __esm({
7395
6419
  "../agent/src/import/imports-config.ts"() {
@@ -7982,141 +7006,6 @@ var init_mcp_client = __esm({
7982
7006
  });
7983
7007
 
7984
7008
  // ../agent/src/index.ts
7985
- var src_exports = {};
7986
- __export(src_exports, {
7987
- AdaptPlanError: () => AdaptPlanError,
7988
- BASH_TOOL_DEFINITION: () => BASH_TOOL_DEFINITION,
7989
- BUILD_FEATURE_TOOL: () => BUILD_FEATURE_TOOL,
7990
- CALL_TIMEOUT_MS: () => CALL_TIMEOUT_MS,
7991
- CODEGEN_TOOL_DEFINITIONS: () => CODEGEN_TOOL_DEFINITIONS,
7992
- CONNECT_TIMEOUT_MS: () => CONNECT_TIMEOUT_MS,
7993
- CREATE_PR_TOOL: () => CREATE_PR_TOOL,
7994
- CloudHost: () => CloudHost,
7995
- DEFAULT_MAX_CONTEXT_TOKENS: () => DEFAULT_MAX_CONTEXT_TOKENS,
7996
- DEFAULT_PROFILE_BUDGET_CHARS: () => DEFAULT_PROFILE_BUDGET_CHARS,
7997
- EDIT_FILE_TOOL: () => EDIT_FILE_TOOL,
7998
- FILE_TOOL_DEFINITIONS: () => FILE_TOOL_DEFINITIONS,
7999
- FileAuditLog: () => FileAuditLog,
8000
- FileStore: () => FileStore,
8001
- GITHUB_TOOL_DEFINITIONS: () => GITHUB_TOOL_DEFINITIONS,
8002
- IMPORT_CATEGORIES: () => IMPORT_CATEGORIES,
8003
- INSTRUCTIONS_READ_TOOL: () => INSTRUCTIONS_READ_TOOL,
8004
- INSTRUCTIONS_TOOL_DEFINITIONS: () => INSTRUCTIONS_TOOL_DEFINITIONS,
8005
- LIST_DIRECTORY_TOOL: () => LIST_DIRECTORY_TOOL,
8006
- LocalHost: () => LocalHost,
8007
- MAX_ASSET_BYTES: () => MAX_ASSET_BYTES,
8008
- MAX_CORE_CHARS: () => MAX_CORE_CHARS,
8009
- MCP_PROTOCOL_VERSION: () => MCP_PROTOCOL_VERSION,
8010
- MEMORY_READ_TOOL: () => MEMORY_READ_TOOL,
8011
- MEMORY_TOOL_DEFINITIONS: () => MEMORY_TOOL_DEFINITIONS,
8012
- McpProtocolError: () => McpProtocolError,
8013
- MemoryAuditLog: () => MemoryAuditLog,
8014
- PERMISSION_MODES: () => PERMISSION_MODES,
8015
- PREVIEW_TOOL: () => PREVIEW_TOOL,
8016
- PREVIEW_TOOL_DEFINITIONS: () => PREVIEW_TOOL_DEFINITIONS,
8017
- PendingRequests: () => PendingRequests,
8018
- PermissionState: () => PermissionState,
8019
- PolicyViolation: () => PolicyViolation,
8020
- ProfileApiError: () => ProfileApiError,
8021
- READ_FILE_TOOL: () => READ_FILE_TOOL,
8022
- RESULT_CAP_CHARS: () => RESULT_CAP_CHARS,
8023
- ReplayBuffer: () => ReplayBuffer,
8024
- RepoIndexer: () => RepoIndexer,
8025
- RpcAbortError: () => RpcAbortError,
8026
- RpcClosedError: () => RpcClosedError,
8027
- RpcError: () => RpcError,
8028
- RpcTimeoutError: () => RpcTimeoutError,
8029
- SEARCH_TOOL: () => SEARCH_TOOL,
8030
- SENTRY_GET_ISSUE_TOOL: () => SENTRY_GET_ISSUE_TOOL,
8031
- SENTRY_LIST_ISSUES_TOOL: () => SENTRY_LIST_ISSUES_TOOL,
8032
- SENTRY_TOOL_DEFINITIONS: () => SENTRY_TOOL_DEFINITIONS,
8033
- SIBLING_PREFIX: () => SIBLING_PREFIX,
8034
- SKILLS_BUDGET_CHARS: () => SKILLS_BUDGET_CHARS,
8035
- SKILL_TOOL: () => SKILL_TOOL,
8036
- SnapshotManager: () => SnapshotManager,
8037
- TODO_TOOL: () => TODO_TOOL,
8038
- TODO_TOOL_DEFINITIONS: () => TODO_TOOL_DEFINITIONS,
8039
- TOOL_NAME_MAP: () => TOOL_NAME_MAP,
8040
- TrustError: () => TrustError,
8041
- TrustStore: () => TrustStore,
8042
- WRITE_FILE_TOOL: () => WRITE_FILE_TOOL,
8043
- allOffConfig: () => allOffConfig,
8044
- applyPlan: () => applyPlan,
8045
- buildCommandRegistry: () => buildCommandRegistry,
8046
- buildProfileSection: () => buildProfileSection,
8047
- buildRuleSet: () => buildRuleSet,
8048
- buildSkillsSection: () => buildSkillsSection,
8049
- buildSystemPrompt: () => buildSystemPrompt,
8050
- buildTranscriptForSummarization: () => buildTranscriptForSummarization,
8051
- checkCoverage: () => checkCoverage,
8052
- classifyCommand: () => classifyCommand,
8053
- classifyRpcMessage: () => classifyRpcMessage,
8054
- claudeProjectSlug: () => claudeProjectSlug,
8055
- computeLockfileHash: () => computeLockfileHash,
8056
- createBashTool: () => createBashTool,
8057
- createCodegenTools: () => createCodegenTools,
8058
- createFileTools: () => createFileTools,
8059
- createGitHubTools: () => createGitHubTools,
8060
- createHostSearchTool: () => createHostSearchTool,
8061
- createHostToolExecutor: () => createHostToolExecutor,
8062
- createInstructionsReadTool: () => createInstructionsReadTool,
8063
- createMcpClient: () => createMcpClient,
8064
- createMemoryReadTool: () => createMemoryReadTool,
8065
- createPreviewTool: () => createPreviewTool,
8066
- createProfileClient: () => createProfileClient,
8067
- createProfileSkillSource: () => createProfileSkillSource,
8068
- createRpcSession: () => createRpcSession,
8069
- createSearchTool: () => createSearchTool,
8070
- createSentryTools: () => createSentryTools,
8071
- createSimpleRouter: () => createSimpleRouter,
8072
- createSkillTool: () => createSkillTool,
8073
- createTodoTool: () => createTodoTool,
8074
- defaultModeFor: () => defaultModeFor,
8075
- diffSources: () => diffSources,
8076
- estimateTokens: () => estimateTokens,
8077
- estimateTotalTokens: () => estimateTotalTokens,
8078
- expandCommand: () => expandCommand,
8079
- expandRefs: () => expandRefs,
8080
- expandServerEnv: () => expandServerEnv,
8081
- extractRuleLines: () => extractRuleLines,
8082
- findUnsupported: () => findUnsupported,
8083
- fingerprintSources: () => fingerprintSources,
8084
- flattenMessageText: () => flattenMessageText,
8085
- formatSkillBody: () => formatSkillBody,
8086
- formatSourceChanges: () => formatSourceChanges,
8087
- formatToolResult: () => formatToolResult,
8088
- getCachedTools: () => getCachedTools,
8089
- guardHost: () => guardHost,
8090
- isReadOnlyCommand: () => isReadOnlyCommand,
8091
- joinSections: () => joinSections,
8092
- listMcpSpawns: () => listMcpSpawns,
8093
- loadProfile: () => loadProfile,
8094
- normalizeClaudeJsonMcp: () => normalizeClaudeJsonMcp,
8095
- parseCommandFile: () => parseCommandFile,
8096
- parseGitStatus: () => parseGitStatus,
8097
- parseImportsConfig: () => parseImportsConfig,
8098
- parseSkillFile: () => parseSkillFile,
8099
- parseTodos: () => parseTodos,
8100
- parseToolCache: () => parseToolCache,
8101
- prepareAdaptation: () => prepareAdaptation,
8102
- projectKey: () => projectKey,
8103
- putCachedTools: () => putCachedTools,
8104
- readClaudeAssets: () => readClaudeAssets,
8105
- renderProjectCard: () => renderProjectCard,
8106
- runAgentLoop: () => runAgentLoop,
8107
- safeSkillPath: () => safeSkillPath,
8108
- sameTools: () => sameTools,
8109
- serializeImportsConfig: () => serializeImportsConfig,
8110
- serializeToolCache: () => serializeToolCache,
8111
- serverCacheKey: () => serverCacheKey,
8112
- siblingAssetName: () => siblingAssetName,
8113
- splitCommand: () => splitCommand,
8114
- splitSections: () => splitSections,
8115
- summarizeConversation: () => summarizeConversation,
8116
- syncAfterBash: () => syncAfterBash,
8117
- syncBeforeBash: () => syncBeforeBash,
8118
- translateToolNames: () => translateToolNames
8119
- });
8120
7009
  var init_src = __esm({
8121
7010
  "../agent/src/index.ts"() {
8122
7011
  init_loop();
@@ -8179,14 +7068,9 @@ var init_src = __esm({
8179
7068
  });
8180
7069
 
8181
7070
  // src/api-key.ts
8182
- var api_key_exports = {};
8183
- __export(api_key_exports, {
8184
- MISSING_KEY_MESSAGE: () => MISSING_KEY_MESSAGE,
8185
- resolveApiKey: () => resolveApiKey
8186
- });
8187
7071
  import { join } from "node:path";
8188
7072
  async function resolveApiKey(deps) {
8189
- const { env: env3, readFile: readFile11, homedir: homedir3 } = deps;
7073
+ const { env: env3, readFile: readFile12, homedir: homedir3 } = deps;
8190
7074
  for (const name of ["SPACECODE_API_KEY", "ANTHROPIC_API_KEY"]) {
8191
7075
  const value = env3[name]?.trim();
8192
7076
  if (value) return value;
@@ -8195,7 +7079,7 @@ async function resolveApiKey(deps) {
8195
7079
  const file = join(dir, "auth.json");
8196
7080
  let raw;
8197
7081
  try {
8198
- raw = await readFile11(file);
7082
+ raw = await readFile12(file);
8199
7083
  } catch (err) {
8200
7084
  if (err.code === "ENOENT") return null;
8201
7085
  throw new Error(`Cannot read ${file}: ${err instanceof Error ? err.message : String(err)}`);
@@ -8228,26 +7112,26 @@ __export(import_gate_exports, {
8228
7112
  importGateStatus: () => importGateStatus,
8229
7113
  nodeTrustFs: () => nodeTrustFs,
8230
7114
  readRepoSources: () => readRepoSources,
8231
- sha256: () => sha2562,
7115
+ sha256: () => sha256,
8232
7116
  writeTextAtomic: () => writeTextAtomic
8233
7117
  });
8234
7118
  import { createHash, randomUUID } from "node:crypto";
8235
7119
  import { chmod, mkdir, readFile, readdir, realpath, rename, writeFile } from "node:fs/promises";
8236
7120
  import { dirname as dirname3, join as join2 } from "node:path";
8237
- async function readOrNull(path12) {
7121
+ async function readOrNull(path13) {
8238
7122
  try {
8239
- return await readFile(path12, "utf8");
7123
+ return await readFile(path13, "utf8");
8240
7124
  } catch (err) {
8241
7125
  if (err.code === "ENOENT") return null;
8242
7126
  throw err;
8243
7127
  }
8244
7128
  }
8245
- async function writeTextAtomic(path12, text) {
8246
- await mkdir(dirname3(path12), { recursive: true });
8247
- const tmp = `${path12}.${process.pid}.${randomUUID()}.tmp`;
7129
+ async function writeTextAtomic(path13, text) {
7130
+ await mkdir(dirname3(path13), { recursive: true });
7131
+ const tmp = `${path13}.${process.pid}.${randomUUID()}.tmp`;
8248
7132
  await writeFile(tmp, text, { mode: 384 });
8249
7133
  await chmod(tmp, 384);
8250
- await rename(tmp, path12);
7134
+ await rename(tmp, path13);
8251
7135
  }
8252
7136
  async function readRepoSources(cwd2) {
8253
7137
  const files = {};
@@ -8301,7 +7185,7 @@ function nodeTrustFs() {
8301
7185
  function createNodeTrustStore(configDir) {
8302
7186
  let store = trustStores.get(configDir);
8303
7187
  if (!store) {
8304
- store = new TrustStore({ fs: nodeTrustFs(), path: join2(configDir, "trust.json"), hash: sha2562 });
7188
+ store = new TrustStore({ fs: nodeTrustFs(), path: join2(configDir, "trust.json"), hash: sha256 });
8305
7189
  trustStores.set(configDir, store);
8306
7190
  }
8307
7191
  return store;
@@ -8333,7 +7217,7 @@ async function importGateStatus(deps) {
8333
7217
  }
8334
7218
  return lines2;
8335
7219
  }
8336
- var REPO_SOURCE_FILES, MAX_REPO_SKILL_FILES, MAX_REPO_SKILL_BYTES, sha2562, trustStores;
7220
+ var REPO_SOURCE_FILES, MAX_REPO_SKILL_FILES, MAX_REPO_SKILL_BYTES, sha256, trustStores;
8337
7221
  var init_import_gate = __esm({
8338
7222
  "src/import-gate.ts"() {
8339
7223
  "use strict";
@@ -8341,7 +7225,7 @@ var init_import_gate = __esm({
8341
7225
  REPO_SOURCE_FILES = [".mcp.json", ".claude/settings.json", ".claude/settings.local.json", "CLAUDE.local.md"];
8342
7226
  MAX_REPO_SKILL_FILES = 200;
8343
7227
  MAX_REPO_SKILL_BYTES = 256 * 1024;
8344
- sha2562 = (text) => createHash("sha256").update(text).digest("hex");
7228
+ sha256 = (text) => createHash("sha256").update(text).digest("hex");
8345
7229
  trustStores = /* @__PURE__ */ new Map();
8346
7230
  }
8347
7231
  });
@@ -8527,12 +7411,6 @@ var init_mcp_loader = __esm({
8527
7411
  });
8528
7412
 
8529
7413
  // src/profile-runtime.ts
8530
- var profile_runtime_exports = {};
8531
- __export(profile_runtime_exports, {
8532
- DEFAULT_BACKEND_URL: () => DEFAULT_BACKEND_URL,
8533
- backendUrl: () => backendUrl,
8534
- prepareProfile: () => prepareProfile
8535
- });
8536
7414
  import { join as join5 } from "node:path";
8537
7415
  function backendUrl(env3) {
8538
7416
  return env3.SPACECODE_BACKEND_URL?.trim() || env3.SPACECODE_API_URL?.trim() || DEFAULT_BACKEND_URL;
@@ -9023,15 +7901,24 @@ var init_mcp_manager = __esm({
9023
7901
  }
9024
7902
  }
9025
7903
  }
9026
- /** Starts every eligible server in the background. Resolves when all have connected or failed; callers need not await it. */
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
+ */
9027
7909
  start() {
9028
- this.startPromise ??= Promise.all(this.deps.defs.map((def) => {
9029
- const p = this.startOne(def);
9030
- this.connected.set(def.name, p);
9031
- return p;
9032
- })).then(() => void 0);
7910
+ this.startPromise ??= Promise.all(this.deps.defs.filter((def) => !this.cachedTools.has(def.name)).map((def) => this.begin(def))).then(() => void 0);
9033
7911
  return this.startPromise;
9034
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
+ }
9035
7922
  /** Tools whose full names are valid and unique, plus the ones that are not. */
9036
7923
  usable(server, listed) {
9037
7924
  const tools = [];
@@ -9148,9 +8035,10 @@ var init_mcp_manager = __esm({
9148
8035
  }
9149
8036
  return out;
9150
8037
  }
9151
- /** A call on a cached tool waits for its server to connect (bounded by the connect timeout) and then goes through the normal checks. */
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. */
9152
8039
  async callWhenReady(server, tool, input) {
9153
- await this.connected.get(server);
8040
+ const def = this.deps.defs.find((d) => d.name === server);
8041
+ if (def) await this.begin(def);
9154
8042
  const state = this.servers.get(server);
9155
8043
  if (state?.status === "ready" && !state.tools.some((t) => t.name === tool)) {
9156
8044
  return { output: `MCP server "${server}" no longer offers ${tool}; its tool list changed since it was cached.`, is_error: true };
@@ -9202,7 +8090,7 @@ var init_mcp_manager = __esm({
9202
8090
  const s = this.servers.get(def.name);
9203
8091
  if (s === void 0) {
9204
8092
  const cached3 = this.cachedTools.get(def.name);
9205
- if (cached3) lines2.push(`mcp: ${def.name} connecting... (${cached3.length} tools from cache)`);
8093
+ if (cached3) lines2.push(`mcp: ${def.name} not started, ${cached3.length} tools from cache (starts on first use)`);
9206
8094
  continue;
9207
8095
  }
9208
8096
  if (s.status === "ready") lines2.push(`mcp: ${s.name} ready, ${s.tools.length} tools${s.skippedTools.length ? ` (not usable: ${s.skippedTools.join(", ")})` : ""}`);
@@ -9226,13 +8114,9 @@ var init_mcp_manager = __esm({
9226
8114
  });
9227
8115
 
9228
8116
  // src/mcp/approve-mcp.ts
9229
- var approve_mcp_exports = {};
9230
- __export(approve_mcp_exports, {
9231
- runApproveMcp: () => runApproveMcp
9232
- });
9233
8117
  async function runApproveMcp(deps) {
9234
8118
  const { defs, statusLines } = await loadMcpDefs(deps);
9235
- for (const l of statusLines) deps.log(l);
8119
+ if (!deps.quiet) for (const l of statusLines) deps.log(l);
9236
8120
  const stdio = defs.filter((d) => d.transport === "stdio");
9237
8121
  const todo = [];
9238
8122
  for (const d of stdio) {
@@ -9240,13 +8124,15 @@ async function runApproveMcp(deps) {
9240
8124
  if (await deps.trust.stdioStatus(key) === "unapproved") todo.push(d);
9241
8125
  }
9242
8126
  if (todo.length === 0) {
9243
- 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.");
9244
8128
  return 0;
9245
8129
  }
9246
8130
  if (!deps.ask) {
9247
8131
  deps.log(`${todo.length} MCP server(s) need approval, but this is not an interactive terminal: nothing was approved.`);
9248
8132
  return 1;
9249
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).`);
9250
8136
  let approved = 0;
9251
8137
  for (const d of todo) {
9252
8138
  deps.log(`
@@ -9274,19 +8160,65 @@ var init_approve_mcp = __esm({
9274
8160
  }
9275
8161
  });
9276
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
+
9277
8209
  // src/repo-trust-prompt.ts
9278
8210
  var repo_trust_prompt_exports = {};
9279
8211
  __export(repo_trust_prompt_exports, {
9280
8212
  describeRepoSources: () => describeRepoSources,
9281
8213
  promptRepoTrust: () => promptRepoTrust
9282
8214
  });
9283
- function ruleLists(path12, text, lines2, warnings) {
8215
+ function ruleLists(path13, text, lines2, warnings) {
9284
8216
  let data;
9285
8217
  try {
9286
8218
  data = JSON.parse(text);
9287
8219
  } catch (err) {
9288
8220
  if (!(err instanceof SyntaxError)) throw err;
9289
- warnings.push(`${path12}: not valid JSON, nothing from it loads`);
8221
+ warnings.push(`${path13}: not valid JSON, nothing from it loads`);
9290
8222
  return;
9291
8223
  }
9292
8224
  const perms = typeof data === "object" && data !== null ? data.permissions : void 0;
@@ -9294,7 +8226,7 @@ function ruleLists(path12, text, lines2, warnings) {
9294
8226
  for (const effect of ["allow", "deny", "ask"]) {
9295
8227
  const list2 = perms[effect];
9296
8228
  if (!Array.isArray(list2)) continue;
9297
- for (const rule of list2) if (typeof rule === "string") lines2.push(` ${effect}: ${show2(rule)} (${path12})`);
8229
+ for (const rule of list2) if (typeof rule === "string") lines2.push(` ${effect}: ${show2(rule)} (${path13})`);
9298
8230
  }
9299
8231
  }
9300
8232
  function describeRepoSources(files) {
@@ -9540,7 +8472,7 @@ __export(base_exports, {
9540
8472
  synchronizedOutput: () => synchronizedOutput
9541
8473
  });
9542
8474
  import process2 from "node:process";
9543
- import os from "node:os";
8475
+ import os2 from "node:os";
9544
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;
9545
8477
  var init_base = __esm({
9546
8478
  "../../node_modules/ansi-escapes/base.js"() {
@@ -9623,7 +8555,7 @@ var init_base = __esm({
9623
8555
  if (isBrowser || !isWindows2) {
9624
8556
  return false;
9625
8557
  }
9626
- const parts = os.release().split(".");
8558
+ const parts = os2.release().split(".");
9627
8559
  const major = Number(parts[0]);
9628
8560
  const build2 = Number(parts[2] ?? 0);
9629
8561
  if (major < 10) {
@@ -31903,10 +30835,10 @@ var require_react_reconciler_development = __commonJS({
31903
30835
  var setErrorHandler = null;
31904
30836
  var setSuspenseHandler = null;
31905
30837
  {
31906
- var copyWithDeleteImpl = function(obj, path12, index2) {
31907
- var key = path12[index2];
30838
+ var copyWithDeleteImpl = function(obj, path13, index2) {
30839
+ var key = path13[index2];
31908
30840
  var updated = isArray(obj) ? obj.slice() : assign({}, obj);
31909
- if (index2 + 1 === path12.length) {
30841
+ if (index2 + 1 === path13.length) {
31910
30842
  if (isArray(updated)) {
31911
30843
  updated.splice(key, 1);
31912
30844
  } else {
@@ -31914,11 +30846,11 @@ var require_react_reconciler_development = __commonJS({
31914
30846
  }
31915
30847
  return updated;
31916
30848
  }
31917
- updated[key] = copyWithDeleteImpl(obj[key], path12, index2 + 1);
30849
+ updated[key] = copyWithDeleteImpl(obj[key], path13, index2 + 1);
31918
30850
  return updated;
31919
30851
  };
31920
- var copyWithDelete = function(obj, path12) {
31921
- return copyWithDeleteImpl(obj, path12, 0);
30852
+ var copyWithDelete = function(obj, path13) {
30853
+ return copyWithDeleteImpl(obj, path13, 0);
31922
30854
  };
31923
30855
  var copyWithRenameImpl = function(obj, oldPath, newPath, index2) {
31924
30856
  var oldKey = oldPath[index2];
@@ -31956,17 +30888,17 @@ var require_react_reconciler_development = __commonJS({
31956
30888
  }
31957
30889
  return copyWithRenameImpl(obj, oldPath, newPath, 0);
31958
30890
  };
31959
- var copyWithSetImpl = function(obj, path12, index2, value) {
31960
- if (index2 >= path12.length) {
30891
+ var copyWithSetImpl = function(obj, path13, index2, value) {
30892
+ if (index2 >= path13.length) {
31961
30893
  return value;
31962
30894
  }
31963
- var key = path12[index2];
30895
+ var key = path13[index2];
31964
30896
  var updated = isArray(obj) ? obj.slice() : assign({}, obj);
31965
- updated[key] = copyWithSetImpl(obj[key], path12, index2 + 1, value);
30897
+ updated[key] = copyWithSetImpl(obj[key], path13, index2 + 1, value);
31966
30898
  return updated;
31967
30899
  };
31968
- var copyWithSet = function(obj, path12, value) {
31969
- return copyWithSetImpl(obj, path12, 0, value);
30900
+ var copyWithSet = function(obj, path13, value) {
30901
+ return copyWithSetImpl(obj, path13, 0, value);
31970
30902
  };
31971
30903
  var findHook = function(fiber, id) {
31972
30904
  var currentHook2 = fiber.memoizedState;
@@ -31976,10 +30908,10 @@ var require_react_reconciler_development = __commonJS({
31976
30908
  }
31977
30909
  return currentHook2;
31978
30910
  };
31979
- overrideHookState = function(fiber, id, path12, value) {
30911
+ overrideHookState = function(fiber, id, path13, value) {
31980
30912
  var hook = findHook(fiber, id);
31981
30913
  if (hook !== null) {
31982
- var newState = copyWithSet(hook.memoizedState, path12, value);
30914
+ var newState = copyWithSet(hook.memoizedState, path13, value);
31983
30915
  hook.memoizedState = newState;
31984
30916
  hook.baseState = newState;
31985
30917
  fiber.memoizedProps = assign({}, fiber.memoizedProps);
@@ -31989,10 +30921,10 @@ var require_react_reconciler_development = __commonJS({
31989
30921
  }
31990
30922
  }
31991
30923
  };
31992
- overrideHookStateDeletePath = function(fiber, id, path12) {
30924
+ overrideHookStateDeletePath = function(fiber, id, path13) {
31993
30925
  var hook = findHook(fiber, id);
31994
30926
  if (hook !== null) {
31995
- var newState = copyWithDelete(hook.memoizedState, path12);
30927
+ var newState = copyWithDelete(hook.memoizedState, path13);
31996
30928
  hook.memoizedState = newState;
31997
30929
  hook.baseState = newState;
31998
30930
  fiber.memoizedProps = assign({}, fiber.memoizedProps);
@@ -32015,8 +30947,8 @@ var require_react_reconciler_development = __commonJS({
32015
30947
  }
32016
30948
  }
32017
30949
  };
32018
- overrideProps = function(fiber, path12, value) {
32019
- fiber.pendingProps = copyWithSet(fiber.memoizedProps, path12, value);
30950
+ overrideProps = function(fiber, path13, value) {
30951
+ fiber.pendingProps = copyWithSet(fiber.memoizedProps, path13, value);
32020
30952
  if (fiber.alternate) {
32021
30953
  fiber.alternate.pendingProps = fiber.pendingProps;
32022
30954
  }
@@ -32025,8 +30957,8 @@ var require_react_reconciler_development = __commonJS({
32025
30957
  scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
32026
30958
  }
32027
30959
  };
32028
- overridePropsDeletePath = function(fiber, path12) {
32029
- fiber.pendingProps = copyWithDelete(fiber.memoizedProps, path12);
30960
+ overridePropsDeletePath = function(fiber, path13) {
30961
+ fiber.pendingProps = copyWithDelete(fiber.memoizedProps, path13);
32030
30962
  if (fiber.alternate) {
32031
30963
  fiber.alternate.pendingProps = fiber.pendingProps;
32032
30964
  }
@@ -37882,7 +36814,7 @@ var init_ansi_styles2 = __esm({
37882
36814
 
37883
36815
  // ../../node_modules/chalk/source/vendor/supports-color/index.js
37884
36816
  import process4 from "node:process";
37885
- import os2 from "node:os";
36817
+ import os3 from "node:os";
37886
36818
  import tty from "node:tty";
37887
36819
  function hasFlag(flag, argv = globalThis.Deno ? globalThis.Deno.args : process4.argv) {
37888
36820
  const prefix = flag.startsWith("-") ? "" : flag.length === 1 ? "-" : "--";
@@ -37940,7 +36872,7 @@ function _supportsColor(haveStream, { streamIsTTY, sniffFlags = true } = {}) {
37940
36872
  return min;
37941
36873
  }
37942
36874
  if (process4.platform === "win32") {
37943
- const osRelease = os2.release().split(".");
36875
+ const osRelease = os3.release().split(".");
37944
36876
  if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) {
37945
36877
  return Number(osRelease[2]) >= 14931 ? 3 : 2;
37946
36878
  }
@@ -39731,8 +38663,8 @@ var init_ErrorOverview = __esm({
39731
38663
  init_dist3();
39732
38664
  init_Box();
39733
38665
  init_Text();
39734
- cleanupPath = (path12) => {
39735
- return path12?.replace(`file://${cwd()}/`, "");
38666
+ cleanupPath = (path13) => {
38667
+ return path13?.replace(`file://${cwd()}/`, "");
39736
38668
  };
39737
38669
  stackUtils = new import_stack_utils.default({
39738
38670
  cwd: cwd(),
@@ -42971,7 +41903,7 @@ var require_has_flag = __commonJS({
42971
41903
  var require_supports_colors = __commonJS({
42972
41904
  "../../node_modules/@colors/colors/lib/system/supports-colors.js"(exports, module) {
42973
41905
  "use strict";
42974
- var os6 = __require("os");
41906
+ var os7 = __require("os");
42975
41907
  var hasFlag2 = require_has_flag();
42976
41908
  var env3 = process.env;
42977
41909
  var forceColor = void 0;
@@ -43009,7 +41941,7 @@ var require_supports_colors = __commonJS({
43009
41941
  }
43010
41942
  var min = forceColor ? 1 : 0;
43011
41943
  if (process.platform === "win32") {
43012
- var osRelease = os6.release().split(".");
41944
+ var osRelease = os7.release().split(".");
43013
41945
  if (Number(process.versions.node.split(".")[0]) >= 8 && Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) {
43014
41946
  return Number(osRelease[2]) >= 14931 ? 3 : 2;
43015
41947
  }
@@ -87094,15 +86026,15 @@ var require_route = __commonJS({
87094
86026
  };
87095
86027
  }
87096
86028
  function wrapConversion(toModel, graph) {
87097
- const path12 = [graph[toModel].parent, toModel];
86029
+ const path13 = [graph[toModel].parent, toModel];
87098
86030
  let fn = conversions[graph[toModel].parent][toModel];
87099
86031
  let cur = graph[toModel].parent;
87100
86032
  while (graph[cur].parent) {
87101
- path12.unshift(graph[cur].parent);
86033
+ path13.unshift(graph[cur].parent);
87102
86034
  fn = link2(conversions[graph[cur].parent][cur], fn);
87103
86035
  cur = graph[cur].parent;
87104
86036
  }
87105
- fn.conversion = path12;
86037
+ fn.conversion = path13;
87106
86038
  return fn;
87107
86039
  }
87108
86040
  module.exports = function(fromModel) {
@@ -87342,7 +86274,7 @@ var require_has_flag2 = __commonJS({
87342
86274
  var require_supports_color = __commonJS({
87343
86275
  "../../node_modules/supports-color/index.js"(exports, module) {
87344
86276
  "use strict";
87345
- var os6 = __require("os");
86277
+ var os7 = __require("os");
87346
86278
  var tty2 = __require("tty");
87347
86279
  var hasFlag2 = require_has_flag2();
87348
86280
  var { env: env3 } = process;
@@ -87390,7 +86322,7 @@ var require_supports_color = __commonJS({
87390
86322
  return min;
87391
86323
  }
87392
86324
  if (process.platform === "win32") {
87393
- const osRelease = os6.release().split(".");
86325
+ const osRelease = os7.release().split(".");
87394
86326
  if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) {
87395
86327
  return Number(osRelease[2]) >= 14931 ? 3 : 2;
87396
86328
  }
@@ -102323,9 +101255,9 @@ var init_credential_filter = __esm({
102323
101255
  });
102324
101256
 
102325
101257
  // src/session/session-store.ts
102326
- import { chmod as chmod2, mkdir as mkdir2, open, readdir as readdir2, readFile as readFile2, rename as rename2, rm, stat, writeFile as writeFile2 } from "node:fs/promises";
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";
102327
101259
  import { randomBytes } from "node:crypto";
102328
- import path3 from "node:path";
101260
+ import path4 from "node:path";
102329
101261
  function redactCredentials(value) {
102330
101262
  if (typeof value === "string") return containsCredential(value) ? REDACTED : value;
102331
101263
  if (Array.isArray(value)) return value.map(redactCredentials);
@@ -102400,7 +101332,7 @@ var init_session_store = __esm({
102400
101332
  }
102401
101333
  fileFor(id) {
102402
101334
  if (!ID.test(id)) throw new SessionError(this.dir, `Invalid session id ${JSON.stringify(id)}`);
102403
- return path3.join(this.dir, `${id}.jsonl`);
101335
+ return path4.join(this.dir, `${id}.jsonl`);
102404
101336
  }
102405
101337
  async save(session2) {
102406
101338
  const file = this.fileFor(session2.meta.id);
@@ -102429,7 +101361,7 @@ var init_session_store = __esm({
102429
101361
  if (size > MAX_SESSION_BYTES) {
102430
101362
  throw new SessionError(file, `Session is ${size} bytes, over the ${MAX_SESSION_BYTES}-byte cap; not loaded`);
102431
101363
  }
102432
- text = await readFile2(file, "utf8");
101364
+ text = await readFile3(file, "utf8");
102433
101365
  } catch (err) {
102434
101366
  if (err instanceof SessionError) throw err;
102435
101367
  throw new SessionError(file, `Could not read session: ${err instanceof Error ? err.message : String(err)}`, { cause: err });
@@ -102453,7 +101385,7 @@ var init_session_store = __esm({
102453
101385
  try {
102454
101386
  sessions.push((await this.load(name.slice(0, -".jsonl".length))).meta);
102455
101387
  } catch (err) {
102456
- errors.push(err instanceof SessionError ? err : new SessionError(path3.join(this.dir, name), String(err), { cause: err }));
101388
+ errors.push(err instanceof SessionError ? err : new SessionError(path4.join(this.dir, name), String(err), { cause: err }));
102457
101389
  }
102458
101390
  }
102459
101391
  sessions.sort((a, b2) => b2.updatedAt.localeCompare(a.updatedAt));
@@ -103527,17 +102459,17 @@ function isSubsequence(needle, hay) {
103527
102459
  for (let j2 = 0; j2 < hay.length && i < needle.length; j2++) if (hay[j2] === needle[i]) i++;
103528
102460
  return i === needle.length;
103529
102461
  }
103530
- function tier(path12, q2) {
103531
- const base = path12.slice(path12.lastIndexOf("/") + 1);
102462
+ function tier(path13, q2) {
102463
+ const base = path13.slice(path13.lastIndexOf("/") + 1);
103532
102464
  if (base.startsWith(q2)) return 0;
103533
102465
  if (base.includes(q2)) return 1;
103534
- if (path12.includes(q2)) return 2;
102466
+ if (path13.includes(q2)) return 2;
103535
102467
  return 3;
103536
102468
  }
103537
102469
  function rankPaths(paths, query, limit = 8) {
103538
102470
  if (query === "") return paths.slice(0, limit);
103539
102471
  const q2 = query.toLowerCase();
103540
- return paths.map((path12) => ({ path: path12, lower: path12.toLowerCase() })).filter(({ lower }) => isSubsequence(q2, lower)).map(({ path: path12, lower }) => ({ path: path12, 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);
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);
103541
102473
  }
103542
102474
  var init_mention_rank = __esm({
103543
102475
  "src/editor/mention-rank.ts"() {
@@ -103554,9 +102486,9 @@ function activeMention(text, cursor) {
103554
102486
  const explicit = token[1] === "!";
103555
102487
  return { start, query: token.slice(explicit ? 2 : 1), explicit };
103556
102488
  }
103557
- function insertMention(text, cursor, mention, path12) {
103558
- if (/\s/.test(path12)) return { text, cursor };
103559
- const inserted = `@${mention.explicit ? "!" : ""}${path12}`;
102489
+ function insertMention(text, cursor, mention, path13) {
102490
+ if (/\s/.test(path13)) return { text, cursor };
102491
+ const inserted = `@${mention.explicit ? "!" : ""}${path13}`;
103560
102492
  const head = text.slice(0, mention.start) + inserted;
103561
102493
  const next = text[cursor];
103562
102494
  if (next !== void 0 && /\s/.test(next)) return { text: head + text.slice(cursor), cursor: head.length + 1 };
@@ -103926,20 +102858,20 @@ var init_history_store = __esm({
103926
102858
  });
103927
102859
 
103928
102860
  // src/editor/history-file-io.ts
103929
- import { chmod as chmod3, mkdir as mkdir3, readFile as readFile3, rename as rename3, writeFile as writeFile3 } from "node:fs/promises";
103930
- import path4 from "node:path";
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";
103931
102863
  function createFileHistoryIO(file) {
103932
102864
  return {
103933
102865
  async read() {
103934
102866
  try {
103935
- return await readFile3(file, "utf8");
102867
+ return await readFile4(file, "utf8");
103936
102868
  } catch (err) {
103937
102869
  if (err.code === "ENOENT") return null;
103938
102870
  throw err;
103939
102871
  }
103940
102872
  },
103941
102873
  async write(content) {
103942
- await mkdir3(path4.dirname(file), { recursive: true, mode: 448 });
102874
+ await mkdir3(path5.dirname(file), { recursive: true, mode: 448 });
103943
102875
  const tmp = `${file}.${process.pid}.tmp`;
103944
102876
  await writeFile3(tmp, content, { mode: 384 });
103945
102877
  await chmod3(tmp, 384);
@@ -104101,10 +103033,10 @@ var init_session_commands = __esm({
104101
103033
  });
104102
103034
 
104103
103035
  // src/local-toolset.ts
104104
- import * as os3 from "node:os";
104105
- import * as path5 from "node:path";
103036
+ import * as os4 from "node:os";
103037
+ import * as path6 from "node:path";
104106
103038
  function createLocalToolset(opts) {
104107
- const auditFile = opts.auditFile ?? path5.join(os3.homedir(), ".spacecode", "audit.jsonl");
103039
+ const auditFile = opts.auditFile ?? path6.join(os4.homedir(), ".spacecode", "audit.jsonl");
104108
103040
  const host = guardHost(new LocalHost(opts.cwd), {
104109
103041
  audit: new FileAuditLog(auditFile),
104110
103042
  mode: opts.yolo ? "yolo" : "enforce",
@@ -104134,7 +103066,7 @@ var init_local_toolset = __esm({
104134
103066
 
104135
103067
  // src/project-card-scan.ts
104136
103068
  import { execFile as execFile3 } from "node:child_process";
104137
- import { readdir as readdir3, readFile as readFile4 } from "node:fs/promises";
103069
+ import { readdir as readdir3, readFile as readFile5 } from "node:fs/promises";
104138
103070
  import { join as join7 } from "node:path";
104139
103071
  function digestLines(md) {
104140
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);
@@ -104235,13 +103167,13 @@ var init_project_card_scan = __esm({
104235
103167
  MAX_BACKLOG = 6;
104236
103168
  DIGEST_FILES = ["CLAUDE.md", "TODO.md", "BUGS.md", "ISSUES.md", "CHANGELOG.md", "README.md"];
104237
103169
  nodeScanIo = {
104238
- async readdir(path12) {
104239
- const entries = await readdir3(path12, { withFileTypes: true });
103170
+ async readdir(path13) {
103171
+ const entries = await readdir3(path13, { withFileTypes: true });
104240
103172
  return entries.map((e) => ({ name: e.name, isDir: e.isDirectory() }));
104241
103173
  },
104242
- async readFile(path12) {
103174
+ async readFile(path13) {
104243
103175
  try {
104244
- return await readFile4(path12, "utf8");
103176
+ return await readFile5(path13, "utf8");
104245
103177
  } catch (err) {
104246
103178
  if (err.code === "ENOENT") return null;
104247
103179
  throw err;
@@ -104594,9 +103526,9 @@ var init_mention_resolver = __esm({
104594
103526
 
104595
103527
  // src/editor/node-mention-fs.ts
104596
103528
  import { open as open2, realpath as realpath2, stat as stat2 } from "node:fs/promises";
104597
- import path6 from "node:path";
103529
+ import path7 from "node:path";
104598
103530
  function isInside(root, target) {
104599
- return target === root || target.startsWith(root.endsWith(path6.sep) ? root : root + path6.sep);
103531
+ return target === root || target.startsWith(root.endsWith(path7.sep) ? root : root + path7.sep);
104600
103532
  }
104601
103533
  function createNodeMentionFS(cwd2) {
104602
103534
  let rootPromise;
@@ -104641,10 +103573,10 @@ var init_node_mention_fs = __esm({
104641
103573
 
104642
103574
  // src/editor/node-read-dir.ts
104643
103575
  import { readdir as readdir4 } from "node:fs/promises";
104644
- import path7 from "node:path";
103576
+ import path8 from "node:path";
104645
103577
  function createNodeReadDir(cwd2) {
104646
103578
  return async (rel) => {
104647
- const entries = await readdir4(path7.join(cwd2, rel), { withFileTypes: true });
103579
+ const entries = await readdir4(path8.join(cwd2, rel), { withFileTypes: true });
104648
103580
  return entries.map((e) => ({ name: e.name, isFile: e.isFile(), isDirectory: e.isDirectory() }));
104649
103581
  };
104650
103582
  }
@@ -104655,11 +103587,11 @@ var init_node_read_dir = __esm({
104655
103587
  });
104656
103588
 
104657
103589
  // src/editor/node-mentions.ts
104658
- import { readFile as readFile5 } from "node:fs/promises";
104659
- import path8 from "node:path";
103590
+ import { readFile as readFile6 } from "node:fs/promises";
103591
+ import path9 from "node:path";
104660
103592
  async function readGitignore(cwd2) {
104661
103593
  try {
104662
- return createGitignoreMatcher(await readFile5(path8.join(cwd2, ".gitignore"), "utf8"));
103594
+ return createGitignoreMatcher(await readFile6(path9.join(cwd2, ".gitignore"), "utf8"));
104663
103595
  } catch (err) {
104664
103596
  if (err.code === "ENOENT") return () => false;
104665
103597
  throw err;
@@ -104769,7 +103701,7 @@ var init_recording_chunks = __esm({
104769
103701
 
104770
103702
  // src/recording/recorder.ts
104771
103703
  import { createHash as createHash4 } from "node:crypto";
104772
- import { mkdir as mkdir4, readFile as readFile6, rename as rename4, rm as rm2, writeFile as writeFile4, readdir as readdir5, stat as stat3 } from "node:fs/promises";
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";
104773
103705
  import { join as join9 } from "node:path";
104774
103706
  function projectHash(root) {
104775
103707
  return createHash4("sha256").update(root).digest("hex");
@@ -104793,7 +103725,7 @@ function createRecorder(deps) {
104793
103725
  if (cached3) return cached3;
104794
103726
  let st;
104795
103727
  try {
104796
- st = JSON.parse(await readFile6(stateFile(sessionId), "utf8"));
103728
+ st = JSON.parse(await readFile7(stateFile(sessionId), "utf8"));
104797
103729
  } catch (e) {
104798
103730
  if (e.code !== "ENOENT") {
104799
103731
  const bad = join9(deps.dir, "bad");
@@ -104988,15 +103920,15 @@ var init_recorder = __esm({
104988
103920
  });
104989
103921
 
104990
103922
  // src/recording/recording-runtime.ts
104991
- import { mkdir as mkdir5, readFile as readFile7, writeFile as writeFile5, stat as stat4 } from "node:fs/promises";
103923
+ import { mkdir as mkdir5, readFile as readFile8, writeFile as writeFile5, stat as stat4 } from "node:fs/promises";
104992
103924
  import { join as join10, sep as sep2 } from "node:path";
104993
103925
  function matchesExclude(pattern, root) {
104994
103926
  const re3 = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, "\0").replace(/\*/g, `[^${sep2 === "\\" ? "\\\\" : "/"}]*`).replace(/\u0000/g, ".*");
104995
103927
  return new RegExp(`^${re3}(?:[/\\\\].*)?$`).test(root);
104996
103928
  }
104997
- async function exists(path12) {
103929
+ async function exists(path13) {
104998
103930
  try {
104999
- await stat4(path12);
103931
+ await stat4(path13);
105000
103932
  return true;
105001
103933
  } catch (e) {
105002
103934
  if (e.code === "ENOENT") return false;
@@ -105013,7 +103945,7 @@ function createRecordingRuntime(deps) {
105013
103945
  if (deps.env.SPACECODE_RECORD?.trim().toLowerCase() === "off") return "SPACECODE_RECORD=off";
105014
103946
  let cfg = {};
105015
103947
  try {
105016
- cfg = JSON.parse(await readFile7(join10(deps.configDir, "config.json"), "utf8"));
103948
+ cfg = JSON.parse(await readFile8(join10(deps.configDir, "config.json"), "utf8"));
105017
103949
  } catch (e) {
105018
103950
  if (e.code !== "ENOENT") return `~/.spacecode/config.json is unreadable (${e instanceof Error ? e.message : String(e)}); fix it to record`;
105019
103951
  }
@@ -105162,21 +104094,21 @@ var init_skills_command = __esm({
105162
104094
  });
105163
104095
 
105164
104096
  // src/mcp/tool-cache-file.ts
105165
- import { readFile as readFile8 } from "node:fs/promises";
104097
+ import { readFile as readFile9 } from "node:fs/promises";
105166
104098
  import { join as join11 } from "node:path";
105167
104099
  function nodeToolCache(configDir) {
105168
- const path12 = join11(configDir, "mcp-tools-cache.json");
104100
+ const path13 = join11(configDir, "mcp-tools-cache.json");
105169
104101
  return {
105170
104102
  read: async () => {
105171
104103
  try {
105172
- return await readFile8(path12, "utf8");
104104
+ return await readFile9(path13, "utf8");
105173
104105
  } catch (err) {
105174
104106
  if (err.code === "ENOENT") return null;
105175
104107
  throw err;
105176
104108
  }
105177
104109
  },
105178
- write: (text) => writeTextAtomic(path12, text),
105179
- hash: sha2562
104110
+ write: (text) => writeTextAtomic(path13, text),
104111
+ hash: sha256
105180
104112
  };
105181
104113
  }
105182
104114
  var init_tool_cache_file = __esm({
@@ -105187,11 +104119,11 @@ var init_tool_cache_file = __esm({
105187
104119
  });
105188
104120
 
105189
104121
  // src/permission-rules-loader.ts
105190
- import { readFile as readFile9 } from "node:fs/promises";
104122
+ import { readFile as readFile10 } from "node:fs/promises";
105191
104123
  import { join as join12 } from "node:path";
105192
- async function readOrNull2(path12) {
104124
+ async function readOrNull2(path13) {
105193
104125
  try {
105194
- return await readFile9(path12, "utf8");
104126
+ return await readFile10(path13, "utf8");
105195
104127
  } catch (err) {
105196
104128
  if (err.code === "ENOENT") return null;
105197
104129
  throw err;
@@ -105258,7 +104190,7 @@ var init_permission_rules_loader = __esm({
105258
104190
  // src/import-reload.ts
105259
104191
  async function checkRepoSources(deps, loaded) {
105260
104192
  const files = await readRepoSources(deps.cwd);
105261
- const current = fingerprintSources(files, sha2562);
104193
+ const current = fingerprintSources(files, sha256);
105262
104194
  const status = Object.keys(files).length === 0 ? "untrusted" : await createNodeTrustStore(deps.configDir).repoStatus(deps.cwd, files);
105263
104195
  return { current, changes: diffSources(loaded, current), trusted: status === "trusted" || status === "session" };
105264
104196
  }
@@ -105279,7 +104211,7 @@ async function describeReload(deps, loaded) {
105279
104211
  async function trustCurrentSources(deps) {
105280
104212
  const files = await readRepoSources(deps.cwd);
105281
104213
  await createNodeTrustStore(deps.configDir).setRepo(deps.cwd, files, "trusted");
105282
- return fingerprintSources(files, sha2562);
104214
+ return fingerprintSources(files, sha256);
105283
104215
  }
105284
104216
  var init_import_reload = __esm({
105285
104217
  "src/import-reload.ts"() {
@@ -105379,7 +104311,7 @@ var init_command_loader = __esm({
105379
104311
  });
105380
104312
 
105381
104313
  // src/useReplController.ts
105382
- import { readFile as readFile10, writeFile as writeFile6, mkdir as mkdir6 } from "node:fs/promises";
104314
+ import { readFile as readFile11, writeFile as writeFile6, mkdir as mkdir6 } from "node:fs/promises";
105383
104315
  import { homedir as homedir2 } from "node:os";
105384
104316
  import { join as join14 } from "node:path";
105385
104317
  import { execFile as execFile4 } from "node:child_process";
@@ -105404,7 +104336,7 @@ function useReplController({ repoName: repoName2, yolo: yolo2, mentions: mention
105404
104336
  }, [turnStartedAt]);
105405
104337
  const projectRoot = (0, import_react30.useMemo)(() => process.cwd(), []);
105406
104338
  const promptTemplates = (0, import_react30.useCallback)(
105407
- () => loadPromptTemplates({ env: process.env, readFile: (p) => readFile10(p, "utf-8"), writeFile: (p, d) => writeFile6(p, d, "utf-8"), mkdir: (p) => mkdir6(p, { recursive: true }).then(() => void 0), homedir: homedir2, fetch }),
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 }),
105408
104340
  []
105409
104341
  );
105410
104342
  (0, import_react30.useEffect)(() => {
@@ -105424,7 +104356,7 @@ function useReplController({ repoName: repoName2, yolo: yolo2, mentions: mention
105424
104356
  projectRoot,
105425
104357
  fetch,
105426
104358
  baseUrl: backendUrl(process.env),
105427
- resolveApiKey: () => resolveApiKey({ env: process.env, readFile: (p) => readFile10(p, "utf-8"), homedir: homedir2 }).catch(() => null),
104359
+ resolveApiKey: () => resolveApiKey({ env: process.env, readFile: (p) => readFile11(p, "utf-8"), homedir: homedir2 }).catch(() => null),
105428
104360
  log: (m2) => console.error(`[agent-ui] ${m2}`)
105429
104361
  });
105430
104362
  const resumeListRef = (0, import_react30.useRef)([]);
@@ -105451,7 +104383,7 @@ function useReplController({ repoName: repoName2, yolo: yolo2, mentions: mention
105451
104383
  configDir: process.env.SPACECODE_CONFIG_DIR?.trim() || join14(homedir2(), ".spacecode"),
105452
104384
  cwd: process.cwd(),
105453
104385
  env: process.env,
105454
- readFile: (p) => readFile10(p, "utf-8"),
104386
+ readFile: (p) => readFile11(p, "utf-8"),
105455
104387
  homedir: homedir2,
105456
104388
  fetch,
105457
104389
  builtins: SLASH_COMMANDS.map((c) => c.name)
@@ -105677,12 +104609,12 @@ function useReplController({ repoName: repoName2, yolo: yolo2, mentions: mention
105677
104609
  return;
105678
104610
  }
105679
104611
  try {
105680
- const baseline = loadedSourcesRef.current ?? fingerprintSources(await readRepoSources(process.cwd()), sha2562);
104612
+ const baseline = loadedSourcesRef.current ?? fingerprintSources(await readRepoSources(process.cwd()), sha256);
105681
104613
  if (rest[0] === "trust") {
105682
104614
  loadedSourcesRef.current = await trustCurrentSources(deps);
105683
104615
  const loaded = await loadPermissionRules({ configDir, homeDir: homedir2(), cwd: process.cwd() });
105684
104616
  rulesRef.current = loaded.rules;
105685
- const cmds = await loadCommands({ configDir, cwd: process.cwd(), env: process.env, readFile: (p) => readFile10(p, "utf-8"), homedir: homedir2, fetch, builtins: SLASH_COMMANDS.map((c) => c.name) });
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) });
105686
104618
  setCommandsLoaded(cmds);
105687
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"));
105688
104620
  } else notice((await describeReload(deps, baseline)).join("\n"));
@@ -105762,7 +104694,7 @@ function useReplController({ repoName: repoName2, yolo: yolo2, mentions: mention
105762
104694
  }
105763
104695
  setIsRunning(true);
105764
104696
  try {
105765
- const apiKey2 = await resolveApiKey({ env: process.env, readFile: (p) => readFile10(p, "utf-8"), homedir: homedir2 });
104697
+ const apiKey2 = await resolveApiKey({ env: process.env, readFile: (p) => readFile11(p, "utf-8"), homedir: homedir2 });
105766
104698
  if (!apiKey2) {
105767
104699
  notice(MISSING_KEY_MESSAGE);
105768
104700
  return;
@@ -105784,7 +104716,7 @@ function useReplController({ repoName: repoName2, yolo: yolo2, mentions: mention
105784
104716
  }
105785
104717
  case "model": {
105786
104718
  try {
105787
- const apiKey2 = await resolveApiKey({ env: process.env, readFile: (p) => readFile10(p, "utf-8"), homedir: homedir2 });
104719
+ const apiKey2 = await resolveApiKey({ env: process.env, readFile: (p) => readFile11(p, "utf-8"), homedir: homedir2 });
105788
104720
  if (!apiKey2) {
105789
104721
  notice(MISSING_KEY_MESSAGE);
105790
104722
  return;
@@ -105819,7 +104751,7 @@ ${list2.map((c) => ` /${c.name} (${c.scope})${c.description ? ` ${c.descripti
105819
104751
  }
105820
104752
  case "skills": {
105821
104753
  const state = profileRef.current;
105822
- const apiKey2 = await resolveApiKey({ env: process.env, readFile: (p) => readFile10(p, "utf-8"), homedir: homedir2 }).catch(() => null);
104754
+ const apiKey2 = await resolveApiKey({ env: process.env, readFile: (p) => readFile11(p, "utf-8"), homedir: homedir2 }).catch(() => null);
105823
104755
  const client = apiKey2 ? createProfileClient({ baseUrl: backendUrl(process.env), apiKey: apiKey2, fetch }) : null;
105824
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)"] }));
105825
104757
  return;
@@ -105841,7 +104773,7 @@ ${list2.map((c) => ` /${c.name} (${c.scope})${c.description ? ` ${c.descripti
105841
104773
  try {
105842
104774
  apiKey = await resolveApiKey({
105843
104775
  env: process.env,
105844
- readFile: (p) => readFile10(p, "utf-8"),
104776
+ readFile: (p) => readFile11(p, "utf-8"),
105845
104777
  homedir: homedir2
105846
104778
  });
105847
104779
  } catch (err) {
@@ -105894,7 +104826,7 @@ ${list2.map((c) => ` /${c.name} (${c.scope})${c.description ? ` ${c.descripti
105894
104826
  }
105895
104827
  profilePromiseRef.current ??= prepareProfile({
105896
104828
  env: process.env,
105897
- readFile: (p) => readFile10(p, "utf-8"),
104829
+ readFile: (p) => readFile11(p, "utf-8"),
105898
104830
  writeFile: (p, d) => writeFile6(p, d, { encoding: "utf-8", mode: 384 }),
105899
104831
  mkdir: async (p) => {
105900
104832
  await mkdir6(p, { recursive: true });
@@ -105905,7 +104837,7 @@ ${list2.map((c) => ` /${c.name} (${c.scope})${c.description ? ` ${c.descripti
105905
104837
  gitRoot: gitTopLevel,
105906
104838
  importGate: async () => {
105907
104839
  const configDir = process.env.SPACECODE_CONFIG_DIR?.trim() || join14(homedir2(), ".spacecode");
105908
- const fingerprint = fingerprintSources(await readRepoSources(process.cwd()), sha2562);
104840
+ const fingerprint = fingerprintSources(await readRepoSources(process.cwd()), sha256);
105909
104841
  const gate = await importGateStatus({ configDir, cwd: process.cwd(), interactive: Boolean(process.stdin.isTTY) });
105910
104842
  const loaded = await loadPermissionRules({ configDir, homeDir: homedir2(), cwd: process.cwd() });
105911
104843
  rulesRef.current = loaded.rules;
@@ -106201,12 +105133,12 @@ var init_TurnViewer = __esm({
106201
105133
  });
106202
105134
 
106203
105135
  // src/render/resume-rows.ts
106204
- import path9 from "node:path";
105136
+ import path10 from "node:path";
106205
105137
  function orderSessions(sessions, projectRoot) {
106206
105138
  return [...sessions.filter((s) => s.projectRoot === projectRoot), ...sessions.filter((s) => s.projectRoot !== projectRoot)];
106207
105139
  }
106208
105140
  function formatResumeRow(meta, now, columns, sep3 = "\xB7", ellipsis = "\u2026") {
106209
- const project = path9.basename(meta.projectRoot) || meta.projectRoot;
105141
+ const project = path10.basename(meta.projectRoot) || meta.projectRoot;
106210
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} `);
106211
105143
  const head = `${formatAge(meta.updatedAt, now)} ${sep3} ${project} ${sep3} `;
106212
105144
  const fixed = head.length + 2 + ` ${sep3} ${tail}`.length;
@@ -106351,14 +105283,14 @@ var App_exports = {};
106351
105283
  __export(App_exports, {
106352
105284
  App: () => App2
106353
105285
  });
106354
- import os4 from "node:os";
106355
- import path10 from "node:path";
105286
+ import os5 from "node:os";
105287
+ import path11 from "node:path";
106356
105288
  function App2({ repoName: repoName2, onboardingMode: onboardingMode2, launchCount: launchCount2, yolo: yolo2, history, sessions, resumeLatest: resumeLatest2, git: gitRunner, interactivePicker = true, fullscreen: fullscreen2 = false }) {
106357
105289
  const promptHistory = (0, import_react34.useMemo)(
106358
- () => history ?? new PromptHistory(createFileHistoryIO(path10.join(os4.homedir(), ".spacecode", "history"))),
105290
+ () => history ?? new PromptHistory(createFileHistoryIO(path11.join(os5.homedir(), ".spacecode", "history"))),
106359
105291
  [history]
106360
105292
  );
106361
- const sessionStore = (0, import_react34.useMemo)(() => sessions ?? new SessionStore(path10.join(os4.homedir(), ".spacecode", "sessions")), [sessions]);
105293
+ const sessionStore = (0, import_react34.useMemo)(() => sessions ?? new SessionStore(path11.join(os5.homedir(), ".spacecode", "sessions")), [sessions]);
106362
105294
  const runGit = (0, import_react34.useMemo)(() => gitRunner ?? createNodeGitRunner(process.cwd()), [gitRunner]);
106363
105295
  const startedAtRef = (0, import_react34.useRef)(Date.now());
106364
105296
  const [viewingRaw, setViewing] = (0, import_react34.useState)(false);
@@ -106448,7 +105380,7 @@ function App2({ repoName: repoName2, onboardingMode: onboardingMode2, launchCoun
106448
105380
  viewing && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(TurnViewer, { turns: viewerTurns, steps: turnSteps, rows: rows - 2, columns, onClose: () => setViewing(false), onEscape: swallowEsc }),
106449
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 }),
106450
105382
  /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(Box_default, { flexDirection: "column", flexGrow: 1, display: hidden ? "none" : "flex", children: [
106451
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Box_default, { flexShrink: 0, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Header, { project: repoName2 ?? path10.basename(process.cwd()), git: gitState, turns: turns.length, elapsedMs: Date.now() - startedAtRef.current, columns }) }),
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 }) }),
106452
105384
  /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(Transcript, { scroll, onMeasure, children: [
106453
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)),
106454
105386
  /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(WelcomeBanner, { repoName: repoName2, onboardingMode: onboardingMode2, launchCount: launchCount2 }),
@@ -106478,7 +105410,7 @@ function App2({ repoName: repoName2, onboardingMode: onboardingMode2, launchCoun
106478
105410
  ] });
106479
105411
  }
106480
105412
  return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(Box_default, { flexDirection: "column", children: [
106481
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Header, { project: repoName2 ?? path10.basename(process.cwd()), git: gitState, turns: turns.length, elapsedMs: Date.now() - startedAtRef.current, columns }),
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 }),
106482
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)),
106483
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: [
106484
105416
  /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(Text, { ...paint("secondary"), children: [
@@ -106572,8 +105504,8 @@ init_color_policy();
106572
105504
  applyColorPolicy(process.env, Boolean(process.stdout.isTTY));
106573
105505
 
106574
105506
  // src/index.tsx
106575
- import os5 from "node:os";
106576
- import path11 from "node:path";
105507
+ import os6 from "node:os";
105508
+ import path12 from "node:path";
106577
105509
  import fs5 from "node:fs";
106578
105510
  import util2 from "node:util";
106579
105511
 
@@ -106659,7 +105591,7 @@ function clearOnResize(stdout) {
106659
105591
  var import_jsx_runtime14 = __toESM(require_jsx_runtime(), 1);
106660
105592
  if (process.argv[2] === "import-claude") {
106661
105593
  const { runImportClaude: runImportClaude2 } = await Promise.resolve().then(() => (init_import_claude(), import_claude_exports));
106662
- const { readFile: readFile11, readdir: readdir6 } = await import("node:fs/promises");
105594
+ const { readFile: readFile12, readdir: readdir6 } = await import("node:fs/promises");
106663
105595
  const { execFile: execFile5 } = await import("node:child_process");
106664
105596
  const gitRoot = () => new Promise((resolve2) => {
106665
105597
  execFile5("git", ["rev-parse", "--show-toplevel"], { cwd: process.cwd() }, (err, stdout) => resolve2(err ? null : stdout.trim() || null));
@@ -106667,9 +105599,9 @@ if (process.argv[2] === "import-claude") {
106667
105599
  const code = await runImportClaude2({
106668
105600
  argv: process.argv.slice(2),
106669
105601
  env: process.env,
106670
- fs: { readFile: (p) => readFile11(p, "utf-8"), readdir: (p) => readdir6(p) },
106671
- readFile: (p) => readFile11(p, "utf-8"),
106672
- homedir: os5.homedir,
105602
+ fs: { readFile: (p) => readFile12(p, "utf-8"), readdir: (p) => readdir6(p) },
105603
+ readFile: (p) => readFile12(p, "utf-8"),
105604
+ homedir: os6.homedir,
106673
105605
  cwd: process.cwd(),
106674
105606
  gitRoot,
106675
105607
  fetch,
@@ -106688,37 +105620,8 @@ if (process.argv[2] === "import-claude") {
106688
105620
  process.exit(code);
106689
105621
  }
106690
105622
  if (process.argv[2] === "approve-mcp") {
106691
- const { runApproveMcp: runApproveMcp2 } = await Promise.resolve().then(() => (init_approve_mcp(), approve_mcp_exports));
106692
- const { createNodeTrustStore: createNodeTrustStore2 } = await Promise.resolve().then(() => (init_import_gate(), import_gate_exports));
106693
- const { resolveApiKey: resolveApiKey2 } = await Promise.resolve().then(() => (init_api_key(), api_key_exports));
106694
- const { backendUrl: backendUrl2 } = await Promise.resolve().then(() => (init_profile_runtime(), profile_runtime_exports));
106695
- const { createProfileClient: createProfileClient2 } = await Promise.resolve().then(() => (init_src(), src_exports));
106696
- const { readFile: readFile11 } = await import("node:fs/promises");
106697
- const configDir = process.env.SPACECODE_CONFIG_DIR?.trim() || path11.join(os5.homedir(), ".spacecode");
106698
- const apiKey = await resolveApiKey2({ env: process.env, readFile: (p) => readFile11(p, "utf-8"), homedir: os5.homedir }).catch(() => null);
106699
- const interactive = Boolean(process.stdin.isTTY && process.stdout.isTTY);
106700
- const { createInterface } = interactive ? await import("node:readline/promises") : { createInterface: void 0 };
106701
- const rl = createInterface ? createInterface({ input: process.stdin, output: process.stdout }) : void 0;
106702
- try {
106703
- const code = await runApproveMcp2({
106704
- configDir,
106705
- cwd: process.cwd(),
106706
- readFile: (p) => readFile11(p, "utf-8"),
106707
- client: apiKey ? createProfileClient2({ baseUrl: backendUrl2(process.env), apiKey, fetch }) : null,
106708
- trust: createNodeTrustStore2(configDir),
106709
- ...rl ? { ask: async (q2) => {
106710
- try {
106711
- return await rl.question(q2);
106712
- } catch {
106713
- return null;
106714
- }
106715
- } } : {},
106716
- log: (line) => console.log(line)
106717
- });
106718
- process.exitCode = code;
106719
- } finally {
106720
- rl?.close();
106721
- }
105623
+ const { runApproveMcpOnNode: runApproveMcpOnNode2 } = await Promise.resolve().then(() => (init_approve_mcp_node(), approve_mcp_node_exports));
105624
+ process.exitCode = await runApproveMcpOnNode2({ quiet: false });
106722
105625
  process.exit(process.exitCode ?? 0);
106723
105626
  }
106724
105627
  var repoName = process.env.REPO_NAME || process.cwd().split("/").pop() || void 0;
@@ -106735,7 +105638,7 @@ if (process.stdin.isTTY && process.stdout.isTTY) {
106735
105638
  const { promptRepoTrust: promptRepoTrust2 } = await Promise.resolve().then(() => (init_repo_trust_prompt(), repo_trust_prompt_exports));
106736
105639
  const files = await readRepoSources2(process.cwd());
106737
105640
  if (Object.keys(files).length > 0) {
106738
- const store = createNodeTrustStore2(process.env.SPACECODE_CONFIG_DIR?.trim() || path11.join(os5.homedir(), ".spacecode"));
105641
+ const store = createNodeTrustStore2(process.env.SPACECODE_CONFIG_DIR?.trim() || path12.join(os6.homedir(), ".spacecode"));
106739
105642
  if (await store.repoStatus(process.cwd(), files) === "untrusted") {
106740
105643
  const { createInterface } = await import("node:readline/promises");
106741
105644
  const rl = createInterface({ input: process.stdin, output: process.stdout });
@@ -106763,6 +105666,15 @@ if (process.stdin.isTTY && process.stdout.isTTY) {
106763
105666
  `);
106764
105667
  }
106765
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
+ }
106766
105678
  var restoreCiVariables = hideCiVariables(process.env, Boolean(process.stdout.isTTY));
106767
105679
  var render3;
106768
105680
  var App3;
@@ -106774,9 +105686,9 @@ try {
106774
105686
  var fullscreen = shouldUseFullscreen(process.env, process.stdout, Boolean(process.stdin.isTTY));
106775
105687
  var session;
106776
105688
  if (fullscreen) {
106777
- const dir = path11.join(os5.homedir(), ".spacecode");
105689
+ const dir = path12.join(os6.homedir(), ".spacecode");
106778
105690
  fs5.mkdirSync(dir, { recursive: true });
106779
- const restoreConsole = redirectConsole(path11.join(dir, "repl.log"));
105691
+ const restoreConsole = redirectConsole(path12.join(dir, "repl.log"));
106780
105692
  session = enterFullscreen(process.stdout, process);
106781
105693
  session.onLeave(restoreConsole);
106782
105694
  session.onLeave(clearOnResize(process.stdout));