@wrongstack/tools 0.272.1 → 0.273.0

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/builtin.js CHANGED
@@ -1,13 +1,13 @@
1
1
  import { spawn, execFileSync } from 'node:child_process';
2
2
  import * as Core from '@wrongstack/core';
3
- import { buildChildEnv, detectNewlineStyle, normalizeToLf, toStyle, atomicWrite, unifiedDiff, isPrivateIPv4, isPrivateIPv6, assessCommitSafety, compileGlob, expectDefined, recordPackageAction, detectPackageEcosystem, mutatePlan, clearPlan, getPlanTemplate, addPlanItem, deriveTodosFromPlanItem, removePlanItem, setPlanItemStatus, mutateTasks, formatTaskList, formatPlan, toErrorMessage, computeTaskItemProgress, loadPlan, savePlan, loadTasks, saveTasks, wstackGlobalRoot, resolveWstackPaths, truncate } from '@wrongstack/core';
3
+ import { buildChildEnv, detectNewlineStyle, normalizeToLf, toStyle, atomicWrite, unifiedDiff, isPrivateIPv4, isPrivateIPv6, assessCommitSafety, compileGlob, expectDefined, recordPackageAction, detectPackageEcosystem, mutatePlan, clearPlan, getPlanTemplate, addPlanItem, deriveTodosFromPlanItem, removePlanItem, setPlanItemStatus, mutateTasks, formatTaskList, formatPlan, toErrorMessage as toErrorMessage$1, computeTaskItemProgress, loadPlan, savePlan, loadTasks, saveTasks, wstackGlobalRoot, resolveWstackPaths, truncate } from '@wrongstack/core';
4
4
  import * as fs from 'node:fs';
5
5
  import { statSync, mkdirSync, createWriteStream } from 'node:fs';
6
6
  import * as fs2 from 'node:fs/promises';
7
7
  import * as path3 from 'node:path';
8
8
  import { resolve, sep, dirname, join } from 'node:path';
9
9
  import * as os2 from 'node:os';
10
- import { toErrorMessage as toErrorMessage$1 } from '@wrongstack/core/utils';
10
+ import { toErrorMessage as toErrorMessage$2 } from '@wrongstack/core/utils';
11
11
  import { createRequire } from 'node:module';
12
12
  import { fileURLToPath } from 'node:url';
13
13
  import { Worker } from 'node:worker_threads';
@@ -1084,6 +1084,21 @@ function parseAuditOutput(json, exitCode) {
1084
1084
  }
1085
1085
  }
1086
1086
  var REGISTRY_FILE = ".wrongstack/process-registry.json";
1087
+ function toErrorMessage(err) {
1088
+ return err instanceof Error ? err.message : String(err);
1089
+ }
1090
+ function emitStructuredLog(level, event, message, error) {
1091
+ const payload = {
1092
+ level,
1093
+ event,
1094
+ message,
1095
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
1096
+ };
1097
+ if (error !== void 0) {
1098
+ payload.error = toErrorMessage(error);
1099
+ }
1100
+ console.log(JSON.stringify(payload));
1101
+ }
1087
1102
  var HEARTBEAT_INTERVAL_MS = 5e3;
1088
1103
  var STALE_THRESHOLD_MS = 3e4;
1089
1104
  var LOCKFILE = ".wrongstack/.process-registry.lock";
@@ -1093,6 +1108,9 @@ function generateInstanceId() {
1093
1108
  const random = Math.random().toString(36).slice(2, 8);
1094
1109
  return `${hostname2}:${pid}:${random}`;
1095
1110
  }
1111
+ function isNodeError(err) {
1112
+ return typeof err === "object" && err !== null && "code" in err;
1113
+ }
1096
1114
  async function acquireLock(lockfilePath, timeoutMs = 5e3) {
1097
1115
  const start = Date.now();
1098
1116
  const pidStr = String(process.pid);
@@ -1107,7 +1125,7 @@ async function acquireLock(lockfilePath, timeoutMs = 5e3) {
1107
1125
  }
1108
1126
  };
