@kolisachint/hoocode-agent 0.5.17 → 0.5.19

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 (83) hide show
  1. package/CHANGELOG.md +247 -0
  2. package/dist/core/learn/audit.d.ts +136 -0
  3. package/dist/core/learn/audit.d.ts.map +1 -0
  4. package/dist/core/learn/audit.js +316 -0
  5. package/dist/core/learn/audit.js.map +1 -0
  6. package/dist/core/learn/cache.d.ts +58 -0
  7. package/dist/core/learn/cache.d.ts.map +1 -0
  8. package/dist/core/learn/cache.js +132 -0
  9. package/dist/core/learn/cache.js.map +1 -0
  10. package/dist/core/learn/cluster.d.ts +78 -0
  11. package/dist/core/learn/cluster.d.ts.map +1 -0
  12. package/dist/core/learn/cluster.js +184 -0
  13. package/dist/core/learn/cluster.js.map +1 -0
  14. package/dist/core/learn/coverage.d.ts +58 -0
  15. package/dist/core/learn/coverage.d.ts.map +1 -0
  16. package/dist/core/learn/coverage.js +144 -0
  17. package/dist/core/learn/coverage.js.map +1 -0
  18. package/dist/core/learn/digest.d.ts +13 -0
  19. package/dist/core/learn/digest.d.ts.map +1 -1
  20. package/dist/core/learn/digest.js +113 -14
  21. package/dist/core/learn/digest.js.map +1 -1
  22. package/dist/core/learn/extract.d.ts +108 -105
  23. package/dist/core/learn/extract.d.ts.map +1 -1
  24. package/dist/core/learn/extract.js +308 -447
  25. package/dist/core/learn/extract.js.map +1 -1
  26. package/dist/core/learn/mine.d.ts +178 -0
  27. package/dist/core/learn/mine.d.ts.map +1 -0
  28. package/dist/core/learn/mine.js +390 -0
  29. package/dist/core/learn/mine.js.map +1 -0
  30. package/dist/core/learn/reduce.d.ts +89 -0
  31. package/dist/core/learn/reduce.d.ts.map +1 -0
  32. package/dist/core/learn/reduce.js +179 -0
  33. package/dist/core/learn/reduce.js.map +1 -0
  34. package/dist/core/learn/state.d.ts +19 -18
  35. package/dist/core/learn/state.d.ts.map +1 -1
  36. package/dist/core/learn/state.js +35 -31
  37. package/dist/core/learn/state.js.map +1 -1
  38. package/dist/core/settings-defaults.d.ts +1 -1
  39. package/dist/core/settings-defaults.d.ts.map +1 -1
  40. package/dist/core/settings-defaults.js +1 -1
  41. package/dist/core/settings-defaults.js.map +1 -1
  42. package/dist/core/settings-manager.d.ts +4 -2
  43. package/dist/core/settings-manager.d.ts.map +1 -1
  44. package/dist/core/settings-manager.js +5 -1
  45. package/dist/core/settings-manager.js.map +1 -1
  46. package/dist/core/settings-types.d.ts +1 -1
  47. package/dist/core/settings-types.d.ts.map +1 -1
  48. package/dist/core/settings-types.js.map +1 -1
  49. package/dist/core/startup-progress.d.ts +12 -7
  50. package/dist/core/startup-progress.d.ts.map +1 -1
  51. package/dist/core/startup-progress.js +12 -7
  52. package/dist/core/startup-progress.js.map +1 -1
  53. package/dist/extensions/core/learn.d.ts +8 -4
  54. package/dist/extensions/core/learn.d.ts.map +1 -1
  55. package/dist/extensions/core/learn.js +292 -56
  56. package/dist/extensions/core/learn.js.map +1 -1
  57. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  58. package/dist/modes/interactive/components/footer.js +7 -25
  59. package/dist/modes/interactive/components/footer.js.map +1 -1
  60. package/dist/modes/interactive/components/progress-bar.d.ts +50 -0
  61. package/dist/modes/interactive/components/progress-bar.d.ts.map +1 -0
  62. package/dist/modes/interactive/components/progress-bar.js +77 -0
  63. package/dist/modes/interactive/components/progress-bar.js.map +1 -0
  64. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  65. package/dist/modes/interactive/components/settings-selector.js +1 -1
  66. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  67. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  68. package/dist/modes/interactive/interactive-mode.js +1 -1
  69. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  70. package/dist/modes/interactive/voice/voice-panel.d.ts +6 -1
  71. package/dist/modes/interactive/voice/voice-panel.d.ts.map +1 -1
  72. package/dist/modes/interactive/voice/voice-panel.js +18 -14
  73. package/dist/modes/interactive/voice/voice-panel.js.map +1 -1
  74. package/docs/settings.md +9 -6
  75. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  76. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  77. package/examples/extensions/sandbox/package.json +1 -1
  78. package/examples/extensions/with-deps/package.json +1 -1
  79. package/package.json +4 -4
  80. package/dist/core/learn/normalize.d.ts +0 -65
  81. package/dist/core/learn/normalize.d.ts.map +0 -1
  82. package/dist/core/learn/normalize.js +0 -245
  83. package/dist/core/learn/normalize.js.map +0 -1
