@kolisachint/hoocode-agent 0.5.16 → 0.5.18
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/CHANGELOG.md +79 -0
- package/dist/core/learn/cache.d.ts +58 -0
- package/dist/core/learn/cache.d.ts.map +1 -0
- package/dist/core/learn/cache.js +120 -0
- package/dist/core/learn/cache.js.map +1 -0
- package/dist/core/learn/coverage.d.ts +58 -0
- package/dist/core/learn/coverage.d.ts.map +1 -0
- package/dist/core/learn/coverage.js +142 -0
- package/dist/core/learn/coverage.js.map +1 -0
- package/dist/core/learn/digest.d.ts +1 -0
- package/dist/core/learn/digest.d.ts.map +1 -1
- package/dist/core/learn/digest.js +31 -4
- package/dist/core/learn/digest.js.map +1 -1
- package/dist/core/learn/extract.d.ts +71 -103
- package/dist/core/learn/extract.d.ts.map +1 -1
- package/dist/core/learn/extract.js +162 -437
- package/dist/core/learn/extract.js.map +1 -1
- package/dist/core/learn/mine.d.ts +123 -0
- package/dist/core/learn/mine.d.ts.map +1 -0
- package/dist/core/learn/mine.js +285 -0
- package/dist/core/learn/mine.js.map +1 -0
- package/dist/core/learn/reduce.d.ts +78 -0
- package/dist/core/learn/reduce.d.ts.map +1 -0
- package/dist/core/learn/reduce.js +123 -0
- package/dist/core/learn/reduce.js.map +1 -0
- package/dist/core/learn/state.d.ts +8 -0
- package/dist/core/learn/state.d.ts.map +1 -1
- package/dist/core/learn/state.js +18 -3
- package/dist/core/learn/state.js.map +1 -1
- package/dist/core/settings-manager.d.ts +2 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +4 -0
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/startup-progress.d.ts +12 -7
- package/dist/core/startup-progress.d.ts.map +1 -1
- package/dist/core/startup-progress.js +12 -7
- package/dist/core/startup-progress.js.map +1 -1
- package/dist/extensions/core/learn.d.ts +8 -4
- package/dist/extensions/core/learn.d.ts.map +1 -1
- package/dist/extensions/core/learn.js +208 -27
- package/dist/extensions/core/learn.js.map +1 -1
- package/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/dist/modes/interactive/components/footer.js +7 -25
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/dist/modes/interactive/components/progress-bar.d.ts +50 -0
- package/dist/modes/interactive/components/progress-bar.d.ts.map +1 -0
- package/dist/modes/interactive/components/progress-bar.js +77 -0
- package/dist/modes/interactive/components/progress-bar.js.map +1 -0
- package/dist/modes/interactive/voice/voice-panel.d.ts +6 -1
- package/dist/modes/interactive/voice/voice-panel.d.ts.map +1 -1
- package/dist/modes/interactive/voice/voice-panel.js +18 -14
- package/dist/modes/interactive/voice/voice-panel.js.map +1 -1
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package.json +1 -1
- package/package.json +4 -4
- package/dist/core/learn/normalize.d.ts +0 -65
- package/dist/core/learn/normalize.d.ts.map +0 -1
- package/dist/core/learn/normalize.js +0 -245
- package/dist/core/learn/normalize.js.map +0 -1
|
@@ -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"]}
|