@devstationlabs/cli 0.1.3 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/devstation.js +183 -51
  2. package/package.json +1 -1
package/devstation.js CHANGED
@@ -12510,7 +12510,7 @@ import { existsSync as existsSync12 } from "fs";
12510
12510
 
12511
12511
  // src/lib/agent/cli/args.ts
12512
12512
  var CLI_NAME = "devstation";
12513
- var VERSION = "0.1.3";
12513
+ var VERSION = "0.1.4";
12514
12514
  var COMMANDS = new Set([
12515
12515
  "chat",
12516
12516
  "run",
@@ -12731,7 +12731,7 @@ var SESSION_HELP = ` /undo rewind the last checkpoint
12731
12731
  import { accessSync, constants as constants2, existsSync as existsSync10 } from "fs";
12732
12732
 
12733
12733
  // src/lib/agent/git.ts
12734
- import { existsSync } from "fs";
12734
+ import { existsSync, readFileSync, statSync } from "fs";
12735
12735
  import { join } from "path";
12736
12736
 
12737
12737
  // src/lib/agent/shell.ts
@@ -12949,7 +12949,16 @@ function cloneTargetProblem(target) {
12949
12949
  // src/lib/agent/git.ts
12950
12950
  var CHECKPOINT_PREFIX = "agent checkpoint:";
12951
12951
  function isRepo(root) {
12952
- return existsSync(join(root, ".git"));
12952
+ const dotGit = join(root, ".git");
12953
+ if (!existsSync(dotGit))
12954
+ return false;
12955
+ try {
12956
+ if (statSync(dotGit).isFile())
12957
+ return readFileSync(dotGit, "utf8").startsWith("gitdir:");
12958
+ } catch {
12959
+ return false;
12960
+ }
12961
+ return existsSync(join(dotGit, "HEAD"));
12953
12962
  }
12954
12963
  function quote(value) {
12955
12964
  return `'${value.replace(/'/g, `'\\''`)}'`;
@@ -13074,7 +13083,7 @@ import {
13074
13083
  cpSync,
13075
13084
  existsSync as existsSync3,
13076
13085
  mkdirSync as mkdirSync2,
13077
- readFileSync as readFileSync2,
13086
+ readFileSync as readFileSync3,
13078
13087
  readdirSync as readdirSync2,
13079
13088
  rmSync,
13080
13089
  writeFileSync as writeFileSync2
@@ -13085,10 +13094,10 @@ import { dirname as dirname2, join as join3 } from "path";
13085
13094
  import {
13086
13095
  existsSync as existsSync2,
13087
13096
  mkdirSync,
13088
- readFileSync,
13097
+ readFileSync as readFileSync2,
13089
13098
  readdirSync,
13090
13099
  realpathSync,
13091
- statSync,
13100
+ statSync as statSync2,
13092
13101
  writeFileSync
13093
13102
  } from "fs";
13094
13103
  import { dirname, isAbsolute, join as join2, relative, resolve, sep } from "path";
@@ -13165,10 +13174,10 @@ class Workspace {
13165
13174
  if (!existsSync2(resolved.absolute)) {
13166
13175
  return { ok: false, reason: `There is no file at ${relativePath}.` };
13167
13176
  }
13168
- if (statSync(resolved.absolute).isDirectory()) {
13177
+ if (statSync2(resolved.absolute).isDirectory()) {
13169
13178
  return { ok: false, reason: `${relativePath} is a directory, not a file.` };
13170
13179
  }
13171
- const buffer = readFileSync(resolved.absolute);
13180
+ const buffer = readFileSync2(resolved.absolute);
13172
13181
  if (looksBinary(buffer)) {
13173
13182
  return {
13174
13183
  ok: false,
@@ -13188,10 +13197,10 @@ class Workspace {
13188
13197
  if (!resolved.ok)
13189
13198
  return { ok: false, reason: resolved.reason };
13190
13199
  if (existsSync2(resolved.absolute)) {
13191
- if (statSync(resolved.absolute).isDirectory()) {
13200
+ if (statSync2(resolved.absolute).isDirectory()) {
13192
13201
  return { ok: false, reason: `${relativePath} is a directory.` };
13193
13202
  }
13194
- if (looksBinary(readFileSync(resolved.absolute))) {
13203
+ if (looksBinary(readFileSync2(resolved.absolute))) {
13195
13204
  return { ok: false, reason: `${relativePath} is a binary file and was not overwritten.` };
13196
13205
  }
13197
13206
  }
@@ -13203,7 +13212,7 @@ class Workspace {
13203
13212
  const resolved = this.resolve(relativePath);
13204
13213
  return resolved.ok && existsSync2(resolved.absolute);
13205
13214
  }
13206
- list(subdir = ".") {
13215
+ list(subdir = ".", limit = Number.POSITIVE_INFINITY) {
13207
13216
  const base = subdir === "." || subdir === "" ? { ok: true, absolute: this.root } : this.resolve(subdir);
13208
13217
  if (!base.ok || !existsSync2(base.absolute))
13209
13218
  return [];
@@ -13212,6 +13221,8 @@ class Workspace {
13212
13221
  const out = [];
13213
13222
  const walk = (dir) => {
13214
13223
  for (const entry of readdirSync(dir, { withFileTypes: true })) {
13224
+ if (out.length >= limit)
13225
+ return;
13215
13226
  if (entry.name.startsWith(".") && skip.has(entry.name))
13216
13227
  continue;
13217
13228
  if (skip.has(entry.name))
@@ -13244,7 +13255,7 @@ function listSnapshots(root) {
13244
13255
  if (!existsSync3(manifest))
13245
13256
  continue;
13246
13257
  try {
13247
- found.push(JSON.parse(readFileSync2(manifest, "utf8")));
13258
+ found.push(JSON.parse(readFileSync3(manifest, "utf8")));
13248
13259
  } catch {}
13249
13260
  }
13250
13261
  return found.sort((a, b) => b.id.localeCompare(a.id));
@@ -18122,9 +18133,9 @@ import {
18122
18133
  chmodSync,
18123
18134
  existsSync as existsSync4,
18124
18135
  mkdirSync as mkdirSync3,
18125
- readFileSync as readFileSync3,
18136
+ readFileSync as readFileSync4,
18126
18137
  renameSync,
18127
- statSync as statSync2,
18138
+ statSync as statSync3,
18128
18139
  writeFileSync as writeFileSync3
18129
18140
  } from "fs";
18130
18141
  import { dirname as dirname4, join as join5 } from "path";
@@ -18159,7 +18170,7 @@ function readSettingsFile(path4, problems = []) {
18159
18170
  if (!existsSync4(path4))
18160
18171
  return {};
18161
18172
  try {
18162
- const raw = JSON.parse(readFileSync3(path4, "utf8"));
18173
+ const raw = JSON.parse(readFileSync4(path4, "utf8"));
18163
18174
  const out = {};
18164
18175
  if (raw.provider !== undefined) {
18165
18176
  const named = typeof raw.provider === "string" ? raw.provider.toLowerCase() : raw.provider;
@@ -18204,11 +18215,11 @@ function readCredentials(home, problems = []) {
18204
18215
  if (!existsSync4(path4))
18205
18216
  return {};
18206
18217
  try {
18207
- const mode = statSync2(path4).mode & 511;
18218
+ const mode = statSync3(path4).mode & 511;
18208
18219
  if (mode & 63) {
18209
18220
  problems.push(`${path4} is readable by other users (mode ${mode.toString(8)}). Fix it: chmod 600 ${path4}`);
18210
18221
  }
18211
- const raw = JSON.parse(readFileSync3(path4, "utf8"));
18222
+ const raw = JSON.parse(readFileSync4(path4, "utf8"));
18212
18223
  const out = {};
18213
18224
  for (const id of PROVIDER_IDS) {
18214
18225
  if (typeof raw[id] === "string" && raw[id].trim())
@@ -18456,7 +18467,7 @@ async function embedMissing(store, provider, options = {}) {
18456
18467
  import { join as join7 } from "path";
18457
18468
 
18458
18469
  // src/lib/agent/repo-session.ts
18459
- import { readdirSync as readdirSync3, readFileSync as readFileSync4, statSync as statSync3 } from "fs";
18470
+ import { readdirSync as readdirSync3, readFileSync as readFileSync5, statSync as statSync4 } from "fs";
18460
18471
  import { join as join6, relative as relative2, sep as sep2 } from "path";
18461
18472
  var SKIP_DIRS = new Set([
18462
18473
  ".git",
@@ -18500,9 +18511,9 @@ function readWorkspace(root, maxFileBytes = 1024 * 1024) {
18500
18511
  }
18501
18512
  if (!item.isFile())
18502
18513
  continue;
18503
- if (statSync3(full).size > maxFileBytes)
18514
+ if (statSync4(full).size > maxFileBytes)
18504
18515
  continue;
18505
- const buffer = readFileSync4(full);
18516
+ const buffer = readFileSync5(full);
18506
18517
  if (looksBinary(buffer))
18507
18518
  continue;
18508
18519
  files[relative2(root, full).split(sep2).join("/")] = buffer.toString("utf8");
@@ -18511,6 +18522,69 @@ function readWorkspace(root, maxFileBytes = 1024 * 1024) {
18511
18522
  walk3(root);
18512
18523
  return files;
18513
18524
  }
18525
+ var INDEX_SKIP_DIRS = new Set([
18526
+ ...SKIP_DIRS,
18527
+ "target",
18528
+ "coverage",
18529
+ "__pycache__",
18530
+ ".venv",
18531
+ "venv",
18532
+ ".gradle",
18533
+ "out"
18534
+ ]);
18535
+ var HIDDEN_KEEP = new Set([".github"]);
18536
+ function readWorkspaceBounded(root, opts = {}) {
18537
+ const maxFiles = opts.maxFiles ?? 20000;
18538
+ const until = Date.now() + (opts.deadlineMs ?? 1e4);
18539
+ const maxBytes = opts.maxFileBytes ?? 1024 * 1024;
18540
+ const files = {};
18541
+ let seen = 0;
18542
+ let stopped = null;
18543
+ const walk3 = (dir) => {
18544
+ let entries;
18545
+ try {
18546
+ entries = readdirSync3(dir, { withFileTypes: true });
18547
+ } catch {
18548
+ return;
18549
+ }
18550
+ for (const item of entries) {
18551
+ if (stopped)
18552
+ return;
18553
+ if (item.isSymbolicLink())
18554
+ continue;
18555
+ const full = join6(dir, item.name);
18556
+ if (item.isDirectory()) {
18557
+ if (INDEX_SKIP_DIRS.has(item.name))
18558
+ continue;
18559
+ if (item.name.startsWith(".") && !HIDDEN_KEEP.has(item.name))
18560
+ continue;
18561
+ walk3(full);
18562
+ continue;
18563
+ }
18564
+ if (!item.isFile())
18565
+ continue;
18566
+ if (seen >= maxFiles) {
18567
+ stopped = "max-files";
18568
+ return;
18569
+ }
18570
+ if ((seen & 63) === 0 && Date.now() > until) {
18571
+ stopped = "deadline";
18572
+ return;
18573
+ }
18574
+ seen++;
18575
+ try {
18576
+ if (statSync4(full).size > maxBytes)
18577
+ continue;
18578
+ const buffer = readFileSync5(full);
18579
+ if (looksBinary(buffer))
18580
+ continue;
18581
+ files[relative2(root, full).split(sep2).join("/")] = buffer.toString("utf8");
18582
+ } catch {}
18583
+ }
18584
+ };
18585
+ walk3(root);
18586
+ return { files, stopped };
18587
+ }
18514
18588
  var MAX_TITLE = 72;
18515
18589
  function proposalFor(goal, result, files) {
18516
18590
  const firstLine = (goal || result.summary).split(`
@@ -18879,7 +18953,7 @@ class MemoryStore {
18879
18953
  this.db.run("DELETE FROM chunks WHERE path = ?", [path4]);
18880
18954
  this.db.run("DELETE FROM files WHERE path = ?", [path4]);
18881
18955
  }
18882
- reindex(files) {
18956
+ reindex(files, options = {}) {
18883
18957
  const result = { scanned: 0, reindexed: 0, removed: 0, chunks: 0 };
18884
18958
  this.db.run("BEGIN");
18885
18959
  try {
@@ -18892,9 +18966,11 @@ class MemoryStore {
18892
18966
  result.chunks += this.replaceFile(path4, content);
18893
18967
  result.reindexed++;
18894
18968
  }
18895
- for (const gone of known) {
18896
- this.removeFile(gone);
18897
- result.removed++;
18969
+ if (!options.partial) {
18970
+ for (const gone of known) {
18971
+ this.removeFile(gone);
18972
+ result.removed++;
18973
+ }
18898
18974
  }
18899
18975
  this.db.run("COMMIT");
18900
18976
  } catch (error2) {
@@ -18979,8 +19055,27 @@ function openStore(root) {
18979
19055
  return new MemoryStore(storePath(root));
18980
19056
  }
18981
19057
  async function indexWorkspace(root, options = {}) {
19058
+ const started = Date.now();
19059
+ const home = options.home ?? process.env.HOME ?? "";
19060
+ const strip2 = (p) => p.replace(/\/+$/, "");
19061
+ if (home && strip2(root) === strip2(home)) {
19062
+ return {
19063
+ scanned: 0,
19064
+ reindexed: 0,
19065
+ removed: 0,
19066
+ chunks: 0,
19067
+ embedded: 0,
19068
+ embeddingModel: null,
19069
+ stopped: "home",
19070
+ ms: 0
19071
+ };
19072
+ }
18982
19073
  const store = options.store ?? openStore(root);
18983
- const result = store.reindex(readWorkspace(root));
19074
+ const read = readWorkspaceBounded(root, {
19075
+ maxFiles: options.maxFiles,
19076
+ deadlineMs: options.deadlineMs
19077
+ });
19078
+ const result = store.reindex(read.files, { partial: read.stopped !== null });
18984
19079
  let embedded = 0;
18985
19080
  if (options.embeddings) {
18986
19081
  try {
@@ -18989,11 +19084,17 @@ async function indexWorkspace(root, options = {}) {
18989
19084
  embedded = 0;
18990
19085
  }
18991
19086
  }
18992
- return { ...result, embedded, embeddingModel: options.embeddings?.model ?? null };
19087
+ return {
19088
+ ...result,
19089
+ embedded,
19090
+ embeddingModel: options.embeddings?.model ?? null,
19091
+ stopped: read.stopped,
19092
+ ms: Date.now() - started
19093
+ };
18993
19094
  }
18994
19095
 
18995
19096
  // src/lib/agent/memory/project-memory.ts
18996
- import { existsSync as existsSync5, mkdirSync as mkdirSync5, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "fs";
19097
+ import { existsSync as existsSync5, mkdirSync as mkdirSync5, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "fs";
18997
19098
  import { dirname as dirname6, join as join8 } from "path";
18998
19099
  var DEFAULT_FILE = "PROJECT_MEMORY.md";
18999
19100
  var HEADER = `# Project memory
@@ -19012,7 +19113,7 @@ function readMemory(root) {
19012
19113
  const path4 = memoryPath(root);
19013
19114
  if (!existsSync5(path4))
19014
19115
  return [];
19015
- return parseMemory(readFileSync5(path4, "utf8"));
19116
+ return parseMemory(readFileSync6(path4, "utf8"));
19016
19117
  }
19017
19118
  var ENTRY = /^- \[([^\]]+)\](?:\s*\(([^)]*)\))?\s+([\s\S]*)$/;
19018
19119
  function parseMemory(text) {
@@ -19036,7 +19137,7 @@ function remember(root, note, tag = null, now = new Date) {
19036
19137
  if (!text) {
19037
19138
  return { ok: false, path: path4, message: "There was nothing to remember." };
19038
19139
  }
19039
- const existing = existsSync5(path4) ? readFileSync5(path4, "utf8") : "";
19140
+ const existing = existsSync5(path4) ? readFileSync6(path4, "utf8") : "";
19040
19141
  const entries = parseMemory(existing);
19041
19142
  const normal = (value) => value.toLowerCase().replace(/\s+/g, " ").trim();
19042
19143
  if (entries.some((entry) => normal(entry.note) === normal(text))) {
@@ -19072,7 +19173,7 @@ function renderMemory(entries) {
19072
19173
 
19073
19174
  // src/lib/agent/mcp.ts
19074
19175
  import { spawn as spawn3 } from "child_process";
19075
- import { existsSync as existsSync6, readFileSync as readFileSync6 } from "fs";
19176
+ import { existsSync as existsSync6, readFileSync as readFileSync7 } from "fs";
19076
19177
  import { join as join9 } from "path";
19077
19178
  function configPaths(root, home = process.env.HOME ?? "") {
19078
19179
  return [
@@ -19087,7 +19188,7 @@ function loadConfig(root, home = process.env.HOME ?? "") {
19087
19188
  if (!existsSync6(path4))
19088
19189
  continue;
19089
19190
  try {
19090
- const parsed = JSON.parse(readFileSync6(path4, "utf8"));
19191
+ const parsed = JSON.parse(readFileSync7(path4, "utf8"));
19091
19192
  for (const [name, server] of Object.entries(parsed.mcpServers ?? {})) {
19092
19193
  if (server && typeof server.command === "string")
19093
19194
  merged.mcpServers[name] = server;
@@ -19347,11 +19448,11 @@ function failedResult(message) {
19347
19448
 
19348
19449
  // src/lib/agent/sandbox-exec.ts
19349
19450
  import { randomBytes } from "crypto";
19350
- import { existsSync as existsSync8, statSync as statSync5, unlinkSync } from "fs";
19451
+ import { existsSync as existsSync8, statSync as statSync6, unlinkSync } from "fs";
19351
19452
  import { isAbsolute as isAbsolute3, join as join11, relative as relative4, resolve as resolve3, sep as sep4 } from "path";
19352
19453
 
19353
19454
  // src/lib/agent/project.ts
19354
- import { existsSync as existsSync7, readFileSync as readFileSync7, readdirSync as readdirSync4, statSync as statSync4 } from "fs";
19455
+ import { existsSync as existsSync7, readFileSync as readFileSync8, readdirSync as readdirSync4, statSync as statSync5 } from "fs";
19355
19456
  import { dirname as dirname7, join as join10, relative as relative3, sep as sep3 } from "path";
19356
19457
  var SKIP = new Set([
19357
19458
  "node_modules",
@@ -19378,7 +19479,7 @@ var MANIFESTS = [
19378
19479
  var MAX_DEPTH = 2;
19379
19480
  function readScripts(absolute) {
19380
19481
  try {
19381
- const parsed = JSON.parse(readFileSync7(absolute, "utf8"));
19482
+ const parsed = JSON.parse(readFileSync8(absolute, "utf8"));
19382
19483
  return parsed.scripts ?? {};
19383
19484
  } catch {
19384
19485
  return {};
@@ -19411,7 +19512,7 @@ function detectManifests(root) {
19411
19512
  continue;
19412
19513
  const child = join10(dir, entry);
19413
19514
  try {
19414
- if (statSync4(child).isDirectory())
19515
+ if (statSync5(child).isDirectory())
19415
19516
  scan(child, depth + 1);
19416
19517
  } catch {}
19417
19518
  }
@@ -19685,7 +19786,7 @@ function sandboxExecutor(options) {
19685
19786
  return "The sandbox could not write to the workspace. Check the mount and try --no-sandbox.";
19686
19787
  }
19687
19788
  try {
19688
- const stat2 = statSync5(path4);
19789
+ const stat2 = statSync6(path4);
19689
19790
  const [uid, gid] = user.split(":").map(Number);
19690
19791
  if (stat2.uid !== uid || stat2.gid !== gid) {
19691
19792
  return `The sandbox writes files as ${stat2.uid}:${stat2.gid} but this account is ${user}. ` + "Files it creates would not be yours to edit, and git would stop trusting the " + "repository, so it was not started. Run with --no-sandbox, or set " + "DEVSTATION_SANDBOX_USER.";
@@ -19953,8 +20054,20 @@ function fail(reason) {
19953
20054
  function executeFileTool(workspace, name, args) {
19954
20055
  switch (name) {
19955
20056
  case "list_files": {
19956
- const files = workspace.list(String(args.path ?? "."));
19957
- return files.length === 0 ? { ok: true, output: "The workspace is empty." } : { ok: true, output: files.join(`
20057
+ const LIMIT = 2000;
20058
+ const files = workspace.list(String(args.path ?? "."), LIMIT + 1);
20059
+ if (files.length === 0)
20060
+ return { ok: true, output: "The workspace is empty." };
20061
+ if (files.length > LIMIT) {
20062
+ return {
20063
+ ok: true,
20064
+ output: `${files.slice(0, LIMIT).join(`
20065
+ `)}
20066
+
20067
+ ` + `(Stopped at ${LIMIT} files: this directory is very large. List a subdirectory, or search for what you need.)`
20068
+ };
20069
+ }
20070
+ return { ok: true, output: files.join(`
19958
20071
  `) };
19959
20072
  }
19960
20073
  case "read_file": {
@@ -19991,7 +20104,10 @@ function executeFileTool(workspace, name, args) {
19991
20104
  return fail("No search query was given.");
19992
20105
  const hits = [];
19993
20106
  let truncated = false;
19994
- for (const path4 of workspace.list()) {
20107
+ const MAX_SCAN = 20000;
20108
+ const scanned = workspace.list(".", MAX_SCAN + 1);
20109
+ const scanLimited = scanned.length > MAX_SCAN;
20110
+ for (const path4 of scanned.slice(0, MAX_SCAN)) {
19995
20111
  const file = workspace.read(path4);
19996
20112
  if (!file.ok)
19997
20113
  continue;
@@ -20010,14 +20126,18 @@ function executeFileTool(workspace, name, args) {
20010
20126
  if (truncated)
20011
20127
  break;
20012
20128
  }
20013
- if (hits.length === 0)
20014
- return { ok: true, output: `No match for "${args.query}".` };
20129
+ const scanNote = scanLimited ? `
20130
+
20131
+ (Searched the first ${MAX_SCAN} files only: this workspace is very large. Search a subdirectory.)` : "";
20132
+ if (hits.length === 0) {
20133
+ return { ok: true, output: `No match for "${args.query}".${scanNote}` };
20134
+ }
20015
20135
  return {
20016
20136
  ok: true,
20017
- output: truncated ? `${hits.join(`
20137
+ output: `${truncated ? `${hits.join(`
20018
20138
  `)}
20019
20139
  \u2026 more matches not shown` : hits.join(`
20020
- `)
20140
+ `)}${scanNote}`
20021
20141
  };
20022
20142
  }
20023
20143
  default:
@@ -20738,10 +20858,10 @@ import {
20738
20858
  appendFileSync,
20739
20859
  existsSync as existsSync9,
20740
20860
  mkdirSync as mkdirSync6,
20741
- readFileSync as readFileSync8,
20861
+ readFileSync as readFileSync9,
20742
20862
  readdirSync as readdirSync6,
20743
20863
  renameSync as renameSync2,
20744
- statSync as statSync6,
20864
+ statSync as statSync7,
20745
20865
  writeFileSync as writeFileSync5
20746
20866
  } from "fs";
20747
20867
  import { join as join12 } from "path";
@@ -20804,7 +20924,7 @@ class SessionStore {
20804
20924
  if (!existsSync9(path4))
20805
20925
  return null;
20806
20926
  try {
20807
- return JSON.parse(readFileSync8(path4, "utf8"));
20927
+ return JSON.parse(readFileSync9(path4, "utf8"));
20808
20928
  } catch {
20809
20929
  return null;
20810
20930
  }
@@ -20826,10 +20946,10 @@ class SessionStore {
20826
20946
  const path4 = this.eventPath(id);
20827
20947
  if (!existsSync9(path4))
20828
20948
  return { events: [], offset: 0 };
20829
- const size = statSync6(path4).size;
20949
+ const size = statSync7(path4).size;
20830
20950
  if (size <= fromByte)
20831
20951
  return { events: [], offset: size };
20832
- const text = readFileSync8(path4, "utf8").slice(fromByte);
20952
+ const text = readFileSync9(path4, "utf8").slice(fromByte);
20833
20953
  const events = [];
20834
20954
  let consumed = 0;
20835
20955
  for (const line of text.split(`
@@ -20973,9 +21093,21 @@ async function runCommand(context, goal, options = {}) {
20973
21093
  executor = built.executor;
20974
21094
  }
20975
21095
  const embeddings = embeddingsFromEnv();
21096
+ const firstIndex = !existsSync10(storePath(context.root));
21097
+ if (firstIndex && !options.quiet)
21098
+ terminal.out("Indexing the workspace so the agent can search it\u2026");
20976
21099
  const memory = openStore(context.root);
20977
21100
  try {
20978
- await indexWorkspace(context.root, { store: memory, embeddings });
21101
+ const indexed = await indexWorkspace(context.root, { store: memory, embeddings });
21102
+ if (!options.quiet) {
21103
+ if (indexed.stopped === "home") {
21104
+ terminal.err("Not indexing your home directory. The agent can still read and list files; cd into a project for a real index.");
21105
+ } else if (indexed.stopped) {
21106
+ terminal.err(`Indexed ${indexed.scanned} files and stopped at the ${indexed.stopped === "deadline" ? "time" : "file"} limit: this workspace is very large. cd into the project you mean.`);
21107
+ } else if (firstIndex || indexed.ms > 1500) {
21108
+ terminal.out(`Indexed ${indexed.scanned} files in ${(indexed.ms / 1000).toFixed(1)}s.`);
21109
+ }
21110
+ }
20979
21111
  } catch {}
20980
21112
  const mcp = await McpHub.start(loadConfig(context.root));
20981
21113
  for (const status of mcp.status) {
@@ -22160,7 +22292,7 @@ function lineReader(rl, write) {
22160
22292
  // src/lib/agent/cli/upgrade.ts
22161
22293
  import { spawnSync } from "child_process";
22162
22294
  import { createHash as createHash2 } from "crypto";
22163
- import { chmodSync as chmodSync2, mkdirSync as mkdirSync8, readFileSync as readFileSync9, renameSync as renameSync3, writeFileSync as writeFileSync7 } from "fs";
22295
+ import { chmodSync as chmodSync2, mkdirSync as mkdirSync8, readFileSync as readFileSync10, renameSync as renameSync3, writeFileSync as writeFileSync7 } from "fs";
22164
22296
  import { dirname as dirname8, join as join14 } from "path";
22165
22297
  var PACKAGE = "@devstationlabs/cli";
22166
22298
  var REPO = "linoxbt/dev-shipyard";
@@ -22301,7 +22433,7 @@ function notifyIfOutdated(terminal, opts = {}) {
22301
22433
  const path4 = join14(homeDir, ".devstation", "update-check.json");
22302
22434
  let cached = {};
22303
22435
  try {
22304
- cached = JSON.parse(readFileSync9(path4, "utf8"));
22436
+ cached = JSON.parse(readFileSync10(path4, "utf8"));
22305
22437
  } catch {}
22306
22438
  if (cached.latest && compareVersions(cached.latest, VERSION) > 0) {
22307
22439
  terminal.err(`devstation ${cached.latest} is available (you have ${VERSION}). Run: devstation upgrade`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@devstationlabs/cli",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "The DevStation coding agent, in your terminal.",
5
5
  "license": "MIT",
6
6
  "type": "module",