@inerrata-corporation/errata 2.0.8-dev.2839 → 2.0.8-dev.2870
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/errata.mjs +391 -118
- package/package.json +1 -1
package/errata.mjs
CHANGED
|
@@ -29066,6 +29066,92 @@ var init_agent_signals = __esm({
|
|
|
29066
29066
|
}
|
|
29067
29067
|
});
|
|
29068
29068
|
|
|
29069
|
+
// src/recall-card.ts
|
|
29070
|
+
function labelName(label) {
|
|
29071
|
+
return LABEL_NAMES[label] ?? label.toUpperCase();
|
|
29072
|
+
}
|
|
29073
|
+
function clip(text, width) {
|
|
29074
|
+
const flat = text.replace(/\s+/g, " ").trim();
|
|
29075
|
+
return flat.length > width ? `${flat.slice(0, width - 1)}\u2026` : flat;
|
|
29076
|
+
}
|
|
29077
|
+
function plural(n, word, many = `${word}s`) {
|
|
29078
|
+
return `${n} ${n === 1 ? word : many}`;
|
|
29079
|
+
}
|
|
29080
|
+
function errorCardHeadline(items) {
|
|
29081
|
+
const fixes = items.filter((it) => it.label === "Solution").length;
|
|
29082
|
+
return fixes > 0 ? `${plural(fixes, "known fix", "known fixes")} for this error` : `${plural(items.length, "prior")} for this error`;
|
|
29083
|
+
}
|
|
29084
|
+
function leadItem(items) {
|
|
29085
|
+
return items.find((it) => it.label === "Solution") ?? items[0];
|
|
29086
|
+
}
|
|
29087
|
+
function renderRecallLine(card) {
|
|
29088
|
+
const lead2 = leadItem(card.items);
|
|
29089
|
+
if (!lead2) return null;
|
|
29090
|
+
const rest2 = card.items.length - 1;
|
|
29091
|
+
return `\u26A1 errata \xB7 ${card.headline} \u2014 ${labelName(lead2.label)}: ${clip(lead2.text, INLINE_TEXT_WIDTH)}` + (lead2.note ? ` ${lead2.note}` : "") + (rest2 > 0 ? ` \xB7 +${rest2} in context` : "");
|
|
29092
|
+
}
|
|
29093
|
+
function labelColor(label) {
|
|
29094
|
+
if (label === "Solution") return GREEN;
|
|
29095
|
+
if (label === "Problem" || label === "AntiPattern") return MADDER;
|
|
29096
|
+
return AMBER;
|
|
29097
|
+
}
|
|
29098
|
+
function renderStatusCard(card) {
|
|
29099
|
+
const ordered = [...card.items].sort(
|
|
29100
|
+
(a, b) => Number(b.label === "Solution") - Number(a.label === "Solution")
|
|
29101
|
+
);
|
|
29102
|
+
const rows = ordered.slice(0, STATUS_ROWS).map((it) => {
|
|
29103
|
+
const label = labelName(it.label).padEnd(LABEL_WIDTH);
|
|
29104
|
+
const note = it.note ? ` ${DIM}${it.note}${RESET}` : "";
|
|
29105
|
+
return `${DIM}\u2502${RESET} ${BOLD}${labelColor(it.label)}${label}${RESET} ${INK}${clip(it.text, STATUS_TEXT_WIDTH)}${RESET}${note}`;
|
|
29106
|
+
});
|
|
29107
|
+
const hidden = card.items.length - STATUS_ROWS;
|
|
29108
|
+
const more = hidden > 0 ? ` ${DIM}\xB7 +${hidden} more in the agent's context${RESET}` : "";
|
|
29109
|
+
return [`${AMBER}\u26A1${RESET} ${BOLD}errata${RESET} ${DIM}\xB7${RESET} ${card.headline}${more}`, ...rows].join("\n");
|
|
29110
|
+
}
|
|
29111
|
+
function rememberCard(sessionId, card, now = Date.now()) {
|
|
29112
|
+
if (!sessionId) return;
|
|
29113
|
+
latestBySession.delete(sessionId);
|
|
29114
|
+
latestBySession.set(sessionId, { card, at: now });
|
|
29115
|
+
if (latestBySession.size > MAX_SESSIONS) {
|
|
29116
|
+
latestBySession.delete(latestBySession.keys().next().value);
|
|
29117
|
+
}
|
|
29118
|
+
}
|
|
29119
|
+
function statuslineText(sessionId, now = Date.now()) {
|
|
29120
|
+
const hit = latestBySession.get(sessionId);
|
|
29121
|
+
if (!hit || now - hit.at > STATUS_CARD_TTL_MS) return IDLE_STATUS;
|
|
29122
|
+
return renderStatusCard(hit.card);
|
|
29123
|
+
}
|
|
29124
|
+
function recallCardEnabled(env2 = process.env) {
|
|
29125
|
+
const v = (env2["ERRATA_RECALL_CARD"] ?? "").trim().toLowerCase();
|
|
29126
|
+
return !(v === "0" || v === "off" || v === "false" || v === "no");
|
|
29127
|
+
}
|
|
29128
|
+
var LABEL_NAMES, INLINE_TEXT_WIDTH, ESC, RESET, DIM, BOLD, AMBER, MADDER, GREEN, INK, STATUS_ROWS, STATUS_TEXT_WIDTH, LABEL_WIDTH, STATUS_CARD_TTL_MS, IDLE_STATUS, latestBySession, MAX_SESSIONS;
|
|
29129
|
+
var init_recall_card = __esm({
|
|
29130
|
+
"src/recall-card.ts"() {
|
|
29131
|
+
"use strict";
|
|
29132
|
+
LABEL_NAMES = {
|
|
29133
|
+
RootCause: "ROOT CAUSE",
|
|
29134
|
+
AntiPattern: "ANTIPATTERN"
|
|
29135
|
+
};
|
|
29136
|
+
INLINE_TEXT_WIDTH = 64;
|
|
29137
|
+
ESC = "\x1B[";
|
|
29138
|
+
RESET = `${ESC}0m`;
|
|
29139
|
+
DIM = `${ESC}2m`;
|
|
29140
|
+
BOLD = `${ESC}1m`;
|
|
29141
|
+
AMBER = `${ESC}38;2;217;164;65m`;
|
|
29142
|
+
MADDER = `${ESC}38;2;210;58;77m`;
|
|
29143
|
+
GREEN = `${ESC}38;2;111;191;138m`;
|
|
29144
|
+
INK = `${ESC}38;2;236;226;221m`;
|
|
29145
|
+
STATUS_ROWS = 2;
|
|
29146
|
+
STATUS_TEXT_WIDTH = 96;
|
|
29147
|
+
LABEL_WIDTH = 10;
|
|
29148
|
+
STATUS_CARD_TTL_MS = 9e4;
|
|
29149
|
+
IDLE_STATUS = `${AMBER}\u26A1${RESET} ${DIM}errata \xB7 watching${RESET}`;
|
|
29150
|
+
latestBySession = /* @__PURE__ */ new Map();
|
|
29151
|
+
MAX_SESSIONS = 256;
|
|
29152
|
+
}
|
|
29153
|
+
});
|
|
29154
|
+
|
|
29069
29155
|
// src/review.ts
|
|
29070
29156
|
import { existsSync as existsSync7, readFileSync as readFileSync5, writeFileSync as writeFileSync6 } from "node:fs";
|
|
29071
29157
|
function loadReviewQueue(paths) {
|
|
@@ -51311,8 +51397,11 @@ var init_message_accumulator = __esm({
|
|
|
51311
51397
|
// src/webui.ts
|
|
51312
51398
|
var webui_exports = {};
|
|
51313
51399
|
__export(webui_exports, {
|
|
51400
|
+
asFailedPostToolUse: () => asFailedPostToolUse,
|
|
51314
51401
|
buildWebUi: () => buildWebUi,
|
|
51315
51402
|
decideContextInject: () => decideContextInject,
|
|
51403
|
+
errorRecallTerms: () => errorRecallTerms,
|
|
51404
|
+
fileRecall: () => fileRecall,
|
|
51316
51405
|
fileUriToFsPath: () => fileUriToFsPath,
|
|
51317
51406
|
findFileByPath: () => findFileByPath,
|
|
51318
51407
|
recallForFile: () => recallForFile,
|
|
@@ -51327,9 +51416,7 @@ function fileUriToFsPath(raw2) {
|
|
|
51327
51416
|
return p;
|
|
51328
51417
|
}
|
|
51329
51418
|
function recallForError(store, errorText) {
|
|
51330
|
-
const terms =
|
|
51331
|
-
new Set(errorText.split(/[^A-Za-z0-9_.]+/).filter((t) => t.length >= 5))
|
|
51332
|
-
).slice(0, 3);
|
|
51419
|
+
const terms = errorRecallTerms(errorText);
|
|
51333
51420
|
if (terms.length === 0) return null;
|
|
51334
51421
|
const labels = ["Problem", "Solution", "RootCause"];
|
|
51335
51422
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -51344,15 +51431,46 @@ function recallForError(store, errorText) {
|
|
|
51344
51431
|
}
|
|
51345
51432
|
if (hits.length === 0) return null;
|
|
51346
51433
|
const lines = hits.slice(0, 3).map((n) => `- [${n.label}] ${(n.description ?? "").replace(/\s+/g, " ").slice(0, 140)}`);
|
|
51347
|
-
|
|
51434
|
+
const items = hits.map((n) => ({ label: n.label, text: n.description ?? "" }));
|
|
51435
|
+
return {
|
|
51436
|
+
context: `errata recall \u2014 ${hits.length} prior match(es) in the local graph for this error:
|
|
51348
51437
|
${lines.join("\n")}
|
|
51349
|
-
Reuse the prior fix if it applies before re-deriving
|
|
51438
|
+
Reuse the prior fix if it applies before re-deriving.`,
|
|
51439
|
+
card: { headline: errorCardHeadline(items), items }
|
|
51440
|
+
};
|
|
51441
|
+
}
|
|
51442
|
+
function asFailedPostToolUse(body2) {
|
|
51443
|
+
const error48 = typeof body2["error"] === "string" ? body2["error"] : "";
|
|
51444
|
+
const exit = /^Exit code (\d+)/.exec(error48);
|
|
51445
|
+
return {
|
|
51446
|
+
...body2,
|
|
51447
|
+
hook_event_name: "PostToolUse",
|
|
51448
|
+
is_error: true,
|
|
51449
|
+
tool_response: { stdout: "", stderr: error48, exitCode: exit ? Number(exit[1]) : 1 }
|
|
51450
|
+
};
|
|
51451
|
+
}
|
|
51452
|
+
function errorRecallTerms(errorText) {
|
|
51453
|
+
const seen = /* @__PURE__ */ new Set();
|
|
51454
|
+
const scored = [];
|
|
51455
|
+
errorText.split(/[^A-Za-z0-9_.-]+/).forEach((raw2, at) => {
|
|
51456
|
+
const term = raw2.replace(/^[.-]+|[.-]+$/g, "");
|
|
51457
|
+
const key = term.toLowerCase();
|
|
51458
|
+
if (term.length < 5 || seen.has(key) || GENERIC_ERROR_WORDS.has(key)) return;
|
|
51459
|
+
seen.add(key);
|
|
51460
|
+
const identifier = /[_.\d-]/.test(term) || /[a-z][A-Z]/.test(term) || /^[A-Z]{5,}$/.test(term);
|
|
51461
|
+
scored.push({ term, score: identifier ? 1 : 0, at });
|
|
51462
|
+
});
|
|
51463
|
+
const hasIdentifier = scored.some((s) => s.score > 0);
|
|
51464
|
+
return scored.filter((s) => !hasIdentifier || s.score > 0).sort((a, b) => b.score - a.score || a.at - b.at).slice(0, 3).map((s) => s.term);
|
|
51350
51465
|
}
|
|
51351
51466
|
function recallLine(n, token) {
|
|
51352
51467
|
const desc = (n.description ?? "").replace(/\s+/g, " ").slice(0, 120);
|
|
51353
51468
|
return `- [${n.label}] ${desc} \u2192 ${token}`;
|
|
51354
51469
|
}
|
|
51355
51470
|
function recallForFile(store, relPath) {
|
|
51471
|
+
return fileRecall(store, relPath)?.context ?? null;
|
|
51472
|
+
}
|
|
51473
|
+
function fileRecall(store, relPath) {
|
|
51356
51474
|
const priors = priorsForFile(store, relPath);
|
|
51357
51475
|
if (!priors) return null;
|
|
51358
51476
|
const lines = [];
|
|
@@ -51370,9 +51488,16 @@ function recallForFile(store, relPath) {
|
|
|
51370
51488
|
lines.push(recallLine(n, `cite with ${TAG_EXAMPLE.prior(n.id)}`));
|
|
51371
51489
|
}
|
|
51372
51490
|
const total = priors.openProblems.length + priors.constraints.length + priors.related.length;
|
|
51373
|
-
|
|
51491
|
+
const items = [...priors.openProblems, ...priors.constraints, ...priors.related].map((n) => ({
|
|
51492
|
+
label: n.label,
|
|
51493
|
+
text: n.description ?? ""
|
|
51494
|
+
}));
|
|
51495
|
+
return {
|
|
51496
|
+
context: `errata \u2014 ${total} prior(s) recorded on this file (act only if relevant):
|
|
51374
51497
|
${lines.join("\n")}
|
|
51375
|
-
` + buildFileRecallInstruction()
|
|
51498
|
+
` + buildFileRecallInstruction(),
|
|
51499
|
+
card: { headline: `${plural(total, "prior")} on ${relPath.split("/").slice(-2).join("/")}`, items }
|
|
51500
|
+
};
|
|
51376
51501
|
}
|
|
51377
51502
|
function workspacePackageIds(store) {
|
|
51378
51503
|
const hit = wsPackagesCache.get(store);
|
|
@@ -51383,7 +51508,11 @@ function workspacePackageIds(store) {
|
|
|
51383
51508
|
return ids;
|
|
51384
51509
|
}
|
|
51385
51510
|
function recallForTool(store, command, ctx) {
|
|
51511
|
+
return toolRecall(store, command, ctx)?.context ?? null;
|
|
51512
|
+
}
|
|
51513
|
+
function toolRecall(store, command, ctx) {
|
|
51386
51514
|
const lines = [];
|
|
51515
|
+
const items = [];
|
|
51387
51516
|
const seen = /* @__PURE__ */ new Set();
|
|
51388
51517
|
for (const name2 of extractExecutables(command)) {
|
|
51389
51518
|
if (seen.has(name2)) continue;
|
|
@@ -51397,11 +51526,19 @@ function recallForTool(store, command, ctx) {
|
|
|
51397
51526
|
fit.node.label === "Problem" ? `${TAG_EXAMPLE.fix(fit.node.id)} if you resolved it \xB7 ${TAG_EXAMPLE.prior(fit.node.id)} to cite` : `cite with ${TAG_EXAMPLE.prior(fit.node.id)}`
|
|
51398
51527
|
).slice(2)}`
|
|
51399
51528
|
);
|
|
51529
|
+
items.push({
|
|
51530
|
+
label: fit.node.label,
|
|
51531
|
+
text: `${name2}: ${fit.node.description ?? ""}`,
|
|
51532
|
+
note: fit.osMismatch ? `[seen on ${fit.observedOn.join("/")}]` : void 0
|
|
51533
|
+
});
|
|
51400
51534
|
}
|
|
51401
51535
|
}
|
|
51402
51536
|
if (lines.length === 0) return null;
|
|
51403
|
-
return
|
|
51404
|
-
${lines.
|
|
51537
|
+
return {
|
|
51538
|
+
context: `errata \u2014 ${lines.length} prior(s) about the tool(s) you're about to run (a \`[platform]\` tag means it was seen on a DIFFERENT OS than this one \u2014 judge it):
|
|
51539
|
+
${lines.join("\n")}`,
|
|
51540
|
+
card: { headline: `${plural(items.length, "prior")} before this command runs`, items }
|
|
51541
|
+
};
|
|
51405
51542
|
}
|
|
51406
51543
|
function decideContextInject(args2) {
|
|
51407
51544
|
const { event, sessionId, block, lastInjectedHash } = args2;
|
|
@@ -51559,8 +51696,10 @@ function buildWebUi(deps) {
|
|
|
51559
51696
|
if (deps.onHook) deps.onHook();
|
|
51560
51697
|
const tool = String(body2["tool_name"] ?? "");
|
|
51561
51698
|
const input = body2["tool_input"] ?? {};
|
|
51562
|
-
const
|
|
51563
|
-
|
|
51699
|
+
const hookEvent = String(body2["hook_event_name"] ?? "PreToolUse");
|
|
51700
|
+
if (hookEvent === "PostToolUseFailure") body2 = asFailedPostToolUse(body2);
|
|
51701
|
+
const event = hookEvent === "PostToolUseFailure" ? "PostToolUse" : hookEvent;
|
|
51702
|
+
let recall = null;
|
|
51564
51703
|
if (event === "PreToolUse") {
|
|
51565
51704
|
const virtual = extractVirtualReads(input);
|
|
51566
51705
|
if (virtual.length > 0) {
|
|
@@ -51575,7 +51714,7 @@ function buildWebUi(deps) {
|
|
|
51575
51714
|
}
|
|
51576
51715
|
if (tool === "Bash" && typeof input["command"] === "string") {
|
|
51577
51716
|
try {
|
|
51578
|
-
|
|
51717
|
+
recall = toolRecall(deps.store, input["command"], {
|
|
51579
51718
|
os: currentOsName(),
|
|
51580
51719
|
workspacePackageIds: workspacePackageIds(deps.store)
|
|
51581
51720
|
});
|
|
@@ -51629,7 +51768,7 @@ function buildWebUi(deps) {
|
|
|
51629
51768
|
deps.onToolsObserved?.(toolSid, observed.nodeIds);
|
|
51630
51769
|
} catch {
|
|
51631
51770
|
}
|
|
51632
|
-
if (failed)
|
|
51771
|
+
if (failed) recall = recallForError(deps.store, stderr || combined);
|
|
51633
51772
|
}
|
|
51634
51773
|
if (event === "PostToolUse" && tool && tool !== "Bash" && deps.onToolError) {
|
|
51635
51774
|
const rawResp = body2["tool_response"];
|
|
@@ -51652,11 +51791,11 @@ function buildWebUi(deps) {
|
|
|
51652
51791
|
exitCode: numericExit ?? 1,
|
|
51653
51792
|
ts: Date.now()
|
|
51654
51793
|
});
|
|
51655
|
-
|
|
51794
|
+
recall = recallForError(deps.store, errText2);
|
|
51656
51795
|
}
|
|
51657
51796
|
}
|
|
51658
51797
|
}
|
|
51659
|
-
if (event === "PostToolUse" && !
|
|
51798
|
+
if (event === "PostToolUse" && !recall && rel && (tool === "Read" || tool === "Edit" || tool === "Write" || tool === "NotebookEdit")) {
|
|
51660
51799
|
const sid = String(body2["session_id"] ?? "");
|
|
51661
51800
|
let seen = fileRecallSeen.get(sid);
|
|
51662
51801
|
if (!seen) {
|
|
@@ -51664,28 +51803,29 @@ function buildWebUi(deps) {
|
|
|
51664
51803
|
fileRecallSeen.set(sid, seen);
|
|
51665
51804
|
}
|
|
51666
51805
|
if (!seen.has(rel)) {
|
|
51667
|
-
const
|
|
51668
|
-
if (
|
|
51806
|
+
const fileHit = fileRecall(deps.store, rel);
|
|
51807
|
+
if (fileHit) {
|
|
51669
51808
|
seen.add(rel);
|
|
51670
|
-
|
|
51809
|
+
recall = fileHit;
|
|
51671
51810
|
}
|
|
51672
51811
|
}
|
|
51673
51812
|
}
|
|
51674
|
-
if (event === "PostToolUse") {
|
|
51675
|
-
|
|
51676
|
-
|
|
51677
|
-
|
|
51678
|
-
});
|
|
51679
|
-
}
|
|
51680
|
-
return c.body(null, 204);
|
|
51681
|
-
}
|
|
51682
|
-
if (event === "PreToolUse" && recallCtx) {
|
|
51813
|
+
if ((event === "PostToolUse" || event === "PreToolUse") && recall) {
|
|
51814
|
+
const showCard = recallCardEnabled();
|
|
51815
|
+
if (showCard) rememberCard(String(body2["session_id"] ?? ""), recall.card);
|
|
51816
|
+
const line = showCard ? renderRecallLine(recall.card) : null;
|
|
51683
51817
|
return c.json({
|
|
51684
|
-
|
|
51818
|
+
...line ? { systemMessage: line } : {},
|
|
51819
|
+
hookSpecificOutput: { hookEventName: hookEvent, additionalContext: recall.context }
|
|
51685
51820
|
});
|
|
51686
51821
|
}
|
|
51822
|
+
if (event === "PostToolUse") return c.body(null, 204);
|
|
51687
51823
|
return c.json({ ok: true, event, tool, recognizedFile: filePath ?? null });
|
|
51688
51824
|
});
|
|
51825
|
+
app.post("/api/statusline", async (c) => {
|
|
51826
|
+
const body2 = await c.req.json().catch(() => ({}));
|
|
51827
|
+
return c.text(statuslineText(String(body2["session_id"] ?? "")));
|
|
51828
|
+
});
|
|
51689
51829
|
app.post("/api/turn", async (c) => {
|
|
51690
51830
|
let body2;
|
|
51691
51831
|
try {
|
|
@@ -51929,7 +52069,7 @@ function proposalToIngest(p, profile, daemonVersion) {
|
|
|
51929
52069
|
payloadDigest: p.id
|
|
51930
52070
|
};
|
|
51931
52071
|
}
|
|
51932
|
-
var messageAccumulator, wsPackagesCache, WS_PACKAGES_TTL_MS, loggedToolSessions, META_LABELS;
|
|
52072
|
+
var messageAccumulator, GENERIC_ERROR_WORDS, wsPackagesCache, WS_PACKAGES_TTL_MS, loggedToolSessions, META_LABELS;
|
|
51933
52073
|
var init_webui = __esm({
|
|
51934
52074
|
"src/webui.ts"() {
|
|
51935
52075
|
"use strict";
|
|
@@ -51937,12 +52077,54 @@ var init_webui = __esm({
|
|
|
51937
52077
|
init_src6();
|
|
51938
52078
|
init_src5();
|
|
51939
52079
|
init_agent_signals();
|
|
52080
|
+
init_recall_card();
|
|
51940
52081
|
init_review2();
|
|
51941
52082
|
init_vfile();
|
|
51942
52083
|
init_tool_index();
|
|
51943
52084
|
init_outbox();
|
|
51944
52085
|
init_message_accumulator();
|
|
51945
52086
|
messageAccumulator = createMessageAccumulator();
|
|
52087
|
+
GENERIC_ERROR_WORDS = /* @__PURE__ */ new Set([
|
|
52088
|
+
"cannot",
|
|
52089
|
+
"error",
|
|
52090
|
+
"errors",
|
|
52091
|
+
"failed",
|
|
52092
|
+
"failure",
|
|
52093
|
+
"found",
|
|
52094
|
+
"install",
|
|
52095
|
+
"installed",
|
|
52096
|
+
"module",
|
|
52097
|
+
"command",
|
|
52098
|
+
"because",
|
|
52099
|
+
"while",
|
|
52100
|
+
"could",
|
|
52101
|
+
"should",
|
|
52102
|
+
"which",
|
|
52103
|
+
"there",
|
|
52104
|
+
"their",
|
|
52105
|
+
"about",
|
|
52106
|
+
"after",
|
|
52107
|
+
"before",
|
|
52108
|
+
"update",
|
|
52109
|
+
"warning",
|
|
52110
|
+
"expected",
|
|
52111
|
+
"received",
|
|
52112
|
+
"value",
|
|
52113
|
+
"undefined",
|
|
52114
|
+
"unable",
|
|
52115
|
+
"invalid",
|
|
52116
|
+
"missing",
|
|
52117
|
+
"exited",
|
|
52118
|
+
"unknown",
|
|
52119
|
+
"files",
|
|
52120
|
+
"directory",
|
|
52121
|
+
"process",
|
|
52122
|
+
"access",
|
|
52123
|
+
"denied",
|
|
52124
|
+
"permission",
|
|
52125
|
+
"running",
|
|
52126
|
+
"argument"
|
|
52127
|
+
]);
|
|
51946
52128
|
wsPackagesCache = /* @__PURE__ */ new WeakMap();
|
|
51947
52129
|
WS_PACKAGES_TTL_MS = 6e4;
|
|
51948
52130
|
loggedToolSessions = /* @__PURE__ */ new Set();
|
|
@@ -55730,8 +55912,8 @@ var require_webidl = __commonJS({
|
|
|
55730
55912
|
return new TypeError(`${message2.header}: ${message2.message}`);
|
|
55731
55913
|
};
|
|
55732
55914
|
webidl.errors.conversionFailed = function(context) {
|
|
55733
|
-
const
|
|
55734
|
-
const message2 = `${context.argument} could not be converted to${
|
|
55915
|
+
const plural2 = context.types.length === 1 ? "" : " one of";
|
|
55916
|
+
const message2 = `${context.argument} could not be converted to${plural2}: ${context.types.join(", ")}.`;
|
|
55735
55917
|
return webidl.errors.exception({
|
|
55736
55918
|
header: context.prefix,
|
|
55737
55919
|
message: message2
|
|
@@ -63095,9 +63277,9 @@ var require_pluralizer = __commonJS({
|
|
|
63095
63277
|
this: "these"
|
|
63096
63278
|
};
|
|
63097
63279
|
module2.exports = class Pluralizer {
|
|
63098
|
-
constructor(singular,
|
|
63280
|
+
constructor(singular, plural2) {
|
|
63099
63281
|
this.singular = singular;
|
|
63100
|
-
this.plural =
|
|
63282
|
+
this.plural = plural2;
|
|
63101
63283
|
}
|
|
63102
63284
|
pluralize(count) {
|
|
63103
63285
|
const one = count === 1;
|
|
@@ -71202,7 +71384,7 @@ var init_report_render = __esm({
|
|
|
71202
71384
|
// src/cli.ts
|
|
71203
71385
|
init_src6();
|
|
71204
71386
|
import { closeSync as closeSync2, existsSync as existsSync34, openSync as openSync2, readFileSync as readFileSync35, renameSync as renameSync4, statSync as statSync9 } from "node:fs";
|
|
71205
|
-
import { join as
|
|
71387
|
+
import { join as join42, resolve as pathResolve2 } from "node:path";
|
|
71206
71388
|
import { spawn as spawn5 } from "node:child_process";
|
|
71207
71389
|
|
|
71208
71390
|
// src/daemon.ts
|
|
@@ -79251,7 +79433,7 @@ function createWorkingFileState() {
|
|
|
79251
79433
|
}
|
|
79252
79434
|
|
|
79253
79435
|
// src/tool-run-state.ts
|
|
79254
|
-
var
|
|
79436
|
+
var MAX_SESSIONS2 = 64;
|
|
79255
79437
|
var MAX_TOOLS_PER_SESSION = 64;
|
|
79256
79438
|
function createToolRunState() {
|
|
79257
79439
|
const bySession = /* @__PURE__ */ new Map();
|
|
@@ -79260,7 +79442,7 @@ function createToolRunState() {
|
|
|
79260
79442
|
if (!sessionId || toolNodeIds.length === 0) return;
|
|
79261
79443
|
let set2 = bySession.get(sessionId);
|
|
79262
79444
|
if (!set2) {
|
|
79263
|
-
if (bySession.size >=
|
|
79445
|
+
if (bySession.size >= MAX_SESSIONS2) {
|
|
79264
79446
|
const oldest = bySession.keys().next().value;
|
|
79265
79447
|
if (oldest !== void 0) bySession.delete(oldest);
|
|
79266
79448
|
}
|
|
@@ -79931,7 +80113,7 @@ function createWatchBreaker(deps) {
|
|
|
79931
80113
|
}
|
|
79932
80114
|
|
|
79933
80115
|
// src/engine.ts
|
|
79934
|
-
var DAEMON_VERSION = true ? "2.0.8-dev.
|
|
80116
|
+
var DAEMON_VERSION = true ? "2.0.8-dev.2870" : "2.0.0-alpha.0";
|
|
79935
80117
|
var IGNORED_PATH = /[\\/](?:\.git|node_modules|\.errata|\.claude|\.codex|\.cursor|\.turbo|\.next|dist|coverage|test-results|playwright-report|__pycache__)(?:[\\/]|$)/;
|
|
79936
80118
|
var IGNORED_NOISE = /castalia\.db|eventlog\.sqlite|turn-cursors/;
|
|
79937
80119
|
var GIT_OP_MUTE_MS = 4e3;
|
|
@@ -80364,7 +80546,7 @@ function createWorkspaceEngine(opts) {
|
|
|
80364
80546
|
}
|
|
80365
80547
|
}
|
|
80366
80548
|
};
|
|
80367
|
-
const
|
|
80549
|
+
const clip2 = (s, n) => s.length > n ? `${s.slice(0, n - 1)}\u2026` : s;
|
|
80368
80550
|
const pickReviewItems = () => {
|
|
80369
80551
|
const shared = opts.sharedStore;
|
|
80370
80552
|
if (!shared) return [];
|
|
@@ -80375,12 +80557,12 @@ function createWorkspaceEngine(opts) {
|
|
|
80375
80557
|
return [{
|
|
80376
80558
|
kind: "abstraction",
|
|
80377
80559
|
candidate: small.candidate,
|
|
80378
|
-
members: small.members.map((m) => ({ id: m.id, description:
|
|
80560
|
+
members: small.members.map((m) => ({ id: m.id, description: clip2(m.description, 110) }))
|
|
80379
80561
|
}];
|
|
80380
80562
|
}
|
|
80381
80563
|
}
|
|
80382
80564
|
const [d] = pendingDiscriminators(shared, { limit: 1 });
|
|
80383
|
-
return d ? [{ kind: "discriminator", route: d.route, presenting:
|
|
80565
|
+
return d ? [{ kind: "discriminator", route: d.route, presenting: clip2(d.presenting, 140), cause: clip2(d.cause, 140) }] : [];
|
|
80384
80566
|
} catch {
|
|
80385
80567
|
return [];
|
|
80386
80568
|
}
|
|
@@ -83358,6 +83540,7 @@ function buildDrainInstances(store, sharedStore, input) {
|
|
|
83358
83540
|
|
|
83359
83541
|
// src/multi.ts
|
|
83360
83542
|
init_webui();
|
|
83543
|
+
init_recall_card();
|
|
83361
83544
|
|
|
83362
83545
|
// src/consolidate-worker-client.ts
|
|
83363
83546
|
import { Worker as Worker2 } from "node:worker_threads";
|
|
@@ -83728,6 +83911,22 @@ function isClaimedElsewhere(err2) {
|
|
|
83728
83911
|
const e = err2;
|
|
83729
83912
|
return e.status === 409 && e.code === "claimed_elsewhere";
|
|
83730
83913
|
}
|
|
83914
|
+
function claimHoldStands(onDisk) {
|
|
83915
|
+
return Boolean(onDisk && !onDisk.projectId && onDisk.projectLinkHold);
|
|
83916
|
+
}
|
|
83917
|
+
var LINK_FIELDS = [
|
|
83918
|
+
"projectId",
|
|
83919
|
+
"projectLocator",
|
|
83920
|
+
"projectLinkHold",
|
|
83921
|
+
"projectLinkDisabled",
|
|
83922
|
+
"rootCommitReportedFor"
|
|
83923
|
+
];
|
|
83924
|
+
function adoptLinkFields(into, from) {
|
|
83925
|
+
for (const k of LINK_FIELDS) {
|
|
83926
|
+
if (from[k] === void 0) delete into[k];
|
|
83927
|
+
else into[k] = from[k];
|
|
83928
|
+
}
|
|
83929
|
+
}
|
|
83731
83930
|
async function readRootCommit(root) {
|
|
83732
83931
|
try {
|
|
83733
83932
|
const { stdout } = await execFileAsync("git", ["rev-list", "--max-parents=0", "HEAD"], {
|
|
@@ -85397,6 +85596,7 @@ function applyLinkDecision(root, profile, config2, decision, now = Date.now) {
|
|
|
85397
85596
|
projectLocator: decision.locator
|
|
85398
85597
|
};
|
|
85399
85598
|
delete nextProfile.projectLinkDisabled;
|
|
85599
|
+
delete nextProfile.projectLinkHold;
|
|
85400
85600
|
let wroteIgnore = false;
|
|
85401
85601
|
if (decision.writeIgnoreStarter !== false) {
|
|
85402
85602
|
const target = join37(root, ".errataignore");
|
|
@@ -85793,9 +85993,9 @@ async function startMultiDaemon(opts = {}) {
|
|
|
85793
85993
|
if (identityBlocks.delete(wsId)) console.log(`[identity] workspace unblocked \u2014 spooled capture drains on this pass`);
|
|
85794
85994
|
};
|
|
85795
85995
|
const projectHolds = /* @__PURE__ */ new Map();
|
|
85796
|
-
const noteProjectHold = (wsId, wsName, reason) => {
|
|
85996
|
+
const noteProjectHold = (wsId, wsName, reason, claimRoot) => {
|
|
85797
85997
|
if (!projectHolds.has(wsId)) {
|
|
85798
|
-
projectHolds.set(wsId, { workspace: wsName, reason, since: Date.now() });
|
|
85998
|
+
projectHolds.set(wsId, { workspace: wsName, reason, since: Date.now(), ...claimRoot ? { claimRoot } : {} });
|
|
85799
85999
|
console.error(
|
|
85800
86000
|
`[project] HOLDING cloud push for workspace "${wsName}": cannot prove its project \u2014 ${reason}. Shipping without the assertion would publish project-scoped knowledge to the whole org; the batch stays pending instead.`
|
|
85801
86001
|
);
|
|
@@ -85804,6 +86004,14 @@ async function startMultiDaemon(opts = {}) {
|
|
|
85804
86004
|
const clearProjectHold = (wsId) => {
|
|
85805
86005
|
if (projectHolds.delete(wsId)) console.log(`[project] project assertion restored \u2014 held contributions drain on this pass`);
|
|
85806
86006
|
};
|
|
86007
|
+
const releaseSettledClaimHolds = () => {
|
|
86008
|
+
for (const [wsId, hold] of projectHolds) {
|
|
86009
|
+
if (hold.claimRoot && !claimHoldStands(loadProfile(hold.claimRoot))) {
|
|
86010
|
+
projectHolds.delete(wsId);
|
|
86011
|
+
console.log(`[project] claim hold on "${hold.workspace}" settled by \`errata link\` \u2014 ships on the next pass`);
|
|
86012
|
+
}
|
|
86013
|
+
}
|
|
86014
|
+
};
|
|
85807
86015
|
const cloudForIdentity = (identity) => {
|
|
85808
86016
|
if (identity.invalid) return blockedCloudClient(identity.reason);
|
|
85809
86017
|
if (identity.rule === "workspace-binding" && identity.profileName) {
|
|
@@ -85898,35 +86106,38 @@ async function startMultiDaemon(opts = {}) {
|
|
|
85898
86106
|
if (detail) boundaryFlushDetail = detail;
|
|
85899
86107
|
if (units > 0 || items > 0) boundaryFlushLastAdvanceAt = Date.now();
|
|
85900
86108
|
};
|
|
85901
|
-
const flushSnapshot = () =>
|
|
85902
|
-
|
|
85903
|
-
|
|
85904
|
-
|
|
85905
|
-
|
|
85906
|
-
|
|
85907
|
-
|
|
85908
|
-
|
|
85909
|
-
|
|
85910
|
-
|
|
85911
|
-
|
|
85912
|
-
|
|
85913
|
-
|
|
85914
|
-
|
|
85915
|
-
|
|
85916
|
-
|
|
85917
|
-
|
|
85918
|
-
|
|
85919
|
-
|
|
85920
|
-
|
|
85921
|
-
|
|
85922
|
-
|
|
85923
|
-
|
|
85924
|
-
|
|
85925
|
-
|
|
85926
|
-
|
|
85927
|
-
|
|
85928
|
-
|
|
85929
|
-
|
|
86109
|
+
const flushSnapshot = () => {
|
|
86110
|
+
releaseSettledClaimHolds();
|
|
86111
|
+
return {
|
|
86112
|
+
running: boundaryFlushing,
|
|
86113
|
+
stage: boundaryFlushStage,
|
|
86114
|
+
detail: boundaryFlushDetail,
|
|
86115
|
+
ageMs: boundaryFlushStartedAt ? Date.now() - boundaryFlushStartedAt : 0,
|
|
86116
|
+
units: boundaryFlushUnits,
|
|
86117
|
+
items: boundaryFlushItems,
|
|
86118
|
+
chunks: boundaryFlushBatches,
|
|
86119
|
+
stalledMs: boundaryFlushLastAdvanceAt ? Date.now() - boundaryFlushLastAdvanceAt : 0,
|
|
86120
|
+
// Held workspaces, so a hold is REPORTABLE and not merely logged once at the
|
|
86121
|
+
// transition. `errata status` reads this rather than the on-disk profile:
|
|
86122
|
+
// the profile records what the workspace INTENDS to assert, which is why
|
|
86123
|
+
// status could print "→ project 019f462d" for hours while every batch was
|
|
86124
|
+
// being held or shipped unlinked. A hold is the daemon's actual behavior.
|
|
86125
|
+
holds: [
|
|
86126
|
+
...[...projectHolds.values()].map((h) => ({
|
|
86127
|
+
workspace: h.workspace,
|
|
86128
|
+
kind: "project",
|
|
86129
|
+
reason: h.reason,
|
|
86130
|
+
sinceMs: Date.now() - h.since
|
|
86131
|
+
})),
|
|
86132
|
+
...[...identityBlocks.values()].map((h) => ({
|
|
86133
|
+
workspace: h.workspace,
|
|
86134
|
+
kind: "identity",
|
|
86135
|
+
reason: h.reason,
|
|
86136
|
+
sinceMs: Date.now() - h.since
|
|
86137
|
+
}))
|
|
86138
|
+
]
|
|
86139
|
+
};
|
|
86140
|
+
};
|
|
85930
86141
|
const endFlush = () => {
|
|
85931
86142
|
boundaryFlushing = false;
|
|
85932
86143
|
boundaryFlushStage = "";
|
|
@@ -86307,6 +86518,10 @@ async function startMultiDaemon(opts = {}) {
|
|
|
86307
86518
|
app.post("/api/message", (c) => forward(c, cwdOf, { affinity: "follow" }));
|
|
86308
86519
|
app.post("/api/session-end", (c) => forward(c, cwdOf, { affinity: "follow" }));
|
|
86309
86520
|
app.post("/api/context-inject", (c) => forward(c, cwdOf, { affinity: "follow" }));
|
|
86521
|
+
app.post("/api/statusline", async (c) => {
|
|
86522
|
+
const body2 = await c.req.json().catch(() => ({}));
|
|
86523
|
+
return c.text(statuslineText(String(body2["session_id"] ?? "")));
|
|
86524
|
+
});
|
|
86310
86525
|
for (const r of records) {
|
|
86311
86526
|
mountedIds.add(r.id);
|
|
86312
86527
|
app.route(`/ws/${r.id}`, r.webApp);
|
|
@@ -86380,9 +86595,9 @@ async function startMultiDaemon(opts = {}) {
|
|
|
86380
86595
|
const reindexAll = async (rOpts) => {
|
|
86381
86596
|
const out2 = /* @__PURE__ */ new Map();
|
|
86382
86597
|
const toIndex = records.filter((r) => rOpts?.force || r.engine.store.nodeCount() === 0);
|
|
86383
|
-
const
|
|
86598
|
+
const plural2 = (n) => `repositor${n === 1 ? "y" : "ies"}`;
|
|
86384
86599
|
if (toIndex.length > 0) {
|
|
86385
|
-
notifyEvent("index-started", `Indexing ${toIndex.length} ${
|
|
86600
|
+
notifyEvent("index-started", `Indexing ${toIndex.length} ${plural2(toIndex.length)}\u2026`);
|
|
86386
86601
|
}
|
|
86387
86602
|
let indexed = 0;
|
|
86388
86603
|
for (const r of records) {
|
|
@@ -86404,7 +86619,7 @@ async function startMultiDaemon(opts = {}) {
|
|
|
86404
86619
|
}
|
|
86405
86620
|
}
|
|
86406
86621
|
if (toIndex.length > 0) {
|
|
86407
|
-
notifyEvent("index-completed", `Indexed ${indexed} ${
|
|
86622
|
+
notifyEvent("index-completed", `Indexed ${indexed} ${plural2(indexed)}`);
|
|
86408
86623
|
}
|
|
86409
86624
|
return out2;
|
|
86410
86625
|
};
|
|
@@ -86503,8 +86718,10 @@ async function startMultiDaemon(opts = {}) {
|
|
|
86503
86718
|
if (!hasCloudCredential(c)) return;
|
|
86504
86719
|
const client = cloudNow();
|
|
86505
86720
|
for (const r of records) {
|
|
86506
|
-
|
|
86507
|
-
|
|
86721
|
+
const working = loadProfile(r.root) ?? r.engine.profile;
|
|
86722
|
+
if (!effectiveConsent(c, working).sync) continue;
|
|
86723
|
+
const out2 = await ensureProjectLink(r.root, working, client);
|
|
86724
|
+
if (working !== r.engine.profile) adoptLinkFields(r.engine.profile, working);
|
|
86508
86725
|
if (out2.linked) {
|
|
86509
86726
|
console.log(
|
|
86510
86727
|
`[errata] linked ${r.engine.profile.name} \u2192 project ${out2.projectId}${out2.created ? " (created)" : ""}`
|
|
@@ -86512,7 +86729,7 @@ async function startMultiDaemon(opts = {}) {
|
|
|
86512
86729
|
} else if (out2.reason === "error") {
|
|
86513
86730
|
console.warn(`[errata] project link failed for ${r.engine.profile.name}: ${out2.detail}`);
|
|
86514
86731
|
} else if (out2.reason === "held") {
|
|
86515
|
-
noteProjectHold(r.id, r.engine.profile.name, out2.detail);
|
|
86732
|
+
noteProjectHold(r.id, r.engine.profile.name, out2.detail, r.root);
|
|
86516
86733
|
}
|
|
86517
86734
|
let primeSalt = null;
|
|
86518
86735
|
if (r.engine.profile.projectId && !r.engine.profile.projectHydratedAt) {
|
|
@@ -86976,7 +87193,8 @@ async function startMultiDaemon(opts = {}) {
|
|
|
86976
87193
|
noteProjectHold(
|
|
86977
87194
|
r.id,
|
|
86978
87195
|
r.entry.name,
|
|
86979
|
-
"this repo is already on errata in another workspace \u2014 run `errata link` to join it or create your own project"
|
|
87196
|
+
"this repo is already on errata in another workspace \u2014 run `errata link` to join it or create your own project",
|
|
87197
|
+
r.root
|
|
86980
87198
|
);
|
|
86981
87199
|
continue;
|
|
86982
87200
|
}
|
|
@@ -87935,6 +88153,7 @@ try {
|
|
|
87935
88153
|
const event = payload.hook_event_name;
|
|
87936
88154
|
if (!Number.isInteger(port) || port < 1 || port > 65535) process.exit(0);
|
|
87937
88155
|
const contexts = [];
|
|
88156
|
+
const messages = [];
|
|
87938
88157
|
const post = async (endpoint, relay = false) => {
|
|
87939
88158
|
try {
|
|
87940
88159
|
const response = await fetch('http://127.0.0.1:' + port + '/api/' + endpoint, {
|
|
@@ -87945,6 +88164,8 @@ try {
|
|
|
87945
88164
|
const result = await response.json();
|
|
87946
88165
|
const context = result.hookSpecificOutput?.additionalContext;
|
|
87947
88166
|
if (typeof context === 'string' && context.trim()) contexts.push(context);
|
|
88167
|
+
// The user-visible recall card; Codex renders it inline as '↳ Hook · …'.
|
|
88168
|
+
if (typeof result.systemMessage === 'string' && result.systemMessage.trim()) messages.push(result.systemMessage);
|
|
87948
88169
|
} catch { /* An unavailable daemon must never block the agent. */ }
|
|
87949
88170
|
};
|
|
87950
88171
|
if (event === 'PreToolUse' || event === 'PostToolUse') await post('hook', true);
|
|
@@ -87955,10 +88176,13 @@ try {
|
|
|
87955
88176
|
// Background extraction finishing later arrives at the next tool boundary.
|
|
87956
88177
|
await post('context-inject', true);
|
|
87957
88178
|
}
|
|
87958
|
-
if (contexts.length) {
|
|
87959
|
-
process.stdout.write(JSON.stringify({
|
|
87960
|
-
|
|
87961
|
-
|
|
88179
|
+
if (contexts.length || messages.length) {
|
|
88180
|
+
process.stdout.write(JSON.stringify({
|
|
88181
|
+
...(messages.length ? { systemMessage: messages.join('\n\n') } : {}),
|
|
88182
|
+
...(contexts.length ? { hookSpecificOutput: {
|
|
88183
|
+
hookEventName: event, additionalContext: contexts.join('\n\n'),
|
|
88184
|
+
} } : {}),
|
|
88185
|
+
}));
|
|
87962
88186
|
}
|
|
87963
88187
|
} catch { /* Invalid input is also fail-open. */ }
|
|
87964
88188
|
`;
|
|
@@ -88152,6 +88376,45 @@ function consolidationGapMs(lastPassMs, policy) {
|
|
|
88152
88376
|
return Math.max(policy.baseFloorMs, lastPassMs * policy.dutyFactor);
|
|
88153
88377
|
}
|
|
88154
88378
|
|
|
88379
|
+
// src/claude-statusline.ts
|
|
88380
|
+
import { mkdirSync as mkdirSync10, writeFileSync as writeFileSync26 } from "node:fs";
|
|
88381
|
+
import { join as join41 } from "node:path";
|
|
88382
|
+
var STATUSLINE_RELAY_SOURCE = String.raw`// errata-managed: regenerated by errata install-hooks claude
|
|
88383
|
+
import { readFileSync } from 'node:fs';
|
|
88384
|
+
|
|
88385
|
+
try {
|
|
88386
|
+
const port = Number(process.argv[2]);
|
|
88387
|
+
if (Number.isInteger(port) && port > 0 && port < 65536) {
|
|
88388
|
+
const response = await fetch('http://127.0.0.1:' + port + '/api/statusline', {
|
|
88389
|
+
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
|
88390
|
+
body: readFileSync(0, 'utf8') || '{}', signal: AbortSignal.timeout(800),
|
|
88391
|
+
});
|
|
88392
|
+
if (response.ok) process.stdout.write(await response.text());
|
|
88393
|
+
}
|
|
88394
|
+
} catch { /* An unavailable daemon leaves the status line empty. */ }
|
|
88395
|
+
`;
|
|
88396
|
+
var STATUSLINE_RELAY_FILE = "errata-statusline.mjs";
|
|
88397
|
+
function buildStatusLine(port, nodePath, relayPath) {
|
|
88398
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535) throw new Error("Invalid status line port");
|
|
88399
|
+
const q = (p) => {
|
|
88400
|
+
if (/["$`\r\n]/.test(p)) throw new Error("Unsupported character in status line path");
|
|
88401
|
+
return `"${p.replace(/\\/g, "/")}"`;
|
|
88402
|
+
};
|
|
88403
|
+
return { type: "command", command: `${q(nodePath)} ${q(relayPath)} ${port}`, padding: 0 };
|
|
88404
|
+
}
|
|
88405
|
+
function isErrataStatusLine(value) {
|
|
88406
|
+
return !!value && typeof value === "object" && typeof value["command"] === "string" && value["command"].includes(STATUSLINE_RELAY_FILE);
|
|
88407
|
+
}
|
|
88408
|
+
function installStatusLine(settings, root, port, nodePath = process.execPath) {
|
|
88409
|
+
if (settings["statusLine"] !== void 0 && !isErrataStatusLine(settings["statusLine"])) return false;
|
|
88410
|
+
const dir = join41(root, ".claude", "hooks");
|
|
88411
|
+
const relay = join41(dir, STATUSLINE_RELAY_FILE);
|
|
88412
|
+
mkdirSync10(dir, { recursive: true });
|
|
88413
|
+
writeFileSync26(relay, STATUSLINE_RELAY_SOURCE, "utf8");
|
|
88414
|
+
settings["statusLine"] = buildStatusLine(port, nodePath, relay);
|
|
88415
|
+
return true;
|
|
88416
|
+
}
|
|
88417
|
+
|
|
88155
88418
|
// src/opencode-plugin.ts
|
|
88156
88419
|
var OPENCODE_PLUGIN_MARKER = "// @inerrata opencode plugin \u2014 errata-managed (errata install-hooks opencode)";
|
|
88157
88420
|
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.";
|
|
@@ -88558,13 +88821,13 @@ async function cmdInit() {
|
|
|
88558
88821
|
console.log("");
|
|
88559
88822
|
console.log("installing harness hooks...");
|
|
88560
88823
|
const { existsSync: existsSync35 } = await import("node:fs");
|
|
88561
|
-
const { join:
|
|
88824
|
+
const { join: join43 } = await import("node:path");
|
|
88562
88825
|
try {
|
|
88563
88826
|
await installClaudeHooks(port);
|
|
88564
88827
|
} catch (err2) {
|
|
88565
88828
|
console.warn(` \u26A0\uFE0F Claude hook install failed: ${err2 instanceof Error ? err2.message : err2}`);
|
|
88566
88829
|
}
|
|
88567
|
-
if (existsSync35(
|
|
88830
|
+
if (existsSync35(join43(ROOT, ".cursor"))) {
|
|
88568
88831
|
try {
|
|
88569
88832
|
await installCursorMcpConfig();
|
|
88570
88833
|
} catch (err2) {
|
|
@@ -89389,7 +89652,6 @@ async function cmdLink(args2) {
|
|
|
89389
89652
|
return;
|
|
89390
89653
|
}
|
|
89391
89654
|
delete profile.projectLinkDisabled;
|
|
89392
|
-
delete profile.projectLinkHold;
|
|
89393
89655
|
const remote = flag("--remote");
|
|
89394
89656
|
if (remote) {
|
|
89395
89657
|
const loc = detectRepoLocator(ROOT, remote);
|
|
@@ -90439,7 +90701,7 @@ async function gatherReportData(generatedAt) {
|
|
|
90439
90701
|
};
|
|
90440
90702
|
}
|
|
90441
90703
|
async function cmdReport(args2) {
|
|
90442
|
-
const { mkdirSync:
|
|
90704
|
+
const { mkdirSync: mkdirSync11, writeFileSync: writeFileSync27 } = await import("node:fs");
|
|
90443
90705
|
const { renderReport: renderReport2 } = await Promise.resolve().then(() => (init_report_render(), report_render_exports));
|
|
90444
90706
|
const includeFutureVerbs = args2.includes("--future-verbs");
|
|
90445
90707
|
const now = /* @__PURE__ */ new Date();
|
|
@@ -90450,10 +90712,10 @@ async function cmdReport(args2) {
|
|
|
90450
90712
|
process.exit(2);
|
|
90451
90713
|
}
|
|
90452
90714
|
const outDir = workspacePaths(ROOT).configDir;
|
|
90453
|
-
|
|
90715
|
+
mkdirSync11(outDir, { recursive: true });
|
|
90454
90716
|
const files = renderReport2(data, { includeFutureVerbs });
|
|
90455
|
-
for (const f of files)
|
|
90456
|
-
const indexPath =
|
|
90717
|
+
for (const f of files) writeFileSync27(join42(outDir, f.name), f.html, "utf8");
|
|
90718
|
+
const indexPath = join42(outDir, "report.html");
|
|
90457
90719
|
console.log(`report \u2192 ${indexPath}`);
|
|
90458
90720
|
console.log(
|
|
90459
90721
|
` ${data.repos.length} repo(s) \xB7 ${files.length} file(s) \xB7 ${data.rollup.nodes.toLocaleString("en-US")} nodes`
|
|
@@ -90573,11 +90835,11 @@ async function cmdInstallHooks(args2) {
|
|
|
90573
90835
|
}
|
|
90574
90836
|
}
|
|
90575
90837
|
async function installOpencodePlugin(port) {
|
|
90576
|
-
const { mkdirSync:
|
|
90577
|
-
const { join:
|
|
90578
|
-
const dir =
|
|
90579
|
-
if (!existsSync35(dir))
|
|
90580
|
-
const file2 =
|
|
90838
|
+
const { mkdirSync: mkdirSync11, existsSync: existsSync35, readFileSync: readFileSync36, writeFileSync: writeFileSync27 } = await import("node:fs");
|
|
90839
|
+
const { join: join43 } = await import("node:path");
|
|
90840
|
+
const dir = join43(ROOT, ".opencode", "plugin");
|
|
90841
|
+
if (!existsSync35(dir)) mkdirSync11(dir, { recursive: true });
|
|
90842
|
+
const file2 = join43(dir, "inerrata.js");
|
|
90581
90843
|
if (existsSync35(file2)) {
|
|
90582
90844
|
const head2 = readFileSync36(file2, "utf8").split("\n")[0] ?? "";
|
|
90583
90845
|
if (head2.trim() !== OPENCODE_PLUGIN_MARKER) {
|
|
@@ -90585,8 +90847,8 @@ async function installOpencodePlugin(port) {
|
|
|
90585
90847
|
process.exit(2);
|
|
90586
90848
|
}
|
|
90587
90849
|
}
|
|
90588
|
-
|
|
90589
|
-
const cfgFile =
|
|
90850
|
+
writeFileSync27(file2, buildOpencodePluginSource(port, { version: DAEMON_VERSION }), "utf8");
|
|
90851
|
+
const cfgFile = join43(ROOT, "opencode.json");
|
|
90590
90852
|
let cfg = { $schema: "https://opencode.ai/config.json" };
|
|
90591
90853
|
if (existsSync35(cfgFile)) {
|
|
90592
90854
|
try {
|
|
@@ -90601,7 +90863,7 @@ async function installOpencodePlugin(port) {
|
|
|
90601
90863
|
if (!hadErrata) {
|
|
90602
90864
|
const inv = errataMcpInvocation();
|
|
90603
90865
|
cfg.mcp["errata"] = { type: "local", command: [inv.command, ...inv.args] };
|
|
90604
|
-
|
|
90866
|
+
writeFileSync27(cfgFile, JSON.stringify(cfg, null, 2) + "\n", "utf8");
|
|
90605
90867
|
}
|
|
90606
90868
|
console.log(`installed OpenCode plugin \u2192 ${file2}`);
|
|
90607
90869
|
if (hadErrata) console.log(` mcp.errata already present in ${cfgFile} \u2014 left untouched`);
|
|
@@ -90621,11 +90883,11 @@ function errataMcpInvocation() {
|
|
|
90621
90883
|
return { command: cmd2, args: args2 };
|
|
90622
90884
|
}
|
|
90623
90885
|
async function installClaudeHooks(port) {
|
|
90624
|
-
const { mkdirSync:
|
|
90625
|
-
const { join:
|
|
90626
|
-
const dir =
|
|
90627
|
-
if (!existsSync35(dir))
|
|
90628
|
-
const file2 =
|
|
90886
|
+
const { mkdirSync: mkdirSync11, existsSync: existsSync35, readFileSync: readFileSync36, writeFileSync: writeFileSync27 } = await import("node:fs");
|
|
90887
|
+
const { join: join43 } = await import("node:path");
|
|
90888
|
+
const dir = join43(ROOT, ".claude");
|
|
90889
|
+
if (!existsSync35(dir)) mkdirSync11(dir, { recursive: true });
|
|
90890
|
+
const file2 = join43(dir, "settings.json");
|
|
90629
90891
|
let settings = {};
|
|
90630
90892
|
if (existsSync35(file2)) {
|
|
90631
90893
|
try {
|
|
@@ -90647,7 +90909,8 @@ async function installClaudeHooks(port) {
|
|
|
90647
90909
|
};
|
|
90648
90910
|
for (const [event, command, eventMatcher] of [
|
|
90649
90911
|
["PreToolUse", preCmd, matcher],
|
|
90650
|
-
["PostToolUse", postCmd, postMatcher]
|
|
90912
|
+
["PostToolUse", postCmd, postMatcher],
|
|
90913
|
+
["PostToolUseFailure", postCmd, postMatcher]
|
|
90651
90914
|
]) {
|
|
90652
90915
|
const list = settings.hooks[event] ??= [];
|
|
90653
90916
|
dropErrata(list);
|
|
@@ -90675,10 +90938,20 @@ async function installClaudeHooks(port) {
|
|
|
90675
90938
|
dropErrata(list);
|
|
90676
90939
|
list.push({ hooks: [{ type: "command", command: injectCmd }] });
|
|
90677
90940
|
}
|
|
90678
|
-
|
|
90941
|
+
const { homedir: homedir8 } = await import("node:os");
|
|
90942
|
+
let userStatusLine;
|
|
90943
|
+
try {
|
|
90944
|
+
userStatusLine = JSON.parse(readFileSync36(join43(homedir8(), ".claude", "settings.json"), "utf8"))["statusLine"];
|
|
90945
|
+
} catch {
|
|
90946
|
+
}
|
|
90947
|
+
const statusLineInstalled = (userStatusLine === void 0 || isErrataStatusLine(userStatusLine)) && installStatusLine(settings, ROOT, port);
|
|
90948
|
+
writeFileSync27(file2, JSON.stringify(settings, null, 2) + "\n", "utf8");
|
|
90679
90949
|
console.log(`installed Claude Code hooks \u2192 ${file2}`);
|
|
90950
|
+
console.log(
|
|
90951
|
+
statusLineInstalled ? " status line: errata recall card in colour (daemon env ERRATA_RECALL_CARD=off hides the cards)" : " status line: kept your own \u2014 errata's recall card shows inline only"
|
|
90952
|
+
);
|
|
90680
90953
|
await installClaudeMcpConfig();
|
|
90681
|
-
const claudeMd =
|
|
90954
|
+
const claudeMd = join43(ROOT, "CLAUDE.md");
|
|
90682
90955
|
const recall = writeManagedBlock(claudeMd, { body: RECALL_FIRST_BLOCK });
|
|
90683
90956
|
if (recall.kind === "collision") {
|
|
90684
90957
|
console.warn(
|
|
@@ -90690,11 +90963,11 @@ async function installClaudeHooks(port) {
|
|
|
90690
90963
|
console.log(` endpoint: http://127.0.0.1:${port}/api/hook`);
|
|
90691
90964
|
}
|
|
90692
90965
|
async function installClaudeMcpConfig() {
|
|
90693
|
-
const { mkdirSync:
|
|
90694
|
-
const { join:
|
|
90695
|
-
const file2 =
|
|
90966
|
+
const { mkdirSync: mkdirSync11, existsSync: existsSync35, readFileSync: readFileSync36, writeFileSync: writeFileSync27 } = await import("node:fs");
|
|
90967
|
+
const { join: join43, dirname: dirname13 } = await import("node:path");
|
|
90968
|
+
const file2 = join43(ROOT, ".mcp.json");
|
|
90696
90969
|
const dir = dirname13(file2);
|
|
90697
|
-
if (!existsSync35(dir))
|
|
90970
|
+
if (!existsSync35(dir)) mkdirSync11(dir, { recursive: true });
|
|
90698
90971
|
let cfg = {};
|
|
90699
90972
|
if (existsSync35(file2)) {
|
|
90700
90973
|
try {
|
|
@@ -90706,17 +90979,17 @@ async function installClaudeMcpConfig() {
|
|
|
90706
90979
|
}
|
|
90707
90980
|
cfg.mcpServers ??= {};
|
|
90708
90981
|
cfg.mcpServers["errata"] = errataMcpInvocation();
|
|
90709
|
-
|
|
90982
|
+
writeFileSync27(file2, JSON.stringify(cfg, null, 2) + "\n", "utf8");
|
|
90710
90983
|
console.log(`installed Claude Code MCP server config \u2192 ${file2}`);
|
|
90711
90984
|
console.log(` Claude Code will spawn \`errata mcp\` on workspace open.`);
|
|
90712
90985
|
console.log(` Tools: errata.search / locate / neighbors / callers_of / what_uses / show / similar`);
|
|
90713
90986
|
}
|
|
90714
90987
|
async function installCursorMcpConfig() {
|
|
90715
|
-
const { mkdirSync:
|
|
90716
|
-
const { join:
|
|
90717
|
-
const dir =
|
|
90718
|
-
if (!existsSync35(dir))
|
|
90719
|
-
const file2 =
|
|
90988
|
+
const { mkdirSync: mkdirSync11, existsSync: existsSync35, readFileSync: readFileSync36, writeFileSync: writeFileSync27 } = await import("node:fs");
|
|
90989
|
+
const { join: join43 } = await import("node:path");
|
|
90990
|
+
const dir = join43(ROOT, ".cursor");
|
|
90991
|
+
if (!existsSync35(dir)) mkdirSync11(dir, { recursive: true });
|
|
90992
|
+
const file2 = join43(dir, "mcp.json");
|
|
90720
90993
|
let cfg = {};
|
|
90721
90994
|
if (existsSync35(file2)) {
|
|
90722
90995
|
try {
|
|
@@ -90728,7 +91001,7 @@ async function installCursorMcpConfig() {
|
|
|
90728
91001
|
}
|
|
90729
91002
|
cfg.mcpServers ??= {};
|
|
90730
91003
|
cfg.mcpServers["errata"] = errataMcpInvocation();
|
|
90731
|
-
|
|
91004
|
+
writeFileSync27(file2, JSON.stringify(cfg, null, 2) + "\n", "utf8");
|
|
90732
91005
|
console.log(`installed Cursor MCP server config \u2192 ${file2}`);
|
|
90733
91006
|
console.log(` Cursor will spawn \`errata mcp\` on workspace open.`);
|
|
90734
91007
|
console.log(` Tools: errata.search / locate / neighbors / callers_of / what_uses`);
|
|
@@ -90739,7 +91012,7 @@ async function installCodexHooks(port) {
|
|
|
90739
91012
|
const gitRoot = nearestGitRoot(process.cwd());
|
|
90740
91013
|
const hookRoot = gitRoot && mainCheckoutOfWorktree(gitRoot) ? gitRoot : ROOT;
|
|
90741
91014
|
const file2 = writeCodexHooks(hookRoot, port);
|
|
90742
|
-
const instructions = writeManagedBlock(
|
|
91015
|
+
const instructions = writeManagedBlock(join42(hookRoot, "AGENTS.md"), {
|
|
90743
91016
|
body: AGENTS_POINTER_BODY,
|
|
90744
91017
|
stable: true
|
|
90745
91018
|
});
|
|
@@ -91143,7 +91416,7 @@ async function cmdDash(args2) {
|
|
|
91143
91416
|
await yieldToLoop2();
|
|
91144
91417
|
try {
|
|
91145
91418
|
const items = selectDurableMemory(handle2.sharedStore, r.engine.profile);
|
|
91146
|
-
const res = bleedRules(
|
|
91419
|
+
const res = bleedRules(join42(r.root, ".claude", "rules"), items);
|
|
91147
91420
|
if (res.written || res.pruned) {
|
|
91148
91421
|
console.log(
|
|
91149
91422
|
`[rules:${r.entry.name}] ${res.written} collective principle(s) \u2192 .claude/rules` + (res.pruned ? `, ${res.pruned} pruned` : "")
|