1109
1127
  } catch (err) {
1110
- if (err.code === "EEXIST") {
1128
+ if (isNodeError(err) && err.code === "EEXIST") {
1111
1129
  try {
1112
1130
  const content = await fs2.readFile(lockfilePath, "utf-8");
1113
1131
  const parts = content.split(":");
@@ -1144,7 +1162,7 @@ async function readRegistryFile(filePath) {
1144
1162
  }
1145
1163
  return parsed;
1146
1164
  } catch (err) {
1147
- if (err.code === "ENOENT") {
1165
+ if (isNodeError(err) && err.code === "ENOENT") {
1148
1166
  return {
1149
1167
  version: 1,
1150
1168
  instances: /* @__PURE__ */ new Map(),
@@ -1180,7 +1198,7 @@ var PersistentProcessRegistry = class {
1180
1198
  this.lockPath = path3.join(homeDir, LOCKFILE);
1181
1199
  this.baseRegistry = baseRegistry ?? getProcessRegistry();
1182
1200
  this.ensureDirectory().catch((err) => {
1183
- console.error("PersistentProcessRegistry: failed to create .wrongstack dir", err);
1201
+ emitStructuredLog("warn", "process_registry.dir_create_failed", "PersistentProcessRegistry: failed to create .wrongstack directory", err);
1184
1202
  });
1185
1203
  }
1186
1204
  async ensureDirectory() {
@@ -1188,7 +1206,7 @@ var PersistentProcessRegistry = class {
1188
1206
  try {
1189
1207
  await fs2.mkdir(dir, { recursive: true });
1190
1208
  } catch (err) {
1191
- if (err.code !== "EEXIST") throw err;
1209
+ if (!isNodeError(err) || err.code !== "EEXIST") throw err;
1192
1210
  }
1193
1211
  }
1194
1212
  /**
@@ -1268,6 +1286,7 @@ var PersistentProcessRegistry = class {
1268
1286
  try {
1269
1287
  const data = await readRegistryFile(this.registryPath);
1270
1288
  data.instances.set(String(entry.pid), entry);
1289
+ const child = null;
1271
1290
  this.baseRegistry.register({
1272
1291
  pid: entry.pid,
1273
1292
  name: entry.name,
@@ -1275,8 +1294,7 @@ var PersistentProcessRegistry = class {
1275
1294
  startedAt: entry.startedAt,
1276
1295
  sessionId: entry.sessionId,
1277
1296
  protected: entry.protected,
1278
- child: null
1279
- // Main process has no child handle
1297
+ child
1280
1298
  });
1281
1299
  await writeRegistryFile(this.registryPath, data);
1282
1300
  } finally {
@@ -1324,7 +1342,7 @@ var PersistentProcessRegistry = class {
1324
1342
  data.lastCleanup = now;
1325
1343
  await writeRegistryFile(this.registryPath, data);
1326
1344
  } catch (err) {
1327
- console.error("PersistentProcessRegistry: sync failed", err);
1345
+ emitStructuredLog("warn", "process_registry.sync_failed", "PersistentProcessRegistry: sync failed", err);
1328
1346
  } finally {
1329
1347
  await release();
1330
1348
  }
@@ -1345,7 +1363,11 @@ var PersistentProcessRegistry = class {
1345
1363
  if (process.platform !== "win32") {
1346
1364
  process.kill(entry.pid, 0);
1347
1365
  } else {
1348
- console.log(`PersistentProcessRegistry: checking stale pid ${entry.pid} (${age}ms old)`);
1366
+ emitStructuredLog(
1367
+ "debug",
1368
+ "process_registry.stale_pid_check",
1369
+ `PersistentProcessRegistry: checking stale pid ${entry.pid} (${age}ms old)`
1370
+ );
1349
1371
  }
1350
1372
  } catch {
1351
1373
  stalePids.push(_pidStr);
@@ -1359,7 +1381,7 @@ var PersistentProcessRegistry = class {
1359
1381
  await writeRegistryFile(this.registryPath, data);
1360
1382
  }
1361
1383
  } catch (err) {
1362
- console.error("PersistentProcessRegistry: cleanup failed", err);
1384
+ emitStructuredLog("warn", "process_registry.cleanup_failed", "PersistentProcessRegistry: cleanup failed", err);
1363
1385
  } finally {
1364
1386
  await release();
1365
1387
  }
@@ -1582,7 +1604,7 @@ async function isKillProtected(kill) {
1582
1604
  const entries = await getProtectedEntries();
1583
1605
  const killNameLower = kill.name.toLowerCase();
1584
1606
  for (const entry of entries) {
1585
- if (entry.name && entry.name.toLowerCase().includes(killNameLower)) {
1607
+ if (entry.name?.toLowerCase().includes(killNameLower)) {
1586
1608
  return true;
1587
1609
  }
1588
1610
  }
@@ -2346,7 +2368,7 @@ function loadDatabaseSync() {
2346
2368
  DatabaseSyncCtor = req("node:sqlite").DatabaseSync;
2347
2369
  } catch (err) {
2348
2370
  throw new Error(
2349
- `The codebase index needs Node's built-in SQLite (node:sqlite), available since Node 22.5. This runtime doesn't provide it: ${toErrorMessage$1(err)}`
2371
+ `The codebase index needs Node's built-in SQLite (node:sqlite), available since Node 22.5. This runtime doesn't provide it: ${toErrorMessage$2(err)}`
2350
2372
  );
2351
2373
  }
2352
2374
  return DatabaseSyncCtor;
@@ -3683,11 +3705,15 @@ async function tryNativeParse(file, content) {
3683
3705
  const crateDir = path3.join(toolsDir, "syn-parser");
3684
3706
  const tmpFile = path3.join(crateDir, "src", "input.rs");
3685
3707
  await fs2.writeFile(tmpFile, content, "utf8");
3686
- const proc = spawn("cargo", ["run", "--manifest-path", path3.join(toolsDir, "Cargo.toml")], {
3687
- cwd: process.cwd(),
3688
- stdio: ["pipe", "pipe", "pipe"],
3689
- windowsHide: true
3690
- });
3708
+ const proc = spawn(
3709
+ "cargo",
3710
+ ["run", "--manifest-path", path3.join(toolsDir, "Cargo.toml")],
3711
+ {
3712
+ cwd: process.cwd(),
3713
+ stdio: ["pipe", "pipe", "pipe"],
3714
+ windowsHide: true
3715
+ }
3716
+ );
3691
3717
  let stdout = "";
3692
3718
  proc.stdout?.on("data", (chunk) => {
3693
3719
  stdout += chunk.toString();
@@ -7842,7 +7868,7 @@ var readTool = {
7842
7868
  } catch (err) {
7843
7869
  const code = err.code;
7844
7870
  if (code === "ENOENT") throw new Error(`read: file not found "${input.path}"`);
7845
- throw new Error(`read: failed to stat "${input.path}": ${toErrorMessage(err)}`);
7871
+ throw new Error(`read: failed to stat "${input.path}": ${toErrorMessage$1(err)}`);
7846
7872
  }
7847
7873
  if (!stat11.isFile()) throw new Error(`read: "${input.path}" is not a regular file`);
7848
7874
  if (stat11.size > MAX_BYTES2) {
@@ -8017,7 +8043,7 @@ var replaceTool = {
8017
8043
  if (err.code === "ENOENT") return null;
8018
8044
  throw err;
8019
8045
  });
8020
- if (!lstat2 || !lstat2.isFile()) continue;
8046
+ if (!lstat2?.isFile()) continue;
8021
8047
  if (lstat2.isSymbolicLink()) continue;
8022
8048
  let realPath;
8023
8049
  try {
@@ -8028,7 +8054,7 @@ var replaceTool = {
8028
8054
  const rel = path3.relative(realRoot, realPath);
8029
8055
  if (rel.startsWith("..") || path3.isAbsolute(rel)) continue;
8030
8056
  const stat11 = await fs2.stat(realPath).catch(() => null);
8031
- if (!stat11 || !stat11.isFile()) continue;
8057
+ if (!stat11?.isFile()) continue;
8032
8058
  let content;
8033
8059
  try {
8034
8060
  const buf = await fs2.readFile(realPath);
@@ -8438,7 +8464,7 @@ async function duckduckgoSearch(query2, num, signal) {
8438
8464
  truncated: results.length >= num
8439
8465
  };
8440
8466
  } catch (err) {
8441
- console.log(JSON.stringify({ level: "debug", event: "search_failed", query: query2, error: toErrorMessage$1(err) }));
8467
+ console.log(JSON.stringify({ level: "debug", event: "search_failed", query: query2, error: toErrorMessage$2(err) }));
8442
8468
  return {
8443
8469
  query: query2,
8444
8470
  results: [{ title: "Search unavailable", url: "", snippet: "Could not reach DuckDuckGo" }],
@@ -8602,7 +8628,7 @@ var setWorkingDirTool = {
8602
8628
  } catch (err) {
8603
8629
  return {
8604
8630
  current: ctx.workingDir,
8605
- error: toErrorMessage$1(err)
8631
+ error: toErrorMessage$2(err)
8606
8632
  };
8607
8633
  }
8608
8634
  try {
@@ -8752,7 +8778,11 @@ var taskTool = {
8752
8778
  const newIds = new Set(input.tasks.map((t) => t.id));
8753
8779
  if (newIds.size !== input.tasks.length) {
8754
8780
  const seen = /* @__PURE__ */ new Set();
8755
- const dupes = [...new Set(input.tasks.map((t) => t.id).filter((id) => seen.has(id) ? true : (seen.add(id), false)))];
8781
+ const dupes = [...new Set(input.tasks.map((t) => t.id).filter((id) => {
8782
+ if (seen.has(id)) return true;
8783
+ seen.add(id);
8784
+ return false;
8785
+ }))];
8756
8786
  early = {
8757
8787
  ok: false,
8758
8788
  message: `action=replace has duplicate task IDs: ${dupes.join(", ")}. Each task id must be unique.`,
@@ -8787,7 +8817,7 @@ var taskTool = {
8787
8817
  }
8788
8818
  case "add": {
8789
8819
  const t = input.task;
8790
- if (!t || !t.title) {
8820
+ if (!t?.title) {
8791
8821
  early = { ok: false, message: "action=add requires `task` with at least `title`.", count: 0, completed: 0, inProgress: 0 };
8792
8822
  return f;
8793
8823
  }