@inerrata-corporation/errata 2.0.0-dev.47 → 2.0.0-dev.49

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/errata.mjs +143 -45
  2. package/package.json +1 -1
package/errata.mjs CHANGED
@@ -20946,6 +20946,20 @@ var init_client = __esm({
20946
20946
  async createProject(name2) {
20947
20947
  return this.json("POST", "/api/gate/projects/create", { name: name2 });
20948
20948
  }
20949
+ /** Read-only discovery (PJ-attach clone tripwire): the org's claim on a
20950
+ * locator, or null. NEVER creates — 404 means the org hasn't linked it. */
20951
+ async lookupProject(locator) {
20952
+ try {
20953
+ const res = await this.json(
20954
+ "GET",
20955
+ `/api/gate/projects/lookup?locator=${encodeURIComponent(locator)}`
20956
+ );
20957
+ return res.project;
20958
+ } catch (err2) {
20959
+ if (err2 instanceof CloudError && err2.status === 404) return null;
20960
+ throw err2;
20961
+ }
20962
+ }
20949
20963
  // ─── ingest ────────────────────────────────────────────────────────
20950
20964
  /** Upload one outbox batch (projected onto the reconciled wire) and fold
20951
20965
  * the per-decision response into flush accounting.
@@ -21422,7 +21436,10 @@ function defaultConfig() {
21422
21436
  oauthClientId: null,
21423
21437
  userId: null,
21424
21438
  email: null,
21425
- consent: { sync: false, telemetry: false, contributePackages: false },
21439
+ // contributePackages defaults ON the dependency backbone rides project
21440
+ // linking (see ConsentConfig). Still `sync`-gated: nothing ships until the
21441
+ // user opts into sync at all.
21442
+ consent: { sync: false, telemetry: false, contributePackages: true },
21426
21443
  notifications: true,
21427
21444
  onboardedAt: null,
21428
21445
  machineId: null,
@@ -38269,8 +38286,8 @@ var init_report_render = __esm({
38269
38286
 
38270
38287
  // src/cli.ts
38271
38288
  init_src5();
38272
- import { closeSync as closeSync2, existsSync as existsSync21, openSync as openSync2, readFileSync as readFileSync20, renameSync as renameSync3, statSync as statSync6 } from "node:fs";
38273
- import { join as join24 } from "node:path";
38289
+ import { closeSync as closeSync2, existsSync as existsSync22, openSync as openSync2, readFileSync as readFileSync20, renameSync as renameSync3, statSync as statSync6 } from "node:fs";
38290
+ import { join as join25 } from "node:path";
38274
38291
  import { spawn as spawn3 } from "node:child_process";
38275
38292
 
38276
38293
  // src/daemon.ts
@@ -39586,9 +39603,9 @@ var NodeFsHandler = class {
39586
39603
  if (this.fsw.closed) {
39587
39604
  return;
39588
39605
  }
39589
- const dirname8 = sysPath.dirname(file2);
39606
+ const dirname9 = sysPath.dirname(file2);
39590
39607
  const basename5 = sysPath.basename(file2);
39591
- const parent = this.fsw._getWatchedDir(dirname8);
39608
+ const parent = this.fsw._getWatchedDir(dirname9);
39592
39609
  let prevStats = stats;
39593
39610
  if (parent.has(basename5))
39594
39611
  return;
@@ -39615,7 +39632,7 @@ var NodeFsHandler = class {
39615
39632
  prevStats = newStats2;
39616
39633
  }
39617
39634
  } catch (error48) {
39618
- this.fsw._remove(dirname8, basename5);
39635
+ this.fsw._remove(dirname9, basename5);
39619
39636
  }
39620
39637
  } else if (parent.has(basename5)) {
39621
39638
  const at = newStats.atimeMs;
@@ -42679,7 +42696,7 @@ function startLoopLagMonitor(thresholdMs = 1e3) {
42679
42696
  }
42680
42697
 
42681
42698
  // src/engine.ts
42682
- var DAEMON_VERSION = true ? "2.0.0-dev.47" : "2.0.0-alpha.0";
42699
+ var DAEMON_VERSION = true ? "2.0.0-dev.49" : "2.0.0-alpha.0";
42683
42700
  var IGNORED_PATH = /[\\/](?:\.git|node_modules|\.errata|\.claude|\.codex|\.cursor|\.turbo|\.next|dist|coverage|test-results|playwright-report|__pycache__)(?:[\\/]|$)/;
42684
42701
  var IGNORED_NOISE = /castalia\.db|eventlog\.sqlite|turn-cursors/;
42685
42702
  var GIT_OP_MUTE_MS = 4e3;
@@ -44761,6 +44778,48 @@ async function ensureProjectLink(root, profile, client) {
44761
44778
  }
44762
44779
  }
44763
44780
 
44781
+ // src/adopt.ts
44782
+ import { existsSync as existsSync21 } from "node:fs";
44783
+ import { dirname as dirname8, join as join24 } from "node:path";
44784
+ function findGitRoot(absPath) {
44785
+ let dir = absPath;
44786
+ for (let depth = 0; depth < 64; depth++) {
44787
+ if (existsSync21(join24(dir, ".git"))) return dir;
44788
+ const parent = dirname8(dir);
44789
+ if (parent === dir) return null;
44790
+ dir = parent;
44791
+ }
44792
+ return null;
44793
+ }
44794
+ var RETRY_COOLDOWN_MS = 15 * 6e4;
44795
+ function createRootAdopter(deps) {
44796
+ const attempted = /* @__PURE__ */ new Map();
44797
+ let chain = Promise.resolve();
44798
+ const tryAdopt = async (path2) => {
44799
+ if (!deps.linkingAllowed()) return;
44800
+ const root = findGitRoot(path2);
44801
+ if (!root || deps.isKnownRoot(root)) return;
44802
+ const last = attempted.get(root);
44803
+ const now = Date.now();
44804
+ if (last !== void 0 && now - last < RETRY_COOLDOWN_MS) return;
44805
+ attempted.set(root, now);
44806
+ if (!resolveGitDir(root)) return;
44807
+ const locator = detectRepoLocator(root);
44808
+ if (!locator) return;
44809
+ const claimed = await deps.lookup(locator);
44810
+ if (!claimed) return;
44811
+ await deps.adopt(root);
44812
+ deps.log(`[errata] adopted ${root} \u2014 ${locator} is claimed by your org (project ${claimed.id})`);
44813
+ };
44814
+ return {
44815
+ noteMiss(path2) {
44816
+ if (!path2) return;
44817
+ chain = chain.then(() => tryAdopt(path2)).catch(() => void 0);
44818
+ },
44819
+ idle: () => chain
44820
+ };
44821
+ }
44822
+
44764
44823
  // src/multi.ts
44765
44824
  function normPath(p) {
44766
44825
  return p.replace(/\\/g, "/").replace(/\/+$/, "").toLowerCase();
@@ -45023,7 +45082,10 @@ async function startMultiDaemon(opts = {}) {
45023
45082
  return c.json({ error: "bad-json" }, 400);
45024
45083
  }
45025
45084
  const owner = ownerOf(records, pathInBody(body2));
45026
- if (!owner) return c.json({ error: "no-matching-project" }, 404);
45085
+ if (!owner) {
45086
+ rootAdopter.noteMiss(pathInBody(body2));
45087
+ return c.json({ error: "no-matching-project" }, 404);
45088
+ }
45027
45089
  const sub = c.req.path;
45028
45090
  const done = markPass(`${sub}:${owner.entry.name}`);
45029
45091
  try {
@@ -45116,6 +45178,25 @@ async function startMultiDaemon(opts = {}) {
45116
45178
  if (opts.reindexOnStart !== false && records.length > 0) {
45117
45179
  void reindexAll({ skipEmbed: opts.skipEmbed ?? true });
45118
45180
  }
45181
+ const rootAdopter = createRootAdopter({
45182
+ isKnownRoot: (root) => pickOwnerRoot(records.map((r) => r.root), root) !== null,
45183
+ linkingAllowed: () => {
45184
+ const c = loadConfig();
45185
+ return c.consent.sync && hasCloudCredential(c);
45186
+ },
45187
+ lookup: async (locator) => cloudNow().lookupProject(locator),
45188
+ adopt: async (root) => {
45189
+ let profile = loadProfile(root);
45190
+ if (!profile) {
45191
+ profile = autodetectProfile(root);
45192
+ saveProfile(root, profile);
45193
+ }
45194
+ registerWorkspace(profile, root);
45195
+ attachWorkspace(root);
45196
+ },
45197
+ // eslint-disable-next-line no-console
45198
+ log: (line) => console.log(line)
45199
+ });
45119
45200
  const ambientLinkAll = async () => {
45120
45201
  const c = loadConfig();
45121
45202
  if (!c.consent.sync || !hasCloudCredential(c)) return;
@@ -46009,21 +46090,21 @@ async function cmdInit() {
46009
46090
  if (!skipHooks) {
46010
46091
  console.log("");
46011
46092
  console.log("installing harness hooks...");
46012
- const { existsSync: existsSync22 } = await import("node:fs");
46013
- const { join: join25 } = await import("node:path");
46093
+ const { existsSync: existsSync23 } = await import("node:fs");
46094
+ const { join: join26 } = await import("node:path");
46014
46095
  try {
46015
46096
  await installClaudeHooks(port);
46016
46097
  } catch (err2) {
46017
46098
  console.warn(` \u26A0\uFE0F Claude hook install failed: ${err2 instanceof Error ? err2.message : err2}`);
46018
46099
  }
46019
- if (existsSync22(join25(ROOT, ".cursor"))) {
46100
+ if (existsSync23(join26(ROOT, ".cursor"))) {
46020
46101
  try {
46021
46102
  await installCursorMcpConfig();
46022
46103
  } catch (err2) {
46023
46104
  console.warn(` \u26A0\uFE0F Cursor MCP config failed: ${err2 instanceof Error ? err2.message : err2}`);
46024
46105
  }
46025
46106
  }
46026
- if (existsSync22(join25(ROOT, ".codex"))) {
46107
+ if (existsSync23(join26(ROOT, ".codex"))) {
46027
46108
  try {
46028
46109
  await installCodexHooks(port);
46029
46110
  } catch (err2) {
@@ -46097,6 +46178,23 @@ async function cmdNotifyTest() {
46097
46178
  }
46098
46179
  }
46099
46180
  async function cmdStart() {
46181
+ if (!loadProfile(ROOT)) {
46182
+ const cfg = loadConfig();
46183
+ const locator = detectRepoLocator(ROOT);
46184
+ if (locator && cfg.consent.sync && hasCloudCredential(cfg)) {
46185
+ try {
46186
+ const claimed = await authedCloudClient(cfg).lookupProject(locator);
46187
+ if (claimed) {
46188
+ const adopted = autodetectProfile(ROOT);
46189
+ saveProfile(ROOT, adopted);
46190
+ console.log(
46191
+ `adopted: ${locator} is claimed by your org (project ${claimed.id}) \u2014 workspace initialized`
46192
+ );
46193
+ }
46194
+ } catch {
46195
+ }
46196
+ }
46197
+ }
46100
46198
  await ensureProfile();
46101
46199
  const profile = loadProfile(ROOT);
46102
46200
  if (profile) registerWorkspace(profile, ROOT);
@@ -46160,8 +46258,8 @@ async function cmdStatus() {
46160
46258
  console.log(` stack: ${profile.stack.join(", ") || "(none)"}`);
46161
46259
  console.log(` languages: ${profile.languages.join(", ") || "(none)"}`);
46162
46260
  }
46163
- console.log(` graph db: ${existsSync21(paths.castalia) ? "yes" : "no"} (${paths.castalia})`);
46164
- console.log(` event log: ${existsSync21(paths.eventLog) ? "yes" : "no"} (${paths.eventLog})`);
46261
+ console.log(` graph db: ${existsSync22(paths.castalia) ? "yes" : "no"} (${paths.castalia})`);
46262
+ console.log(` event log: ${existsSync22(paths.eventLog) ? "yes" : "no"} (${paths.eventLog})`);
46165
46263
  const lockPath = globalDaemonLock();
46166
46264
  const running = isDaemonAlive(lockPath) ? readDaemonLock(lockPath) : null;
46167
46265
  console.log(
@@ -46576,7 +46674,7 @@ async function cmdUse(args2) {
46576
46674
  }
46577
46675
  async function cmdReview() {
46578
46676
  const paths = workspacePaths(ROOT);
46579
- if (!existsSync21(paths.reviewQueue)) {
46677
+ if (!existsSync22(paths.reviewQueue)) {
46580
46678
  console.log("(review queue empty)");
46581
46679
  return;
46582
46680
  }
@@ -47210,7 +47308,7 @@ async function gatherRepo(store, ws) {
47210
47308
  };
47211
47309
  }
47212
47310
  async function gatherReportData(generatedAt) {
47213
- const { existsSync: existsSync22 } = await import("node:fs");
47311
+ const { existsSync: existsSync23 } = await import("node:fs");
47214
47312
  const { openGraphStore: openGraphStore2 } = await Promise.resolve().then(() => (init_src4(), src_exports2));
47215
47313
  const cfg = loadConfig();
47216
47314
  const outbound = cfg.consent.sync ? "auto" : "off";
@@ -47218,7 +47316,7 @@ async function gatherReportData(generatedAt) {
47218
47316
  for (const ws of listWorkspaces()) {
47219
47317
  if (ws.missing) continue;
47220
47318
  const dbPath = workspacePaths(ws.path).castalia;
47221
- if (!existsSync22(dbPath)) continue;
47319
+ if (!existsSync23(dbPath)) continue;
47222
47320
  let store = null;
47223
47321
  try {
47224
47322
  store = openGraphStore2({ path: dbPath });
@@ -47262,8 +47360,8 @@ async function cmdReport(args2) {
47262
47360
  const outDir = workspacePaths(ROOT).configDir;
47263
47361
  mkdirSync6(outDir, { recursive: true });
47264
47362
  const files = renderReport2(data, { includeFutureVerbs });
47265
- for (const f of files) writeFileSync16(join24(outDir, f.name), f.html, "utf8");
47266
- const indexPath = join24(outDir, "report.html");
47363
+ for (const f of files) writeFileSync16(join25(outDir, f.name), f.html, "utf8");
47364
+ const indexPath = join25(outDir, "report.html");
47267
47365
  console.log(`report \u2192 ${indexPath}`);
47268
47366
  console.log(` ${data.repos.length} repo(s) \xB7 ${files.length} file(s) \xB7 ${data.rollup.nodes.toLocaleString("en-US")} nodes`);
47269
47367
  console.log(` open: file://${indexPath.replace(/\\/g, "/")}`);
@@ -47383,13 +47481,13 @@ function hookRelayCommand(port, path2) {
47383
47481
  return process.platform === "win32" ? `cmd //c "curl -s --connect-timeout 1 --max-time 2 -X POST -H \\"Content-Type: application/json\\" --data-binary @- ${url2} 2>NUL || echo {}"` : `curl -s --connect-timeout 1 --max-time 2 -X POST -H 'Content-Type: application/json' --data-binary @- ${url2} 2>/dev/null || echo '{}'`;
47384
47482
  }
47385
47483
  async function installClaudeHooks(port) {
47386
- const { mkdirSync: mkdirSync6, existsSync: existsSync22, readFileSync: readFileSync21, writeFileSync: writeFileSync16 } = await import("node:fs");
47387
- const { join: join25 } = await import("node:path");
47388
- const dir = join25(ROOT, ".claude");
47389
- if (!existsSync22(dir)) mkdirSync6(dir, { recursive: true });
47390
- const file2 = join25(dir, "settings.json");
47484
+ const { mkdirSync: mkdirSync6, existsSync: existsSync23, readFileSync: readFileSync21, writeFileSync: writeFileSync16 } = await import("node:fs");
47485
+ const { join: join26 } = await import("node:path");
47486
+ const dir = join26(ROOT, ".claude");
47487
+ if (!existsSync23(dir)) mkdirSync6(dir, { recursive: true });
47488
+ const file2 = join26(dir, "settings.json");
47391
47489
  let settings = {};
47392
- if (existsSync22(file2)) {
47490
+ if (existsSync23(file2)) {
47393
47491
  try {
47394
47492
  settings = JSON.parse(readFileSync21(file2, "utf8"));
47395
47493
  } catch {
@@ -47440,7 +47538,7 @@ async function installClaudeHooks(port) {
47440
47538
  writeFileSync16(file2, JSON.stringify(settings, null, 2) + "\n", "utf8");
47441
47539
  console.log(`installed Claude Code hooks \u2192 ${file2}`);
47442
47540
  await installClaudeMcpConfig();
47443
- const claudeMd = join25(ROOT, "CLAUDE.md");
47541
+ const claudeMd = join26(ROOT, "CLAUDE.md");
47444
47542
  const recall = writeManagedBlock(claudeMd, { body: RECALL_FIRST_BLOCK });
47445
47543
  if (recall.kind === "collision") {
47446
47544
  console.warn(
@@ -47452,13 +47550,13 @@ async function installClaudeHooks(port) {
47452
47550
  console.log(` endpoint: http://127.0.0.1:${port}/api/hook`);
47453
47551
  }
47454
47552
  async function installClaudeMcpConfig() {
47455
- const { mkdirSync: mkdirSync6, existsSync: existsSync22, readFileSync: readFileSync21, writeFileSync: writeFileSync16 } = await import("node:fs");
47456
- const { join: join25, dirname: dirname8 } = await import("node:path");
47457
- const file2 = join25(ROOT, ".mcp.json");
47458
- const dir = dirname8(file2);
47459
- if (!existsSync22(dir)) mkdirSync6(dir, { recursive: true });
47553
+ const { mkdirSync: mkdirSync6, existsSync: existsSync23, readFileSync: readFileSync21, writeFileSync: writeFileSync16 } = await import("node:fs");
47554
+ const { join: join26, dirname: dirname9 } = await import("node:path");
47555
+ const file2 = join26(ROOT, ".mcp.json");
47556
+ const dir = dirname9(file2);
47557
+ if (!existsSync23(dir)) mkdirSync6(dir, { recursive: true });
47460
47558
  let cfg = {};
47461
- if (existsSync22(file2)) {
47559
+ if (existsSync23(file2)) {
47462
47560
  try {
47463
47561
  cfg = JSON.parse(readFileSync21(file2, "utf8"));
47464
47562
  } catch {
@@ -47474,13 +47572,13 @@ async function installClaudeMcpConfig() {
47474
47572
  console.log(` Tools: errata.search / locate / neighbors / callers_of / what_uses / show / similar`);
47475
47573
  }
47476
47574
  async function installCursorMcpConfig() {
47477
- const { mkdirSync: mkdirSync6, existsSync: existsSync22, readFileSync: readFileSync21, writeFileSync: writeFileSync16 } = await import("node:fs");
47478
- const { join: join25 } = await import("node:path");
47479
- const dir = join25(ROOT, ".cursor");
47480
- if (!existsSync22(dir)) mkdirSync6(dir, { recursive: true });
47481
- const file2 = join25(dir, "mcp.json");
47575
+ const { mkdirSync: mkdirSync6, existsSync: existsSync23, readFileSync: readFileSync21, writeFileSync: writeFileSync16 } = await import("node:fs");
47576
+ const { join: join26 } = await import("node:path");
47577
+ const dir = join26(ROOT, ".cursor");
47578
+ if (!existsSync23(dir)) mkdirSync6(dir, { recursive: true });
47579
+ const file2 = join26(dir, "mcp.json");
47482
47580
  let cfg = {};
47483
- if (existsSync22(file2)) {
47581
+ if (existsSync23(file2)) {
47484
47582
  try {
47485
47583
  cfg = JSON.parse(readFileSync21(file2, "utf8"));
47486
47584
  } catch {
@@ -47498,15 +47596,15 @@ async function installCursorMcpConfig() {
47498
47596
  console.log(` \u26A0\uFE0F reload Cursor (Cmd/Ctrl-Shift-P \u2192 "Reload Window") to pick it up.`);
47499
47597
  }
47500
47598
  async function installCodexHooks(port) {
47501
- const { mkdirSync: mkdirSync6, existsSync: existsSync22, readFileSync: readFileSync21, writeFileSync: writeFileSync16 } = await import("node:fs");
47502
- const { join: join25 } = await import("node:path");
47503
- const dir = join25(ROOT, ".codex");
47504
- if (!existsSync22(dir)) mkdirSync6(dir, { recursive: true });
47505
- const file2 = join25(dir, "config.toml");
47599
+ const { mkdirSync: mkdirSync6, existsSync: existsSync23, readFileSync: readFileSync21, writeFileSync: writeFileSync16 } = await import("node:fs");
47600
+ const { join: join26 } = await import("node:path");
47601
+ const dir = join26(ROOT, ".codex");
47602
+ if (!existsSync23(dir)) mkdirSync6(dir, { recursive: true });
47603
+ const file2 = join26(dir, "config.toml");
47506
47604
  const BEGIN = `# >>> errata hooks (errata-managed)`;
47507
47605
  const END = `# <<< errata hooks`;
47508
47606
  let existing = "";
47509
- if (existsSync22(file2)) {
47607
+ if (existsSync23(file2)) {
47510
47608
  existing = readFileSync21(file2, "utf8");
47511
47609
  const beginIdx = existing.indexOf(BEGIN);
47512
47610
  const endIdx = existing.indexOf(END);
@@ -47799,7 +47897,7 @@ async function cmdDash(args2) {
47799
47897
  await yieldToLoop2();
47800
47898
  try {
47801
47899
  const items = selectDurableMemory(handle2.sharedStore, r.engine.profile);
47802
- const res = bleedRules(join24(r.root, ".claude", "rules"), items);
47900
+ const res = bleedRules(join25(r.root, ".claude", "rules"), items);
47803
47901
  if (res.written || res.pruned) {
47804
47902
  console.log(
47805
47903
  `[rules:${r.entry.name}] ${res.written} collective principle(s) \u2192 .claude/rules` + (res.pruned ? `, ${res.pruned} pruned` : "")
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inerrata-corporation/errata",
3
- "version": "2.0.0-dev.47",
3
+ "version": "2.0.0-dev.49",
4
4
  "description": "errata - local-first observation engine for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {