@inerrata-corporation/errata 2.0.2-dev.1218 → 2.0.2-dev.1255

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 +305 -6
  2. package/package.json +1 -1
package/errata.mjs CHANGED
@@ -53370,6 +53370,7 @@ function parseDesignResolutions(text) {
53370
53370
  }
53371
53371
  return out2;
53372
53372
  }
53373
+ var OPENCODE_TRANSCRIPT_HEADER_TYPE = "errata-opencode-session";
53373
53374
  var FILE_TOOLS = /* @__PURE__ */ new Set(["Read", "Edit", "Write", "MultiEdit", "NotebookEdit"]);
53374
53375
  var WRITE_TOOLS = /* @__PURE__ */ new Set(["Edit", "Write", "MultiEdit", "NotebookEdit"]);
53375
53376
  function isUserTurnBoundary(obj) {
@@ -53421,6 +53422,9 @@ function detectHostHarness(raw2) {
53421
53422
  if (obj.type === "gemini" || "$set" in obj || "projectHash" in obj && "sessionId" in obj || obj.type === "user" && !("message" in obj) && "content" in obj) {
53422
53423
  return "gemini";
53423
53424
  }
53425
+ if (obj.type === OPENCODE_TRANSCRIPT_HEADER_TYPE || obj.type === void 0 && "info" in obj && Array.isArray(obj["parts"]) && typeof obj["info"]?.["role"] === "string") {
53426
+ return "opencode";
53427
+ }
53424
53428
  if (obj.type === "user" || obj.type === "assistant" || obj.type === "summary") {
53425
53429
  return "claude_code";
53426
53430
  }
@@ -53432,8 +53436,44 @@ function parseAssistantTurns(raw2, includeThinking, skipFirstLineParseFailure =
53432
53436
  const harness = detectHostHarness(raw2);
53433
53437
  if (harness === "codex") return parseCodexAssistantTurns(raw2, skipFirstLineParseFailure);
53434
53438
  if (harness === "gemini") return parseGeminiAssistantTurns(raw2, skipFirstLineParseFailure);
53439
+ if (harness === "opencode") return parseOpencodeAssistantTurns(raw2, skipFirstLineParseFailure);
53435
53440
  return parseClaudeAssistantTurns(raw2, includeThinking, skipFirstLineParseFailure);
53436
53441
  }
53442
+ function parseOpencodeAssistantTurns(raw2, skipFirstLineParseFailure = false) {
53443
+ const lines = raw2.split(/\r?\n/);
53444
+ const byId = /* @__PURE__ */ new Map();
53445
+ let order = 0;
53446
+ let parseFailures = 0;
53447
+ for (let i2 = 0; i2 < lines.length; i2++) {
53448
+ const line = lines[i2];
53449
+ if (!line) continue;
53450
+ let obj;
53451
+ try {
53452
+ obj = JSON.parse(line);
53453
+ } catch {
53454
+ if (!(i2 === 0 && skipFirstLineParseFailure)) parseFailures++;
53455
+ continue;
53456
+ }
53457
+ if (!obj || typeof obj !== "object") continue;
53458
+ if (obj["type"] === OPENCODE_TRANSCRIPT_HEADER_TYPE) continue;
53459
+ const info2 = obj["info"];
53460
+ const parts2 = obj["parts"];
53461
+ if (!info2 || typeof info2 !== "object" || !Array.isArray(parts2)) continue;
53462
+ if (info2["role"] !== "assistant") continue;
53463
+ const texts = [];
53464
+ for (const p of parts2) {
53465
+ if (!p || p["type"] !== "text") continue;
53466
+ if (p["synthetic"] === true || p["ignored"] === true) continue;
53467
+ if (typeof p["text"] === "string" && p["text"].length > 0) texts.push(p["text"]);
53468
+ }
53469
+ if (texts.length === 0) continue;
53470
+ const id = typeof info2["id"] === "string" ? info2["id"] : `opencode-${i2}`;
53471
+ const prev = byId.get(id);
53472
+ byId.set(id, { text: texts.join("\n\n"), order: prev ? prev.order : order++ });
53473
+ }
53474
+ const turns = [...byId.entries()].sort((a, b) => a[1].order - b[1].order).map(([uuid3, v]) => ({ uuid: uuid3, text: v.text, hostHarness: "opencode" }));
53475
+ return { turns, parseFailures };
53476
+ }
53437
53477
  function parseGeminiAssistantTurns(raw2, skipFirstLineParseFailure = false) {
53438
53478
  const lines = raw2.split(/\r?\n/);
53439
53479
  const byId = /* @__PURE__ */ new Map();
@@ -53805,6 +53845,71 @@ function geminiTranscriptsForCwd(cwd, opts = {}) {
53805
53845
  out2.sort((a, b) => b.mtimeMs - a.mtimeMs);
53806
53846
  return out2.slice(0, limit);
53807
53847
  }
53848
+ function opencodeSessionRoots(env2 = process.env, home = homedir4()) {
53849
+ const roots = /* @__PURE__ */ new Set();
53850
+ const explicit = env2["ERRATA_OPENCODE_SESSIONS_DIRS"];
53851
+ if (explicit) {
53852
+ for (const d of explicit.split(":")) if (d.trim()) roots.add(d.trim());
53853
+ }
53854
+ if (env2["XDG_DATA_HOME"]) roots.add(join16(env2["XDG_DATA_HOME"], "opencode", "errata"));
53855
+ roots.add(join16(home, ".local", "share", "opencode", "errata"));
53856
+ return [...roots];
53857
+ }
53858
+ function readOpencodeTranscriptDirectory(path2) {
53859
+ let fd;
53860
+ try {
53861
+ fd = openSync(path2, "r");
53862
+ const buf = Buffer.allocUnsafe(8192);
53863
+ const n = readSync(fd, buf, 0, buf.length, 0);
53864
+ const nl = buf.indexOf(10);
53865
+ const first = buf.toString("utf8", 0, nl >= 0 && nl < n ? nl : n);
53866
+ const o = JSON.parse(first);
53867
+ if (o?.type !== OPENCODE_TRANSCRIPT_HEADER_TYPE) return null;
53868
+ return typeof o.directory === "string" ? o.directory : null;
53869
+ } catch {
53870
+ return null;
53871
+ } finally {
53872
+ if (fd !== void 0) try {
53873
+ closeSync(fd);
53874
+ } catch {
53875
+ }
53876
+ }
53877
+ }
53878
+ function opencodeTranscriptsForCwd(cwd, opts = {}) {
53879
+ const sinceMs = opts.sinceMs ?? 0;
53880
+ const limit = opts.limit ?? 25;
53881
+ const want = /* @__PURE__ */ new Set([cwd, cwd.replace(/\/+$/, "")]);
53882
+ try {
53883
+ want.add(realpathSync(cwd));
53884
+ } catch {
53885
+ }
53886
+ const out2 = [];
53887
+ for (const root of opencodeSessionRoots(opts.env, opts.home)) {
53888
+ if (!existsSync13(root)) continue;
53889
+ let names;
53890
+ try {
53891
+ names = readdirSync6(root);
53892
+ } catch {
53893
+ continue;
53894
+ }
53895
+ for (const name2 of names) {
53896
+ if (!name2.endsWith(".jsonl")) continue;
53897
+ const full = join16(root, name2);
53898
+ let st;
53899
+ try {
53900
+ st = statSync4(full);
53901
+ } catch {
53902
+ continue;
53903
+ }
53904
+ if (!st.isFile() || st.mtimeMs < sinceMs) continue;
53905
+ const dir = readOpencodeTranscriptDirectory(full);
53906
+ if (!dir || !(want.has(dir) || want.has(dir.replace(/\/+$/, "")))) continue;
53907
+ out2.push({ path: full, sessionId: `opencode:${basename3(name2, ".jsonl")}`, mtimeMs: st.mtimeMs });
53908
+ }
53909
+ }
53910
+ out2.sort((a, b) => b.mtimeMs - a.mtimeMs);
53911
+ return out2.slice(0, limit);
53912
+ }
53808
53913
 
53809
53914
  // src/prior-tags.ts
53810
53915
  init_src();
@@ -57204,7 +57309,7 @@ function createWatchBreaker(deps) {
57204
57309
  }
57205
57310
 
57206
57311
  // src/engine.ts
57207
- var DAEMON_VERSION = true ? "2.0.2-dev.1218" : "2.0.0-alpha.0";
57312
+ var DAEMON_VERSION = true ? "2.0.2-dev.1255" : "2.0.0-alpha.0";
57208
57313
  var IGNORED_PATH = /[\\/](?:\.git|node_modules|\.errata|\.claude|\.codex|\.cursor|\.turbo|\.next|dist|coverage|test-results|playwright-report|__pycache__)(?:[\\/]|$)/;
57209
57314
  var IGNORED_NOISE = /castalia\.db|eventlog\.sqlite|turn-cursors/;
57210
57315
  var GIT_OP_MUTE_MS = 4e3;
@@ -58855,6 +58960,14 @@ function createWorkspaceEngine(opts) {
58855
58960
  await yieldToLoop();
58856
58961
  await harvestSession(ref.sessionId, ref.path);
58857
58962
  }
58963
+ const opencodeRefs = opencodeTranscriptsForCwd(opts.workspaceRoot, {
58964
+ sinceMs: Date.now() - TURN_REPLAY_LOOKBACK_MS,
58965
+ limit: 25
58966
+ });
58967
+ for (const ref of opencodeRefs) {
58968
+ await yieldToLoop();
58969
+ await harvestSession(ref.sessionId, ref.path);
58970
+ }
58858
58971
  } catch {
58859
58972
  }
58860
58973
  })();
