vexp-cli 3.3.2 → 3.3.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.
Files changed (2) hide show
  1. package/dist/doctor.js +60 -1
  2. package/package.json +6 -6
package/dist/doctor.js CHANGED
@@ -946,6 +946,60 @@ export function resolveMemberParent(root, registryRoots) {
946
946
  }
947
947
  return null;
948
948
  }
949
+ /**
950
+ * Entries of `.vexp/workspace.json` the daemon skips: one with no folder
951
+ * behind it, and one that is a folder this workspace sits inside (serving
952
+ * it would index every project beside this one). The daemon said so only in
953
+ * its log, on every load of the config: a repository deleted without editing
954
+ * workspace.json filled two thirds of a daemon log while doctor reported
955
+ * the workspace healthy (field report, 2026-10-06). The log now says it
956
+ * once, so this is where it is still said a day later.
957
+ */
958
+ export function workspaceSkippedRepoFindings(root) {
959
+ const configPath = path.join(root, ".vexp", "workspace.json");
960
+ const realOf = (p) => {
961
+ try {
962
+ return fs.realpathSync.native(p);
963
+ }
964
+ catch {
965
+ return p;
966
+ }
967
+ };
968
+ const isFolder = (p) => {
969
+ try {
970
+ return fs.statSync(p).isDirectory();
971
+ }
972
+ catch {
973
+ return false;
974
+ }
975
+ };
976
+ // Paths resolve as the daemon resolves them (`resolve_repo_path` in
977
+ // vexp-core): against the workspace root, then against `.vexp/` (configs
978
+ // written before 2.2.1). Against the real folder, so that ".." steps out
979
+ // of it and not out of a link to it; and through `path.resolve`, so that
980
+ // on Windows a path with no drive letter takes the workspace's drive.
981
+ const real = realOf(root);
982
+ // A folder the workspace is inside of: the daemon serves none (`"."`, the
983
+ // workspace itself, is not one).
984
+ const holdsTheWorkspace = (dir) => {
985
+ const rel = path.relative(realOf(dir), real);
986
+ return rel !== "" && rel !== ".." && !rel.startsWith(".." + path.sep) && !path.isAbsolute(rel);
987
+ };
988
+ const out = [];
989
+ for (const r of listWorkspaceRepos(root)) {
990
+ const folders = [path.resolve(real, r.rawPath), path.resolve(real, ".vexp", r.rawPath)].filter(isFolder);
991
+ if (folders.some((dir) => !holdsTheWorkspace(dir)))
992
+ continue;
993
+ const where = `[${r.alias}] listed in ${configPath} at ${r.rawPath}`;
994
+ out.push({
995
+ level: WARN,
996
+ message: folders.length > 0
997
+ ? `${where}, a folder this workspace is inside of — the daemon skips it: serving it would index every project beside this one. Point the entry at the repository itself or remove it.`
998
+ : `${where}, but there is no folder there — the daemon skips it. Restore the folder or remove the entry.`,
999
+ });
1000
+ }
1001
+ return out;
1002
+ }
949
1003
  /**
950
1004
  * Coverage verdicts for the workspace at `root` AND every repo connected to
951
1005
  * it, each tagged with its alias. Each repo's indexer writes its own
@@ -1254,11 +1308,14 @@ export function hookActivityFindings(ha, ledger, nowS) {
1254
1308
  const days = count(h.period_days) || 7;
1255
1309
  const short = count(h.abstained_short);
1256
1310
  const empty = count(h.abstained_empty_index);
1257
- const skipped = short + empty;
1311
+ // Absent from a daemon that predates the key: zero.
1312
+ const agentText = count(h.abstained_agent_text);
1313
+ const skipped = short + empty + agentText;
1258
1314
  const analyzed = count(ledger.prompts_analyzed);
1259
1315
  const parts = [
1260
1316
  short > 0 ? `${short} under 8 words (follow-ups are never oriented)` : "",
1261
1317
  empty > 0 ? `${empty} while the index was empty` : "",
1318
+ agentText > 0 ? `${agentText} that were the agent's own text, nothing typed (a task notice, an approval)` : "",
1262
1319
  ]
1263
1320
  .filter(Boolean)
1264
1321
  .join(", ");
@@ -2338,6 +2395,8 @@ async function doctorChecks(onWorkspace) {
2338
2395
  // built by a daemon that is no longer running.
2339
2396
  // Every connected repo has its own coverage.json; each finding carries the
2340
2397
  // repo alias so a run at the primary sees the whole workspace.
2398
+ for (const f of workspaceSkippedRepoFindings(ws.root))
2399
+ line(f.level, f.message);
2341
2400
  for (const f of workspaceCoverageFindings(ws.root))
2342
2401
  line(f.level, f.message);
2343
2402
  // Whether each index applies the ignore rules on disk, read from the index
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vexp-cli",
3
- "version": "3.3.2",
3
+ "version": "3.3.3",
4
4
  "description": "Local-first context engine for AI coding agents. Pre-indexes your codebase into a dependency graph and feeds any MCP agent only the code that matters, so it stops rediscovering your repo before every change. Fable-5-level results from Opus 5 on SWE-bench Pro, at half the token price. Mechanical work verification and a PII/secret scanner. Works with Claude Code, Cursor, Codex, Copilot, Windsurf, Cline, Aider and 14 agents. Your code never leaves your machine.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -105,10 +105,10 @@
105
105
  },
106
106
  "homepage": "https://vexp.dev",
107
107
  "optionalDependencies": {
108
- "@vexp/core-linux-x64": "3.3.2",
109
- "@vexp/core-linux-arm64": "3.3.2",
110
- "@vexp/core-darwin-x64": "3.3.2",
111
- "@vexp/core-darwin-arm64": "3.3.2",
112
- "@vexp/core-win32-x64": "3.3.2"
108
+ "@vexp/core-linux-x64": "3.3.3",
109
+ "@vexp/core-linux-arm64": "3.3.3",
110
+ "@vexp/core-darwin-x64": "3.3.3",
111
+ "@vexp/core-darwin-arm64": "3.3.3",
112
+ "@vexp/core-win32-x64": "3.3.3"
113
113
  }
114
114
  }