@@ -1,245 +0,0 @@
1
- /**
2
- * Normalization and noise filters for the `/learn` extractor.
3
- *
4
- * Everything downstream — clustering, dedupe, cross-session counting — is only
5
- * as good as what happens here. Two runs of the same failing command differ by
6
- * absolute paths, line numbers, PIDs and timings; two statements of the same
7
- * rule differ by politeness and word order. Collapsing that variance is what
8
- * turns a pile of transcripts into counts, and the counts are the product.
9
- *
10
- * These are deliberately lexical and cheap. Semantic grouping is left to the
11
- * model, which sees the deduped candidates and can tell that "run tests with
12
- * coverage" and "use --coverage on the suite" are one rule. Trying to do that
13
- * here would cost a dependency and still be worse.
14
- */
15
- /** Longest text we normalize; error dumps can run to megabytes. */
16
- const MAX_SIGNATURE_CHARS = 400;
17
- /**
18
- * Volatile fragments that make two runs of the same failure look different.
19
- *
20
- * Order is load-bearing, because these patterns overlap and the first one to
21
- * match wins the characters. Longest-and-most-specific therefore runs first:
22
- * paths before line numbers so `foo.ts:12:3` does not leave a bare `:12:3`, and
23
- * timestamps before line numbers so the `:00:00` inside `10:00:00` is not
24
- * mistaken for a line/column pair. Adding a pattern means placing it by
25
- * specificity, not appending it.
26
- */
27
- const VOLATILE_PATTERNS = [
28
- // Absolute POSIX and Windows paths, including the temp dirs that change per run.
29
- [/\/(?:[\w.@+-]+\/)+[\w.@+-]+/g, "<path>"],
30
- [/[A-Za-z]:\\(?:[\w.@+-]+\\)+[\w.@+-]+/g, "<path>"],
31
- // UUIDs, before the shorter hex rule can eat their first group.
32
- [/\b[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\b/gi, "<uuid>"],
33
- // ISO timestamps and clock times, before the line/column rule.
34
- [/\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}:\d{2}(?:\.\d+)?Z?/g, "<time>"],
35
- [/\b\d{1,2}:\d{2}:\d{2}\b/g, "<time>"],
36
- // Ports, before the line/column rule can claim the colon.
37
- [/\b(?:127\.0\.0\.1|localhost):\d+/gi, "localhost:<port>"],
38
- // file:line:col and bare line references.
39
- [/:\d+:\d+/g, ":<line>"],
40
- [/\bline \d+/gi, "line <line>"],
41
- // Hex-ish identifiers: git shas, content hashes, addresses.
42
- [/\b0x[0-9a-f]+\b/gi, "<addr>"],
43
- [/\b[0-9a-f]{7,40}\b/gi, "<hash>"],
44
- // Durations and byte counts reported by build tools.
45
- [/\b\d+(?:\.\d+)?\s?(?:ms|s|sec|secs|seconds|m|min|mins|kb|mb|gb)\b/gi, "<qty>"],
46
- // PIDs, which vary per machine and per run.
47
- [/\bpid[= ]\d+/gi, "pid <pid>"],
48
- // Anything left that is a bare multi-digit run.
49
- [/\b\d{3,}\b/g, "<n>"],
50
- ];
51
- /**
52
- * Collapse a raw error or command output into a comparable signature.
53
- *
54
- * Case is preserved: `TS2307` and `ERR_MODULE_NOT_FOUND` carry meaning that
55
- * lowercasing would blur into surrounding prose, and they are exactly the
56
- * tokens worth matching on.
57
- */
58
- export function normalizeErrorSignature(text) {
59
- let out = text.slice(0, MAX_SIGNATURE_CHARS * 4);
60
- for (const [pattern, replacement] of VOLATILE_PATTERNS) {
61
- out = out.replace(pattern, replacement);
62
- }
63
- return out.replace(/\s+/g, " ").trim().slice(0, MAX_SIGNATURE_CHARS);
64
- }
65
- /**
66
- * Normalize a shell command so two invocations of "the same" command match.
67
- *
68
- * Paths are collapsed but flags are kept — `npm test` and `npm test --coverage`
69
- * are genuinely different commands, and flags are frequently the fix itself.
70
- */
71
- export function normalizeCommand(command) {
72
- const collapsed = command
73
- .replace(/\s+/g, " ")
74
- .replace(/(?:^|\s)\/(?:[\w.@+-]+\/)+[\w.@+-]+/g, " <path>")
75
- .trim();
76
- return collapsed.slice(0, MAX_SIGNATURE_CHARS);
77
- }
78
- /**
79
- * Prefixes that say where or how a command runs rather than what it does.
80
- *
81
- * Nearly every command an agent runs is wrapped in `cd <somewhere> && …`, and
82
- * once paths are collapsed that prefix is identical everywhere. Taking the head
83
- * of the raw command therefore yields `cd <path>` for the whole session, which
84
- * makes every bash step look like every other one.
85
- */
86
- const COMMAND_PREFIX = /^(?:cd\s+\S+\s*&&\s*|(?:sudo|env|time|nice|exec)\s+)/;
87
- /**
88
- * The first `tokens` words of what a command actually does, with the
89
- * where-and-how prefixes stripped. `cd <path> && npm run check` becomes
90
- * `npm run`, which is the part worth comparing across sessions.
91
- */
92
- export function commandHead(command, tokens = 2) {
93
- let out = normalizeCommand(command);
94
- // Prefixes stack: `cd x && sudo env FOO=1 cmd`.
95
- for (let i = 0; i < 4 && COMMAND_PREFIX.test(out); i++) {
96
- out = out.replace(COMMAND_PREFIX, "");
97
- }
98
- return out.split(" ").slice(0, tokens).join(" ").trim();
99
- }
100
- /**
101
- * The part of a command's output that is actually the error.
102
- *
103
- * Build tools lead with a banner — npm's `> pkg@1.0 check > biome check …`, a
104
- * compiler's version line — that is byte-identical across every run. Signing a
105
- * failure by its whole output therefore signs mostly the banner, and two
106
- * unrelated failures of the same command look like the same problem. Scanning
107
- * for the first error-shaped line fixes both the signature and what the reader
108
- * is shown; failing that, the tail beats the head, because tools print the
109
- * banner first and the verdict last.
110
- */
111
- const ERROR_LINE = /\b(?:error|err!|errors?:|failed|failure|cannot|can't|unable to|not found|no such|unexpected|expected|invalid|denied|refused|timed out|traceback|panic|exception|fatal)\b|^\s*(?:×|✖|✗)/i;
112
- export function extractErrorRegion(output, maxChars = 400) {
113
- const lines = output.split("\n");
114
- const start = lines.findIndex((line) => ERROR_LINE.test(line));
115
- if (start >= 0) {
116
- return lines.slice(start).join("\n").slice(0, maxChars).trim();
117
- }
118
- return output.slice(-maxChars).trim();
119
- }
120
- /**
121
- * Output that reports no failure worth learning from: an abort is the user
122
- * changing their mind, and an empty result says nothing at all.
123
- */
124
- export function isUninformativeFailure(output) {
125
- const trimmed = output.trim();
126
- if (trimmed.length === 0)
127
- return true;
128
- return /^(?:command aborted|aborted|cancelled|canceled|interrupted|killed|sigint|sigterm)\b/i.test(trimmed);
129
- }
130
- /** Filler that carries no rule content, stripped before directives are compared. */
131
- const DIRECTIVE_FILLER = /^(?:please|pls|hey|ok|okay|now|also|and|so|just|quickly|can you|could you|would you|i want you to|i'd like you to|let's|lets)\b[\s,]*/i;
132
- /** Words too common to distinguish one directive from another. */
133
- const STOPWORDS = new Set([
134
- "a",
135
- "an",
136
- "and",
137
- "are",
138
- "as",
139
- "at",
140
- "be",
141
- "but",
142
- "by",
143
- "do",
144
- "for",
145
- "from",
146
- "in",
147
- "is",
148
- "it",
149
- "its",
150
- "of",
151
- "on",
152
- "or",
153
- "that",
154
- "the",
155
- "then",
156
- "this",
157
- "to",
158
- "we",
159
- "when",
160
- "with",
161
- "you",
162
- "your",
163
- ]);
164
- /**
165
- * Normalize a user directive for clustering: lowercase, strip filler and
166
- * punctuation, collapse whitespace. Unlike error signatures these are compared
167
- * loosely, so case and symbols are noise.
168
- */
169
- export function normalizeDirective(text) {
170
- let out = text.trim();
171
- // Filler can stack ("ok so please also ..."), so strip until it stops matching.
172
- for (let i = 0; i < 4 && DIRECTIVE_FILLER.test(out); i++) {
173
- out = out.replace(DIRECTIVE_FILLER, "");
174
- }
175
- return out
176
- .toLowerCase()
177
- .replace(/[^\w\s@./+-]/g, " ")
178
- .replace(/\s+/g, " ")
179
- .trim();
180
- }
181
- /** Content words of a directive, for overlap comparisons against existing rules. */
182
- export function contentWords(text) {
183
- return [...new Set(normalizeDirective(text).split(" "))].filter((w) => w.length > 2 && !STOPWORDS.has(w));
184
- }
185
- /**
186
- * Fraction of `words` that appear in `haystack`, 0 when there is nothing to
187
- * compare. Used to decide whether a proposed rule is already written down.
188
- */
189
- export function wordOverlap(words, haystack) {
190
- if (words.length === 0)
191
- return 0;
192
- const hay = ` ${normalizeDirective(haystack)} `;
193
- const hits = words.filter((w) => hay.includes(` ${w} `)).length;
194
- return hits / words.length;
195
- }
196
- /** Approvals and acknowledgements. These are the bulk of user turns and none are rules. */
197
- const ACKNOWLEDGEMENT = /^(?:y|n|ok|okay|yes|no|yep|yeah|nope|sure|thanks|thank you|ty|go|go ahead|continue|proceed|do it|next|done|good|great|nice|perfect|lgtm|cool|k|fine|stop|wait|hmm|\?+|\.+)$/i;
198
- /**
199
- * Imperative or corrective phrasing — the shape a rule takes when it is spoken
200
- * aloud. A directive that matches none of these is almost always a task
201
- * ("add a button here"), which is worth doing once and never worth writing down.
202
- */
203
- const RULE_SHAPED = /\b(?:always|never|don'?t|do not|avoid|make sure|ensure|must|should|prefer|instead|remember|from now on|going forward|every time|each time|whenever|stop|use|run|keep|only|no need to)\b/i;
204
- /** Minimum length before a directive is worth considering at all. */
205
- const MIN_DIRECTIVE_CHARS = 12;
206
- /** Above this a "directive" is a task description or a pasted spec, not a rule. */
207
- const MAX_DIRECTIVE_CHARS = 400;
208
- /**
209
- * Whether a user message is worth mining as a potential rule.
210
- *
211
- * Deliberately strict. The extractor's output is read by a model with a limited
212
- * budget, and recall costs precision: every task statement that slips through
213
- * displaces a real recurring directive in the ranking.
214
- */
215
- export function isRuleShapedDirective(text) {
216
- const trimmed = text.trim();
217
- if (trimmed.length < MIN_DIRECTIVE_CHARS || trimmed.length > MAX_DIRECTIVE_CHARS)
218
- return false;
219
- if (ACKNOWLEDGEMENT.test(trimmed))
220
- return false;
221
- // Slash commands are invocations, not instructions.
222
- if (trimmed.startsWith("/"))
223
- return false;
224
- // Pasted output, diffs and stack traces are not directives.
225
- if (/^(?:```|diff --git|\s*at\s|\{|\[)/.test(trimmed))
226
- return false;
227
- if (trimmed.split("\n").length > 6)
228
- return false;
229
- return RULE_SHAPED.test(trimmed);
230
- }
231
- /**
232
- * Commands whose non-zero exit is a normal answer rather than a failure.
233
- *
234
- * `rg`/`grep` exit 1 on "no match", `test`/`diff`/`cmp` exit non-zero to report
235
- * their result, and a TDD loop is *supposed* to go red. Without this filter the
236
- * fail→fix extractor is mostly noise, since these are among the most-run
237
- * commands in any session.
238
- */
239
- const BENIGN_EXIT_COMMANDS = /^(?:rg|grep|egrep|fgrep|ag|ack|test|\[|diff|cmp|which|command|type|hash|find|fd)\b/;
240
- /** Whether a non-zero exit from this command should be ignored by the fix extractor. */
241
- export function isBenignFailure(command) {
242
- const first = command.trim().replace(/^(?:sudo|env|time|nice)\s+/, "");
243
- return BENIGN_EXIT_COMMANDS.test(first);
244
- }
245
- //# sourceMappingURL=normalize.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"normalize.js","sourceRoot":"","sources":["../../../src/core/learn/normalize.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Normalization and noise filters for the `/learn` extractor.\n *\n * Everything downstream — clustering, dedupe, cross-session counting — is only\n * as good as what happens here. Two runs of the same failing command differ by\n * absolute paths, line numbers, PIDs and timings; two statements of the same\n * rule differ by politeness and word order. Collapsing that variance is what\n * turns a pile of transcripts into counts, and the counts are the product.\n *\n * These are deliberately lexical and cheap. Semantic grouping is left to the\n * model, which sees the deduped candidates and can tell that \"run tests with\n * coverage\" and \"use --coverage on the suite\" are one rule. Trying to do that\n * here would cost a dependency and still be worse.\n */\n\n/** Longest text we normalize; error dumps can run to megabytes. */\nconst MAX_SIGNATURE_CHARS = 400;\n\n/**\n * Volatile fragments that make two runs of the same failure look different.\n *\n * Order is load-bearing, because these patterns overlap and the first one to\n * match wins the characters. Longest-and-most-specific therefore runs first:\n * paths before line numbers so `foo.ts:12:3` does not leave a bare `:12:3`, and\n * timestamps before line numbers so the `:00:00` inside `10:00:00` is not\n * mistaken for a line/column pair. Adding a pattern means placing it by\n * specificity, not appending it.\n */\nconst VOLATILE_PATTERNS: Array<[RegExp, string]> = [\n\t// Absolute POSIX and Windows paths, including the temp dirs that change per run.\n\t[/\\/(?:[\\w.@+-]+\\/)+[\\w.@+-]+/g, \"<path>\"],\n\t[/[A-Za-z]:\\\\(?:[\\w.@+-]+\\\\)+[\\w.@+-]+/g, \"<path>\"],\n\t// UUIDs, before the shorter hex rule can eat their first group.\n\t[/\\b[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\\b/gi, \"<uuid>\"],\n\t// ISO timestamps and clock times, before the line/column rule.\n\t[/\\d{4}-\\d{2}-\\d{2}[T ]\\d{2}:\\d{2}:\\d{2}(?:\\.\\d+)?Z?/g, \"<time>\"],\n\t[/\\b\\d{1,2}:\\d{2}:\\d{2}\\b/g, \"<time>\"],\n\t// Ports, before the line/column rule can claim the colon.\n\t[/\\b(?:127\\.0\\.0\\.1|localhost):\\d+/gi, \"localhost:<port>\"],\n\t// file:line:col and bare line references.\n\t[/:\\d+:\\d+/g, \":<line>\"],\n\t[/\\bline \\d+/gi, \"line <line>\"],\n\t// Hex-ish identifiers: git shas, content hashes, addresses.\n\t[/\\b0x[0-9a-f]+\\b/gi, \"<addr>\"],\n\t[/\\b[0-9a-f]{7,40}\\b/gi, \"<hash>\"],\n\t// Durations and byte counts reported by build tools.\n\t[/\\b\\d+(?:\\.\\d+)?\\s?(?:ms|s|sec|secs|seconds|m|min|mins|kb|mb|gb)\\b/gi, \"<qty>\"],\n\t// PIDs, which vary per machine and per run.\n\t[/\\bpid[= ]\\d+/gi, \"pid <pid>\"],\n\t// Anything left that is a bare multi-digit run.\n\t[/\\b\\d{3,}\\b/g, \"<n>\"],\n];\n\n/**\n * Collapse a raw error or command output into a comparable signature.\n *\n * Case is preserved: `TS2307` and `ERR_MODULE_NOT_FOUND` carry meaning that\n * lowercasing would blur into surrounding prose, and they are exactly the\n * tokens worth matching on.\n */\nexport function normalizeErrorSignature(text: string): string {\n\tlet out = text.slice(0, MAX_SIGNATURE_CHARS * 4);\n\tfor (const [pattern, replacement] of VOLATILE_PATTERNS) {\n\t\tout = out.replace(pattern, replacement);\n\t}\n\treturn out.replace(/\\s+/g, \" \").trim().slice(0, MAX_SIGNATURE_CHARS);\n}\n\n/**\n * Normalize a shell command so two invocations of \"the same\" command match.\n *\n * Paths are collapsed but flags are kept — `npm test` and `npm test --coverage`\n * are genuinely different commands, and flags are frequently the fix itself.\n */\nexport function normalizeCommand(command: string): string {\n\tconst collapsed = command\n\t\t.replace(/\\s+/g, \" \")\n\t\t.replace(/(?:^|\\s)\\/(?:[\\w.@+-]+\\/)+[\\w.@+-]+/g, \" <path>\")\n\t\t.trim();\n\treturn collapsed.slice(0, MAX_SIGNATURE_CHARS);\n}\n\n/**\n * Prefixes that say where or how a command runs rather than what it does.\n *\n * Nearly every command an agent runs is wrapped in `cd <somewhere> && …`, and\n * once paths are collapsed that prefix is identical everywhere. Taking the head\n * of the raw command therefore yields `cd <path>` for the whole session, which\n * makes every bash step look like every other one.\n */\nconst COMMAND_PREFIX = /^(?:cd\\s+\\S+\\s*&&\\s*|(?:sudo|env|time|nice|exec)\\s+)/;\n\n/**\n * The first `tokens` words of what a command actually does, with the\n * where-and-how prefixes stripped. `cd <path> && npm run check` becomes\n * `npm run`, which is the part worth comparing across sessions.\n */\nexport function commandHead(command: string, tokens = 2): string {\n\tlet out = normalizeCommand(command);\n\t// Prefixes stack: `cd x && sudo env FOO=1 cmd`.\n\tfor (let i = 0; i < 4 && COMMAND_PREFIX.test(out); i++) {\n\t\tout = out.replace(COMMAND_PREFIX, \"\");\n\t}\n\treturn out.split(\" \").slice(0, tokens).join(\" \").trim();\n}\n\n/**\n * The part of a command's output that is actually the error.\n *\n * Build tools lead with a banner — npm's `> pkg@1.0 check > biome check …`, a\n * compiler's version line — that is byte-identical across every run. Signing a\n * failure by its whole output therefore signs mostly the banner, and two\n * unrelated failures of the same command look like the same problem. Scanning\n * for the first error-shaped line fixes both the signature and what the reader\n * is shown; failing that, the tail beats the head, because tools print the\n * banner first and the verdict last.\n */\nconst ERROR_LINE =\n\t/\\b(?:error|err!|errors?:|failed|failure|cannot|can't|unable to|not found|no such|unexpected|expected|invalid|denied|refused|timed out|traceback|panic|exception|fatal)\\b|^\\s*(?:×|✖|✗)/i;\n\nexport function extractErrorRegion(output: string, maxChars = 400): string {\n\tconst lines = output.split(\"\\n\");\n\tconst start = lines.findIndex((line) => ERROR_LINE.test(line));\n\tif (start >= 0) {\n\t\treturn lines.slice(start).join(\"\\n\").slice(0, maxChars).trim();\n\t}\n\treturn output.slice(-maxChars).trim();\n}\n\n/**\n * Output that reports no failure worth learning from: an abort is the user\n * changing their mind, and an empty result says nothing at all.\n */\nexport function isUninformativeFailure(output: string): boolean {\n\tconst trimmed = output.trim();\n\tif (trimmed.length === 0) return true;\n\treturn /^(?:command aborted|aborted|cancelled|canceled|interrupted|killed|sigint|sigterm)\\b/i.test(trimmed);\n}\n\n/** Filler that carries no rule content, stripped before directives are compared. */\nconst DIRECTIVE_FILLER =\n\t/^(?:please|pls|hey|ok|okay|now|also|and|so|just|quickly|can you|could you|would you|i want you to|i'd like you to|let's|lets)\\b[\\s,]*/i;\n\n/** Words too common to distinguish one directive from another. */\nconst STOPWORDS = new Set([\n\t\"a\",\n\t\"an\",\n\t\"and\",\n\t\"are\",\n\t\"as\",\n\t\"at\",\n\t\"be\",\n\t\"but\",\n\t\"by\",\n\t\"do\",\n\t\"for\",\n\t\"from\",\n\t\"in\",\n\t\"is\",\n\t\"it\",\n\t\"its\",\n\t\"of\",\n\t\"on\",\n\t\"or\",\n\t\"that\",\n\t\"the\",\n\t\"then\",\n\t\"this\",\n\t\"to\",\n\t\"we\",\n\t\"when\",\n\t\"with\",\n\t\"you\",\n\t\"your\",\n]);\n\n/**\n * Normalize a user directive for clustering: lowercase, strip filler and\n * punctuation, collapse whitespace. Unlike error signatures these are compared\n * loosely, so case and symbols are noise.\n */\nexport function normalizeDirective(text: string): string {\n\tlet out = text.trim();\n\t// Filler can stack (\"ok so please also ...\"), so strip until it stops matching.\n\tfor (let i = 0; i < 4 && DIRECTIVE_FILLER.test(out); i++) {\n\t\tout = out.replace(DIRECTIVE_FILLER, \"\");\n\t}\n\treturn out\n\t\t.toLowerCase()\n\t\t.replace(/[^\\w\\s@./+-]/g, \" \")\n\t\t.replace(/\\s+/g, \" \")\n\t\t.trim();\n}\n\n/** Content words of a directive, for overlap comparisons against existing rules. */\nexport function contentWords(text: string): string[] {\n\treturn [...new Set(normalizeDirective(text).split(\" \"))].filter((w) => w.length > 2 && !STOPWORDS.has(w));\n}\n\n/**\n * Fraction of `words` that appear in `haystack`, 0 when there is nothing to\n * compare. Used to decide whether a proposed rule is already written down.\n */\nexport function wordOverlap(words: string[], haystack: string): number {\n\tif (words.length === 0) return 0;\n\tconst hay = ` ${normalizeDirective(haystack)} `;\n\tconst hits = words.filter((w) => hay.includes(` ${w} `)).length;\n\treturn hits / words.length;\n}\n\n/** Approvals and acknowledgements. These are the bulk of user turns and none are rules. */\nconst ACKNOWLEDGEMENT =\n\t/^(?:y|n|ok|okay|yes|no|yep|yeah|nope|sure|thanks|thank you|ty|go|go ahead|continue|proceed|do it|next|done|good|great|nice|perfect|lgtm|cool|k|fine|stop|wait|hmm|\\?+|\\.+)$/i;\n\n/**\n * Imperative or corrective phrasing — the shape a rule takes when it is spoken\n * aloud. A directive that matches none of these is almost always a task\n * (\"add a button here\"), which is worth doing once and never worth writing down.\n */\nconst RULE_SHAPED =\n\t/\\b(?:always|never|don'?t|do not|avoid|make sure|ensure|must|should|prefer|instead|remember|from now on|going forward|every time|each time|whenever|stop|use|run|keep|only|no need to)\\b/i;\n\n/** Minimum length before a directive is worth considering at all. */\nconst MIN_DIRECTIVE_CHARS = 12;\n/** Above this a \"directive\" is a task description or a pasted spec, not a rule. */\nconst MAX_DIRECTIVE_CHARS = 400;\n\n/**\n * Whether a user message is worth mining as a potential rule.\n *\n * Deliberately strict. The extractor's output is read by a model with a limited\n * budget, and recall costs precision: every task statement that slips through\n * displaces a real recurring directive in the ranking.\n */\nexport function isRuleShapedDirective(text: string): boolean {\n\tconst trimmed = text.trim();\n\tif (trimmed.length < MIN_DIRECTIVE_CHARS || trimmed.length > MAX_DIRECTIVE_CHARS) return false;\n\tif (ACKNOWLEDGEMENT.test(trimmed)) return false;\n\t// Slash commands are invocations, not instructions.\n\tif (trimmed.startsWith(\"/\")) return false;\n\t// Pasted output, diffs and stack traces are not directives.\n\tif (/^(?:```|diff --git|\\s*at\\s|\\{|\\[)/.test(trimmed)) return false;\n\tif (trimmed.split(\"\\n\").length > 6) return false;\n\treturn RULE_SHAPED.test(trimmed);\n}\n\n/**\n * Commands whose non-zero exit is a normal answer rather than a failure.\n *\n * `rg`/`grep` exit 1 on \"no match\", `test`/`diff`/`cmp` exit non-zero to report\n * their result, and a TDD loop is *supposed* to go red. Without this filter the\n * fail→fix extractor is mostly noise, since these are among the most-run\n * commands in any session.\n */\nconst BENIGN_EXIT_COMMANDS = /^(?:rg|grep|egrep|fgrep|ag|ack|test|\\[|diff|cmp|which|command|type|hash|find|fd)\\b/;\n\n/** Whether a non-zero exit from this command should be ignored by the fix extractor. */\nexport function isBenignFailure(command: string): boolean {\n\tconst first = command.trim().replace(/^(?:sudo|env|time|nice)\\s+/, \"\");\n\treturn BENIGN_EXIT_COMMANDS.test(first);\n}\n"]}