@@ -58866,7 +58979,8 @@ function createWorkspaceEngine(opts) {
58866
58979
  const since = Date.now() - CODEX_SWEEP_LOOKBACK_MS;
58867
58980
  const refs = [
58868
58981
  ...codexRolloutsForCwd(opts.workspaceRoot, { sinceMs: since, limit: 10 }),
58869
- ...geminiTranscriptsForCwd(opts.workspaceRoot, { sinceMs: since, limit: 10 })
58982
+ ...geminiTranscriptsForCwd(opts.workspaceRoot, { sinceMs: since, limit: 10 }),
58983
+ ...opencodeTranscriptsForCwd(opts.workspaceRoot, { sinceMs: since, limit: 10 })
58870
58984
  ];
58871
58985
  for (const ref of refs) {
58872
58986
  await yieldToLoop();
@@ -60803,7 +60917,7 @@ var CODEX_WAKE_LOOKBACK_MS = 10 * 6e4;
60803
60917
  var CODEX_WAKE_BOOT_LOOKBACK_MS = 24 * 60 * 6e4;
60804
60918
  function codexWakeCandidates(input) {
60805
60919
  if (input.disabled) return [];
60806
- const find = input.find ?? ((cwd, since) => codexRolloutsForCwd(cwd, { sinceMs: since, limit: 1 }).length + geminiTranscriptsForCwd(cwd, { sinceMs: since, limit: 1 }).length);
60920
+ const find = input.find ?? ((cwd, since) => codexRolloutsForCwd(cwd, { sinceMs: since, limit: 1 }).length + geminiTranscriptsForCwd(cwd, { sinceMs: since, limit: 1 }).length + opencodeTranscriptsForCwd(cwd, { sinceMs: since, limit: 1 }).length);
60807
60921
  const out2 = [];
60808
60922
  for (const entry of input.entries) {
60809
60923
  if (input.liveRoots.has(input.normPath(entry.path))) continue;
@@ -61377,7 +61491,7 @@ async function startMultiDaemon(opts = {}) {
61377
61491
  });
61378
61492
  for (const c of candidates) {
61379
61493
  if (attachWorkspace(c.path).attached) {
61380
- console.log(`[errata] woke workspace ${c.name} \u2014 recent Codex/Gemini transcript (headless capture)`);
61494
+ console.log(`[errata] woke workspace ${c.name} \u2014 recent Codex/Gemini/OpenCode transcript (headless capture)`);
61381
61495
  }
61382
61496
  }
61383
61497
  };
@@ -62924,6 +63038,145 @@ ${CODEX_HOOKS_END}
62924
63038
  `;
62925
63039
  }
62926
63040
 
63041
+ // src/opencode-plugin.ts
63042
+ var OPENCODE_PLUGIN_MARKER = "// @inerrata opencode plugin \u2014 errata-managed (errata install-hooks opencode)";
63043
+ var OPENCODE_D1_CONTRACT = "Behavioral contract: (1) On ANY error, call search() BEFORE debugging \u2014 costs ~400 tokens vs 5,000\u201350,000 cold. (2) After solving non-trivial problems, contribute back: if your harness emits typed nodes (canonicalId + label + attrs), use ingest \u2014 the typed door; otherwise call contribute(), the prose fallback. (3) Before web search, try search() first \u2014 graph is 2\u201315\xD7 cheaper. (4) At session end, review what you solved and contribute anything novel.";
63044
+ var OPENCODE_TOOL_FAILURE_NUDGE = "[inerrata] This tool call failed \u2014 call search() with the error message BEFORE debugging from scratch (~400 tokens vs 5,000\u201350,000 cold).";
63045
+ function buildOpencodePluginSource(port, opts = {}) {
63046
+ const version2 = opts.version ?? "dev";
63047
+ return `${OPENCODE_PLUGIN_MARKER}
63048
+ // Generated by errata ${version2}. Re-run \`errata install-hooks opencode\` to update; edits here are overwritten.
63049
+ // What it does: mirrors each OpenCode session's OWN message record to a JSONL
63050
+ // file the errata daemon can parse, and tells the daemon on every turn boundary.
63051
+ // It has no dependencies and never throws into OpenCode.
63052
+ import { appendFileSync, existsSync, mkdirSync, readFileSync } from "node:fs";
63053
+ import { join } from "node:path";
63054
+ import { homedir } from "node:os";
63055
+
63056
+ const DAEMON = "http://127.0.0.1:${port}";
63057
+ const HEADER_TYPE = "errata-opencode-session";
63058
+ const D1_CONTRACT = ${JSON.stringify(OPENCODE_D1_CONTRACT)};
63059
+ const TOOL_FAILURE_NUDGE = ${JSON.stringify(OPENCODE_TOOL_FAILURE_NUDGE)};
63060
+
63061
+ function mirrorRoot() {
63062
+ const explicit = process.env.ERRATA_OPENCODE_SESSIONS_DIRS;
63063
+ if (explicit) { const first = explicit.split(":").map((s) => s.trim()).find(Boolean); if (first) return first; }
63064
+ if (process.env.XDG_DATA_HOME) return join(process.env.XDG_DATA_HOME, "opencode", "errata");
63065
+ return join(homedir(), ".local", "share", "opencode", "errata");
63066
+ }
63067
+
63068
+ async function post(path, body) {
63069
+ try {
63070
+ const res = await fetch(DAEMON + path, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(body), signal: AbortSignal.timeout(15000) });
63071
+ return res.status;
63072
+ } catch { return 0; }
63073
+ }
63074
+
63075
+ export const InerrataPlugin = async (input) => {
63076
+ const directory = input.directory;
63077
+ const projectID = input.project && input.project.id ? input.project.id : null;
63078
+ const root = mirrorRoot();
63079
+ // per-session state: which message ids are already in the mirror file
63080
+ const written = new Map(); // sessionID -> Set<messageId>
63081
+ const files = new Map(); // sessionID -> path
63082
+
63083
+ function fileFor(sessionID) {
63084
+ let p = files.get(sessionID);
63085
+ if (p) return p;
63086
+ try { mkdirSync(root, { recursive: true }); } catch {}
63087
+ p = join(root, sessionID + ".jsonl");
63088
+ files.set(sessionID, p);
63089
+ const seen = new Set();
63090
+ if (existsSync(p)) {
63091
+ // resumed session / plugin re-instantiated: learn what is already mirrored
63092
+ try {
63093
+ for (const line of readFileSync(p, "utf8").split("\\n")) {
63094
+ if (!line) continue;
63095
+ try { const o = JSON.parse(line); if (o && o.info && typeof o.info.id === "string") seen.add(o.info.id); } catch {}
63096
+ }
63097
+ } catch {}
63098
+ } else {
63099
+ appendFileSync(p, JSON.stringify({ type: HEADER_TYPE, version: 1, sessionID, directory, projectID, worktree: input.worktree || null, writtenAt: Date.now() }) + "\\n");
63100
+ }
63101
+ written.set(sessionID, seen);
63102
+ return p;
63103
+ }
63104
+
63105
+ async function mirror(sessionID) {
63106
+ // OpenCode's own record: session.messages() items are {info: Message, parts: Part[]}
63107
+ // (the same objects \`opencode export\` prints). Append only completed assistant
63108
+ // messages + every user message not yet mirrored; ids never repeat.
63109
+ const p = fileFor(sessionID);
63110
+ const seen = written.get(sessionID);
63111
+ let items = [];
63112
+ try {
63113
+ const r = await input.client.session.messages({ path: { id: sessionID } });
63114
+ items = (r && r.data) ? r.data : (Array.isArray(r) ? r : []);
63115
+ } catch { return { path: p, appended: 0 }; }
63116
+ let appended = 0;
63117
+ for (const item of items) {
63118
+ const info = item && item.info;
63119
+ if (!info || typeof info.id !== "string" || seen.has(info.id)) continue;
63120
+ if (info.role === "assistant" && !(info.time && info.time.completed)) continue; // still streaming
63121
+ try {
63122
+ appendFileSync(p, JSON.stringify({ info, parts: Array.isArray(item.parts) ? item.parts : [] }) + "\\n");
63123
+ seen.add(info.id); appended++;
63124
+ } catch {}
63125
+ }
63126
+ return { path: p, appended };
63127
+ }
63128
+
63129
+ const touched = new Set();
63130
+ return {
63131
+ // D1's text reaches OpenCode (\xA711.5 item 1): appended to the SYSTEM prompt
63132
+ // of every request \u2014 the same channel the Claude session-start hook uses
63133
+ // (system-level fires reliably; per-turn nudges measurably do not).
63134
+ // \`experimental.chat.system.transform\` is the hook the 1.18.18 plugin
63135
+ // API exposes for this ({sessionID?, model} \u2192 {system: string[]}).
63136
+ "experimental.chat.system.transform": async (_input, output) => {
63137
+ try {
63138
+ if (output && Array.isArray(output.system) && !output.system.includes(D1_CONTRACT)) output.system.push(D1_CONTRACT);
63139
+ } catch {}
63140
+ },
63141
+ // Tool-failure nudge (\xA711.5 item 2): when a tool result looks failed, one
63142
+ // advisory line is appended to the OUTPUT the model reads. Failure is a
63143
+ // heuristic \u2014 \`metadata\` is untyped in the plugin API \u2014 pinned to the
63144
+ // shapes observed: metadata.error truthy, or a non-zero exit/exitCode.
63145
+ "tool.execute.after": async (_input, output) => {
63146
+ try {
63147
+ if (!output || typeof output.output !== "string") return;
63148
+ const meta = output.metadata;
63149
+ const failed = !!(meta && (meta.error || (typeof meta.exit === "number" && meta.exit !== 0) || (typeof meta.exitCode === "number" && meta.exitCode !== 0)));
63150
+ if (failed && !output.output.includes(TOOL_FAILURE_NUDGE)) output.output = output.output + "\\n\\n" + TOOL_FAILURE_NUDGE;
63151
+ } catch {}
63152
+ },
63153
+ event: async ({ event }) => {
63154
+ try {
63155
+ if (!event || typeof event.type !== "string") return;
63156
+ const sid = event.properties && event.properties.sessionID;
63157
+ if (event.type === "session.created" && sid) { fileFor(sid); touched.add(sid); return; }
63158
+ if (event.type === "session.idle" && sid) {
63159
+ const { path, appended } = await mirror(sid);
63160
+ touched.add(sid);
63161
+ // turn boundary \u2192 the daemon harvests the mirror (idempotent per turn)
63162
+ await post("/api/turn", { session_id: "opencode:" + sid, transcript_path: path, cwd: directory, hostHarness: "opencode", appended });
63163
+ }
63164
+ } catch {}
63165
+ },
63166
+ dispose: async () => {
63167
+ // OpenCode has no session-end event; process disposal is the closest.
63168
+ for (const sid of touched) {
63169
+ try {
63170
+ const { path } = await mirror(sid);
63171
+ await post("/api/session-end", { session_id: "opencode:" + sid, transcript_path: path, cwd: directory, reason: "opencode-dispose" });
63172
+ } catch {}
63173
+ }
63174
+ },
63175
+ };
63176
+ };
63177
+ `;
63178
+ }
63179
+
62927
63180
  // src/cli.ts
62928
63181
  var exitCleanOnEpipe = (err2) => {
62929
63182
  if (err2.code === "EPIPE") process.exit(0);
@@ -63106,7 +63359,7 @@ Commands:
63106
63359
  Written to .errata/report.html. Flag: --future-verbs
63107
63360
  install-hooks <harness>
63108
63361
  Wire harness hooks \u2192 daemon /api/hook (or MCP).
63109
- <harness>: claude (default) | cursor | codex | aider
63362
+ <harness>: claude (default) | cursor | codex | opencode | aider
63110
63363
  Flags: --port N (default 7891)
63111
63364
  mcp Run the MCP stdio server \u2014 the agent's full errata tool
63112
63365
  surface (navigation, problems, claims, burst, health)
@@ -65006,15 +65259,61 @@ async function cmdInstallHooks(args2) {
65006
65259
  case "codex":
65007
65260
  await installCodexHooks(port);
65008
65261
  return;
65262
+ case "opencode":
65263
+ await installOpencodePlugin(port);
65264
+ return;
65009
65265
  case "aider":
65010
65266
  printAiderInstructions();
65011
65267
  return;
65012
65268
  default:
65013
65269
  console.error(`unknown harness: ${harness}`);
65014
- console.error(` supported: claude, cursor, codex, aider`);
65270
+ console.error(` supported: claude, cursor, codex, opencode, aider`);
65015
65271
  process.exit(2);
65016
65272
  }
65017
65273
  }
65274
+ async function installOpencodePlugin(port) {
65275
+ const { mkdirSync: mkdirSync9, existsSync: existsSync31, readFileSync: readFileSync29, writeFileSync: writeFileSync23 } = await import("node:fs");
65276
+ const { join: join33 } = await import("node:path");
65277
+ const dir = join33(ROOT, ".opencode", "plugin");
65278
+ if (!existsSync31(dir)) mkdirSync9(dir, { recursive: true });
65279
+ const file2 = join33(dir, "inerrata.js");
65280
+ if (existsSync31(file2)) {
65281
+ const head2 = readFileSync29(file2, "utf8").split("\n")[0] ?? "";
65282
+ if (head2.trim() !== OPENCODE_PLUGIN_MARKER) {
65283
+ console.error(`refusing to overwrite ${file2}: not an errata-managed plugin (line 1 lacks the marker)`);
65284
+ process.exit(2);
65285
+ }
65286
+ }
65287
+ writeFileSync23(file2, buildOpencodePluginSource(port, { version: DAEMON_VERSION }), "utf8");
65288
+ const cfgFile = join33(ROOT, "opencode.json");
65289
+ let cfg = { $schema: "https://opencode.ai/config.json" };
65290
+ if (existsSync31(cfgFile)) {
65291
+ try {
65292
+ cfg = JSON.parse(readFileSync29(cfgFile, "utf8"));
65293
+ } catch {
65294
+ console.error(`refusing to touch invalid JSON at ${cfgFile} \u2014 fix it and re-run`);
65295
+ process.exit(2);
65296
+ }
65297
+ }
65298
+ cfg.mcp ??= {};
65299
+ const hadErrata = "errata" in cfg.mcp;
65300
+ if (!hadErrata) {
65301
+ const inv = errataMcpInvocation();
65302
+ cfg.mcp["errata"] = { type: "local", command: [inv.command, ...inv.args] };
65303
+ writeFileSync23(cfgFile, JSON.stringify(cfg, null, 2) + "\n", "utf8");
65304
+ }
65305
+ console.log(`installed OpenCode plugin \u2192 ${file2}`);
65306
+ if (hadErrata) console.log(` mcp.errata already present in ${cfgFile} \u2014 left untouched`);
65307
+ else console.log(`installed OpenCode MCP server config \u2192 ${cfgFile} (mcp.errata: local \`errata mcp\`)`);
65308
+ console.log(` endpoint: http://127.0.0.1:${port}/api/turn on every session.idle (+ /api/session-end on dispose)`);
65309
+ console.log("");
65310
+ console.log(` OpenCode keeps sessions in SQLite, so the plugin mirrors each session's`);
65311
+ console.log(` own message record to $XDG_DATA_HOME/opencode/errata/<sessionID>.jsonl`);
65312
+ console.log(` (override: ERRATA_OPENCODE_SESSIONS_DIRS) and the daemon parses THAT \u2014`);
65313
+ console.log(` role-typed, attested. Works for headless \`opencode run\` too.`);
65314
+ console.log(` Cloud tools (search/contribute) are the MCP entry in opencode.json:`);
65315
+ console.log(` { "mcp": { "inerrata": { "type": "remote", "url": "https://mcp.inerrata.dev/mcp", "headers": { "Authorization": "Bearer <errk_\u2026>" } } } }`);
65316
+ }
65018
65317
  var ERRATA_TAG = "# errata-managed";
65019
65318
  function errataMcpInvocation() {
65020
65319
  const { cmd: cmd2, args: args2 } = selfArgv("mcp");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inerrata-corporation/errata",
3
- "version": "2.0.2-dev.1218",
3
+ "version": "2.0.2-dev.1255",
4
4
  "description": "errata - local-first observation engine for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {