hebbian 0.8.0 → 0.8.1

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/digest.d.ts CHANGED
@@ -48,7 +48,7 @@ export declare function readHookInput(stdin: string): {
48
48
  */
49
49
  export declare function digestTranscript(brainRoot: string, transcriptPath: string, sessionId?: string): DigestResult;
50
50
  /**
51
- * Parse tool_result blocks from a Claude Code transcript.
51
+ * Parse tool_result blocks from a Claude Code transcript file.
52
52
  * Returns detected failures (exit code ≠ 0, is_error = true).
53
53
  */
54
54
  export declare function parseToolResults(transcriptPath: string): ToolFailure[];
@@ -68,6 +68,18 @@ export declare function detectToolFailure(block: {
68
68
  }>;
69
69
  is_error?: boolean;
70
70
  }, toolUseResult?: TranscriptLine['toolUseResult']): ToolFailure | null;
71
+ /**
72
+ * Soft-detect a failure from a tool_result where is_error is false.
73
+ * Catches errors masked by `|| true` or `2>&1` by matching conservative
74
+ * patterns against stdout/stderr content.
75
+ */
76
+ export declare function detectSoftFailure(block: {
77
+ content?: string | Array<{
78
+ type: string;
79
+ text?: string;
80
+ }>;
81
+ is_error?: boolean;
82
+ }, toolUseResult?: TranscriptLine['toolUseResult']): ToolFailure | null;
71
83
  /**
72
84
  * Extract corrections from user messages using pattern matching.
73
85
  * Returns up to MAX_CORRECTIONS_PER_SESSION corrections.
@@ -1 +1 @@
1
- {"version":3,"file":"digest.d.ts","sourceRoot":"","sources":["../src/digest.ts"],"names":[],"mappings":"AAiBA,MAAM,WAAW,YAAY;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,mBAAmB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,IAAI,GAAG,IAAI,GAAG,MAAM,GAAG,MAAM,CAAC;IACtC,QAAQ,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,UAAU,cAAc;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE;QACT,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,OAAO,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,IAAI,CAAC,EAAE,MAAM,CAAC;YAAC,WAAW,CAAC,EAAE,MAAM,CAAC;YAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;SAAE,CAAC,CAAC;KACpG,CAAC;IACF,aAAa,CAAC,EAAE;QACf,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,MAAM,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,WAAW;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CAClB;AA+CD;;GAEG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG;IAAE,cAAc,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAYjG;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,YAAY,CA8E5G;AAiDD;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,cAAc,EAAE,MAAM,GAAG,WAAW,EAAE,CA4BtE;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,WAAW,EAAE,GAAG,WAAW,EAAE,CAoB1E;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAChC,KAAK,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,EACxF,aAAa,CAAC,EAAE,cAAc,CAAC,eAAe,CAAC,GAC7C,WAAW,GAAG,IAAI,CA8BpB;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,mBAAmB,EAAE,CAiC5E"}
1
+ {"version":3,"file":"digest.d.ts","sourceRoot":"","sources":["../src/digest.ts"],"names":[],"mappings":"AAqBA,MAAM,WAAW,YAAY;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,mBAAmB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,IAAI,GAAG,IAAI,GAAG,MAAM,GAAG,MAAM,CAAC;IACtC,QAAQ,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,UAAU,cAAc;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE;QACT,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,OAAO,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,IAAI,CAAC,EAAE,MAAM,CAAC;YAAC,WAAW,CAAC,EAAE,MAAM,CAAC;YAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;SAAE,CAAC,CAAC;KACpG,CAAC;IACF,aAAa,CAAC,EAAE;QACf,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,MAAM,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,WAAW;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CAClB;AA+CD;;GAEG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG;IAAE,cAAc,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAYjG;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,YAAY,CAwF5G;AAsDD;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,cAAc,EAAE,MAAM,GAAG,WAAW,EAAE,CAGtE;AAqCD;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,WAAW,EAAE,GAAG,WAAW,EAAE,CAoB1E;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAChC,KAAK,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,EACxF,aAAa,CAAC,EAAE,cAAc,CAAC,eAAe,CAAC,GAC7C,WAAW,GAAG,IAAI,CA8BpB;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAChC,KAAK,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,EACxF,aAAa,CAAC,EAAE,cAAc,CAAC,eAAe,CAAC,GAC7C,WAAW,GAAG,IAAI,CAiCpB;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,mBAAmB,EAAE,CAiC5E"}
package/dist/index.js CHANGED
@@ -1814,12 +1814,21 @@ function digestTranscript(brainRoot, transcriptPath, sessionId) {
1814
1814
  const resolvedSessionId = sessionId || basename(transcriptPath, ".jsonl");
1815
1815
  const logDir = join15(brainRoot, DIGEST_LOG_DIR);
1816
1816
  const logPath = join15(logDir, `${resolvedSessionId}.jsonl`);
1817
- if (existsSync14(logPath)) {
1817
+ const content = readFileSync7(transcriptPath, "utf8");
1818
+ const allLines = content.split("\n").filter(Boolean);
1819
+ const totalLines = allLines.length;
1820
+ const meta = readAuditMeta(logPath);
1821
+ if (existsSync14(logPath) && !meta) {
1818
1822
  console.log(`\u23ED already digested session ${resolvedSessionId}, skip`);
1819
1823
  return { corrections: 0, skipped: 0, toolFailures: 0, transcriptPath, sessionId: resolvedSessionId };
1820
1824
  }
1821
- const messages = parseTranscript(transcriptPath);
1822
- const toolFailures = parseToolResults(transcriptPath);
1825
+ const skipLines = meta ? meta.lineCount : 0;
1826
+ if (skipLines >= totalLines) {
1827
+ return { corrections: 0, skipped: 0, toolFailures: 0, transcriptPath, sessionId: resolvedSessionId };
1828
+ }
1829
+ const newLines = allLines.slice(skipLines);
1830
+ const messages = parseTranscriptFromLines(newLines);
1831
+ const toolFailures = parseToolResultsFromLines(newLines);
1823
1832
  for (const failure of toolFailures) {
1824
1833
  logEpisode(brainRoot, "tool-failure", failure.toolName, failure.errorText);
1825
1834
  }
@@ -1834,11 +1843,11 @@ function digestTranscript(brainRoot, transcriptPath, sessionId) {
1834
1843
  const corrections = extractCorrections(messages);
1835
1844
  if (corrections.length === 0 && toolFailures.length === 0) {
1836
1845
  console.log(`\u{1F4DD} digest: no corrections found in session ${resolvedSessionId}`);
1837
- writeAuditLog(brainRoot, resolvedSessionId, []);
1846
+ writeAuditLog(brainRoot, resolvedSessionId, [], totalLines);
1838
1847
  return { corrections: 0, skipped: messages.length, toolFailures: toolFailures.length, transcriptPath, sessionId: resolvedSessionId };
1839
1848
  }
1840
1849
  if (corrections.length === 0) {
1841
- writeAuditLog(brainRoot, resolvedSessionId, []);
1850
+ writeAuditLog(brainRoot, resolvedSessionId, [], totalLines);
1842
1851
  return { corrections: 0, skipped: messages.length, toolFailures: toolFailures.length, transcriptPath, sessionId: resolvedSessionId };
1843
1852
  }
1844
1853
  let applied = 0;
@@ -1854,7 +1863,7 @@ function digestTranscript(brainRoot, transcriptPath, sessionId) {
1854
1863
  auditEntries.push({ correction, applied: false });
1855
1864
  }
1856
1865
  }
1857
- writeAuditLog(brainRoot, resolvedSessionId, auditEntries);
1866
+ writeAuditLog(brainRoot, resolvedSessionId, auditEntries, totalLines);
1858
1867
  console.log(`\u{1F4DD} digest: ${applied} correction(s) from session ${resolvedSessionId}`);
1859
1868
  return {
1860
1869
  corrections: applied,
@@ -1864,9 +1873,7 @@ function digestTranscript(brainRoot, transcriptPath, sessionId) {
1864
1873
  sessionId: resolvedSessionId
1865
1874
  };
1866
1875
  }
1867
- function parseTranscript(transcriptPath) {
1868
- const content = readFileSync7(transcriptPath, "utf8");
1869
- const lines = content.split("\n").filter(Boolean);
1876
+ function parseTranscriptFromLines(lines) {
1870
1877
  const messages = [];
1871
1878
  for (const line of lines) {
1872
1879
  let entry;
@@ -1892,9 +1899,19 @@ function extractText(content) {
1892
1899
  return null;
1893
1900
  }
1894
1901
  var MAX_FAILURES_PER_SESSION = 20;
1902
+ var SOFT_ERROR_PATTERNS = [
1903
+ /(?:^|\n)\S*(?:\(\w+\):\d+: )?command not found:/m,
1904
+ // shell: command not found
1905
+ /(?:^|\n)npm error\b/m,
1906
+ // npm error (not npm warn)
1907
+ /(?:^|\n)fatal: /m
1908
+ // git fatal
1909
+ ];
1895
1910
  function parseToolResults(transcriptPath) {
1896
1911
  const content = readFileSync7(transcriptPath, "utf8");
1897
- const lines = content.split("\n").filter(Boolean);
1912
+ return parseToolResultsFromLines(content.split("\n").filter(Boolean));
1913
+ }
1914
+ function parseToolResultsFromLines(lines) {
1898
1915
  const failures = [];
1899
1916
  for (const line of lines) {
1900
1917
  if (failures.length >= MAX_FAILURES_PER_SESSION) break;
@@ -1908,9 +1925,13 @@ function parseToolResults(transcriptPath) {
1908
1925
  if (!entry.message || !Array.isArray(entry.message.content)) continue;
1909
1926
  for (const block of entry.message.content) {
1910
1927
  if (block.type !== "tool_result") continue;
1911
- if (!block.is_error) continue;
1912
- const failure = detectToolFailure(block, entry.toolUseResult);
1913
- if (failure) failures.push(failure);
1928
+ if (block.is_error) {
1929
+ const failure = detectToolFailure(block, entry.toolUseResult);
1930
+ if (failure) failures.push(failure);
1931
+ } else {
1932
+ const failure = detectSoftFailure(block, entry.toolUseResult);
1933
+ if (failure) failures.push(failure);
1934
+ }
1914
1935
  }
1915
1936
  }
1916
1937
  return failures;
@@ -1949,6 +1970,30 @@ function detectToolFailure(block, toolUseResult) {
1949
1970
  const toolName = firstLine.trim().slice(0, 80);
1950
1971
  return { toolName, exitCode, errorText: errorText.slice(0, 500) };
1951
1972
  }
1973
+ function detectSoftFailure(block, toolUseResult) {
1974
+ let text = "";
1975
+ if (typeof block.content === "string") {
1976
+ text = block.content;
1977
+ } else if (Array.isArray(block.content)) {
1978
+ text = block.content.filter((b) => b.type === "text" && b.text).map((b) => b.text).join("\n");
1979
+ }
1980
+ if (toolUseResult && typeof toolUseResult === "object") {
1981
+ if (toolUseResult.stderr) text += "\n" + toolUseResult.stderr;
1982
+ }
1983
+ if (!text) return null;
1984
+ for (const pattern of SOFT_ERROR_PATTERNS) {
1985
+ const match = text.match(pattern);
1986
+ if (match) {
1987
+ const matchedLine = text.split("\n").find((l) => pattern.test(l)) || "unknown";
1988
+ return {
1989
+ toolName: `[soft] ${matchedLine.trim().slice(0, 70)}`,
1990
+ exitCode: 0,
1991
+ errorText: text.slice(0, 500)
1992
+ };
1993
+ }
1994
+ }
1995
+ return null;
1996
+ }
1952
1997
  function extractCorrections(messages) {
1953
1998
  const corrections = [];
1954
1999
  for (const text of messages) {
@@ -2123,13 +2168,28 @@ function extractKeywords(text) {
2123
2168
  ]);
2124
2169
  return text.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/[^a-zA-Z0-9\u3000-\u9FFF\uAC00-\uD7AF]+/g, " ").toLowerCase().split(/\s+/).filter((t) => t.length > 2 && !STOP_WORDS.has(t));
2125
2170
  }
2126
- function writeAuditLog(brainRoot, sessionId, entries) {
2171
+ function readAuditMeta(logPath) {
2172
+ if (!existsSync14(logPath)) return null;
2173
+ try {
2174
+ const content = readFileSync7(logPath, "utf8");
2175
+ const firstLine = content.split("\n")[0];
2176
+ if (!firstLine) return null;
2177
+ const parsed = JSON.parse(firstLine);
2178
+ if (parsed._meta && typeof parsed.lineCount === "number") {
2179
+ return { lineCount: parsed.lineCount };
2180
+ }
2181
+ } catch {
2182
+ }
2183
+ return null;
2184
+ }
2185
+ function writeAuditLog(brainRoot, sessionId, entries, lineCount) {
2127
2186
  const logDir = join15(brainRoot, DIGEST_LOG_DIR);
2128
2187
  if (!existsSync14(logDir)) {
2129
2188
  mkdirSync9(logDir, { recursive: true });
2130
2189
  }
2131
2190
  const logPath = join15(logDir, `${sessionId}.jsonl`);
2132
- const lines = entries.map(
2191
+ const metaLine = JSON.stringify({ _meta: true, lineCount, ts: (/* @__PURE__ */ new Date()).toISOString() });
2192
+ const entryLines = entries.map(
2133
2193
  (e) => JSON.stringify({
2134
2194
  ts: (/* @__PURE__ */ new Date()).toISOString(),
2135
2195
  path: e.correction.path,
@@ -2139,7 +2199,7 @@ function writeAuditLog(brainRoot, sessionId, entries) {
2139
2199
  applied: e.applied
2140
2200
  })
2141
2201
  );
2142
- writeFileSync11(logPath, lines.join("\n") + (lines.length > 0 ? "\n" : ""), "utf8");
2202
+ writeFileSync11(logPath, [metaLine, ...entryLines].join("\n") + "\n", "utf8");
2143
2203
  }
2144
2204
 
2145
2205
  // src/evolve.ts