@kolisachint/hoocode-agent 0.4.110 → 0.4.112

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 (108) hide show
  1. package/CHANGELOG.md +107 -0
  2. package/dist/cli/args.d.ts +1 -0
  3. package/dist/cli/args.d.ts.map +1 -1
  4. package/dist/cli/args.js +8 -3
  5. package/dist/cli/args.js.map +1 -1
  6. package/dist/core/extensions/loader.d.ts +10 -1
  7. package/dist/core/extensions/loader.d.ts.map +1 -1
  8. package/dist/core/extensions/loader.js +17 -2
  9. package/dist/core/extensions/loader.js.map +1 -1
  10. package/dist/core/extensions/plugins/marketplace.d.ts +36 -5
  11. package/dist/core/extensions/plugins/marketplace.d.ts.map +1 -1
  12. package/dist/core/extensions/plugins/marketplace.js +63 -17
  13. package/dist/core/extensions/plugins/marketplace.js.map +1 -1
  14. package/dist/core/format-duration.d.ts +7 -0
  15. package/dist/core/format-duration.d.ts.map +1 -0
  16. package/dist/core/format-duration.js +16 -0
  17. package/dist/core/format-duration.js.map +1 -0
  18. package/dist/core/keybindings.d.ts +10 -0
  19. package/dist/core/keybindings.d.ts.map +1 -1
  20. package/dist/core/keybindings.js +10 -0
  21. package/dist/core/keybindings.js.map +1 -1
  22. package/dist/core/lifeguard.d.ts +15 -0
  23. package/dist/core/lifeguard.d.ts.map +1 -1
  24. package/dist/core/lifeguard.js +48 -18
  25. package/dist/core/lifeguard.js.map +1 -1
  26. package/dist/core/scheduler.d.ts +8 -2
  27. package/dist/core/scheduler.d.ts.map +1 -1
  28. package/dist/core/scheduler.js +9 -3
  29. package/dist/core/scheduler.js.map +1 -1
  30. package/dist/core/settings-defaults.d.ts +1 -1
  31. package/dist/core/settings-defaults.d.ts.map +1 -1
  32. package/dist/core/settings-defaults.js +2 -2
  33. package/dist/core/settings-defaults.js.map +1 -1
  34. package/dist/core/settings-types.d.ts.map +1 -1
  35. package/dist/core/settings-types.js.map +1 -1
  36. package/dist/core/subagent-inbox.d.ts +1 -1
  37. package/dist/core/subagent-inbox.d.ts.map +1 -1
  38. package/dist/core/subagent-inbox.js +2 -0
  39. package/dist/core/subagent-inbox.js.map +1 -1
  40. package/dist/core/subagent-pool-instance.d.ts.map +1 -1
  41. package/dist/core/subagent-pool-instance.js +18 -11
  42. package/dist/core/subagent-pool-instance.js.map +1 -1
  43. package/dist/core/subagent-pool.d.ts +25 -3
  44. package/dist/core/subagent-pool.d.ts.map +1 -1
  45. package/dist/core/subagent-pool.js +120 -25
  46. package/dist/core/subagent-pool.js.map +1 -1
  47. package/dist/core/subagent-result.d.ts.map +1 -1
  48. package/dist/core/subagent-result.js +6 -4
  49. package/dist/core/subagent-result.js.map +1 -1
  50. package/dist/core/task-store.d.ts +45 -3
  51. package/dist/core/task-store.d.ts.map +1 -1
  52. package/dist/core/task-store.js +51 -5
  53. package/dist/core/task-store.js.map +1 -1
  54. package/dist/core/team-view.d.ts.map +1 -1
  55. package/dist/core/team-view.js +2 -0
  56. package/dist/core/team-view.js.map +1 -1
  57. package/dist/core/token-budget.d.ts +13 -1
  58. package/dist/core/token-budget.d.ts.map +1 -1
  59. package/dist/core/token-budget.js +18 -1
  60. package/dist/core/token-budget.js.map +1 -1
  61. package/dist/core/tools/subagent.d.ts +16 -4
  62. package/dist/core/tools/subagent.d.ts.map +1 -1
  63. package/dist/core/tools/subagent.js +211 -67
  64. package/dist/core/tools/subagent.js.map +1 -1
  65. package/dist/core/tools/todo.d.ts.map +1 -1
  66. package/dist/core/tools/todo.js +39 -7
  67. package/dist/core/tools/todo.js.map +1 -1
  68. package/dist/extensions/core/loop.d.ts.map +1 -1
  69. package/dist/extensions/core/loop.js +4 -1
  70. package/dist/extensions/core/loop.js.map +1 -1
  71. package/dist/extensions/core/marketplace.d.ts +5 -3
  72. package/dist/extensions/core/marketplace.d.ts.map +1 -1
  73. package/dist/extensions/core/marketplace.js +37 -15
  74. package/dist/extensions/core/marketplace.js.map +1 -1
  75. package/dist/modes/interactive/components/keybinding-hints.d.ts +10 -0
  76. package/dist/modes/interactive/components/keybinding-hints.d.ts.map +1 -1
  77. package/dist/modes/interactive/components/keybinding-hints.js +27 -1
  78. package/dist/modes/interactive/components/keybinding-hints.js.map +1 -1
  79. package/dist/modes/interactive/components/task-panel.d.ts +22 -1
  80. package/dist/modes/interactive/components/task-panel.d.ts.map +1 -1
  81. package/dist/modes/interactive/components/task-panel.js +302 -80
  82. package/dist/modes/interactive/components/task-panel.js.map +1 -1
  83. package/dist/modes/interactive/components/team-attach-panel.d.ts.map +1 -1
  84. package/dist/modes/interactive/components/team-attach-panel.js +15 -5
  85. package/dist/modes/interactive/components/team-attach-panel.js.map +1 -1
  86. package/dist/modes/interactive/theme/dark.json +8 -1
  87. package/dist/modes/interactive/theme/light.json +8 -1
  88. package/dist/modes/interactive/theme/theme-schema.json +28 -1
  89. package/dist/modes/interactive/theme/theme.d.ts +12 -1
  90. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  91. package/dist/modes/interactive/theme/theme.js +52 -0
  92. package/dist/modes/interactive/theme/theme.js.map +1 -1
  93. package/dist/modes/interactive/voice/voice-panel.d.ts.map +1 -1
  94. package/dist/modes/interactive/voice/voice-panel.js +5 -2
  95. package/dist/modes/interactive/voice/voice-panel.js.map +1 -1
  96. package/dist/modes/rpc/jsonl.d.ts +16 -1
  97. package/dist/modes/rpc/jsonl.d.ts.map +1 -1
  98. package/dist/modes/rpc/jsonl.js +53 -8
  99. package/dist/modes/rpc/jsonl.js.map +1 -1
  100. package/dist/utils/atomic-file.d.ts +13 -0
  101. package/dist/utils/atomic-file.d.ts.map +1 -0
  102. package/dist/utils/atomic-file.js +26 -0
  103. package/dist/utils/atomic-file.js.map +1 -0
  104. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  105. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  106. package/examples/extensions/sandbox/package.json +1 -1
  107. package/examples/extensions/with-deps/package.json +1 -1
  108. package/package.json +4 -4
@@ -14,10 +14,20 @@ export function serializeJsonLine(value) {
14
14
  * This intentionally does not use Node readline. Readline splits on additional
15
15
  * Unicode separators that are valid inside JSON strings and therefore does not
16
16
  * implement strict JSONL framing.
17
+ *
18
+ * The reader detaches itself on stream `error` (which would otherwise crash the
19
+ * process as an unhandled 'error' event) and flushes + detaches on `close`, so
20
+ * a child that dies mid-line cannot leak listeners or buffered data. The
21
+ * returned function detaches manually.
17
22
  */
18
- export function attachJsonlLineReader(stream, onLine) {
23
+ export function attachJsonlLineReader(stream, onLine, options = {}) {
19
24
  const decoder = new StringDecoder("utf8");
25
+ const maxBuffer = options.maxBuffer;
20
26
  let buffer = "";
27
+ /** True while skipping the remainder of a line that overflowed maxBuffer. */
28
+ let discarding = false;
29
+ let flushed = false;
30
+ let detached = false;
21
31
  const emitLine = (line) => {
22
32
  onLine(line.endsWith("\r") ? line.slice(0, -1) : line);
23
33
  };
@@ -26,24 +36,59 @@ export function attachJsonlLineReader(stream, onLine) {
26
36
  while (true) {
27
37
  const newlineIndex = buffer.indexOf("\n");
28
38
  if (newlineIndex === -1) {
39
+ if (maxBuffer !== undefined && buffer.length > maxBuffer) {
40
+ buffer = "";
41
+ discarding = true;
42
+ }
29
43
  return;
30
44
  }
31
- emitLine(buffer.slice(0, newlineIndex));
45
+ const line = buffer.slice(0, newlineIndex);
32
46
  buffer = buffer.slice(newlineIndex + 1);
47
+ if (discarding) {
48
+ // The extracted piece is the tail of an oversized, already-dropped line.
49
+ discarding = false;
50
+ continue;
51
+ }
52
+ emitLine(line);
33
53
  }
34
54
  };
35
- const onEnd = () => {
55
+ const flush = () => {
56
+ if (flushed)
57
+ return;
58
+ flushed = true;
36
59
  buffer += decoder.end();
37
- if (buffer.length > 0) {
60
+ if (buffer.length > 0 && !discarding) {
38
61
  emitLine(buffer);
39
- buffer = "";
40
62
  }
63
+ buffer = "";
41
64
  };
42
- stream.on("data", onData);
43
- stream.on("end", onEnd);
44
- return () => {
65
+ const detach = () => {
66
+ if (detached)
67
+ return;
68
+ detached = true;
45
69
  stream.off("data", onData);
46
70
  stream.off("end", onEnd);
71
+ stream.off("close", onClose);
72
+ stream.off("error", onError);
73
+ buffer = "";
47
74
  };
75
+ const onEnd = () => {
76
+ flush();
77
+ };
78
+ const onClose = () => {
79
+ flush();
80
+ detach();
81
+ };
82
+ const onError = () => {
83
+ // The stream is dead; drop partial data and release the listeners. Having
84
+ // this handler also keeps a stream 'error' from becoming an uncaught
85
+ // exception in the parent.
86
+ detach();
87
+ };
88
+ stream.on("data", onData);
89
+ stream.on("end", onEnd);
90
+ stream.on("close", onClose);
91
+ stream.on("error", onError);
92
+ return detach;
48
93
  }
49
94
  //# sourceMappingURL=jsonl.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"jsonl.js","sourceRoot":"","sources":["../../../src/modes/rpc/jsonl.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEpD;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAAC,KAAc,EAAU;IACzD,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC;AAAA,CACpC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,qBAAqB,CAAC,MAAgB,EAAE,MAA8B,EAAc;IACnG,MAAM,OAAO,GAAG,IAAI,aAAa,CAAC,MAAM,CAAC,CAAC;IAC1C,IAAI,MAAM,GAAG,EAAE,CAAC;IAEhB,MAAM,QAAQ,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC;QAClC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IAAA,CACvD,CAAC;IAEF,MAAM,MAAM,GAAG,CAAC,KAAsB,EAAE,EAAE,CAAC;QAC1C,MAAM,IAAI,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAEnE,OAAO,IAAI,EAAE,CAAC;YACb,MAAM,YAAY,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;YAC1C,IAAI,YAAY,KAAK,CAAC,CAAC,EAAE,CAAC;gBACzB,OAAO;YACR,CAAC;YAED,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,YAAY,CAAC,CAAC,CAAC;YACxC,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,YAAY,GAAG,CAAC,CAAC,CAAC;QACzC,CAAC;IAAA,CACD,CAAC;IAEF,MAAM,KAAK,GAAG,GAAG,EAAE,CAAC;QACnB,MAAM,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;QACxB,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACvB,QAAQ,CAAC,MAAM,CAAC,CAAC;YACjB,MAAM,GAAG,EAAE,CAAC;QACb,CAAC;IAAA,CACD,CAAC;IAEF,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1B,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IAExB,OAAO,GAAG,EAAE,CAAC;QACZ,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAC3B,MAAM,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IAAA,CACzB,CAAC;AAAA,CACF","sourcesContent":["import type { Readable } from \"node:stream\";\nimport { StringDecoder } from \"node:string_decoder\";\n\n/**\n * Serialize a single strict JSONL record.\n *\n * Framing is LF-only. Payload strings may contain other Unicode separators such as\n * U+2028 and U+2029. Clients must split records on `\\n` only.\n */\nexport function serializeJsonLine(value: unknown): string {\n\treturn `${JSON.stringify(value)}\\n`;\n}\n\n/**\n * Attach an LF-only JSONL reader to a stream.\n *\n * This intentionally does not use Node readline. Readline splits on additional\n * Unicode separators that are valid inside JSON strings and therefore does not\n * implement strict JSONL framing.\n */\nexport function attachJsonlLineReader(stream: Readable, onLine: (line: string) => void): () => void {\n\tconst decoder = new StringDecoder(\"utf8\");\n\tlet buffer = \"\";\n\n\tconst emitLine = (line: string) => {\n\t\tonLine(line.endsWith(\"\\r\") ? line.slice(0, -1) : line);\n\t};\n\n\tconst onData = (chunk: string | Buffer) => {\n\t\tbuffer += typeof chunk === \"string\" ? chunk : decoder.write(chunk);\n\n\t\twhile (true) {\n\t\t\tconst newlineIndex = buffer.indexOf(\"\\n\");\n\t\t\tif (newlineIndex === -1) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\temitLine(buffer.slice(0, newlineIndex));\n\t\t\tbuffer = buffer.slice(newlineIndex + 1);\n\t\t}\n\t};\n\n\tconst onEnd = () => {\n\t\tbuffer += decoder.end();\n\t\tif (buffer.length > 0) {\n\t\t\temitLine(buffer);\n\t\t\tbuffer = \"\";\n\t\t}\n\t};\n\n\tstream.on(\"data\", onData);\n\tstream.on(\"end\", onEnd);\n\n\treturn () => {\n\t\tstream.off(\"data\", onData);\n\t\tstream.off(\"end\", onEnd);\n\t};\n}\n"]}
1
+ {"version":3,"file":"jsonl.js","sourceRoot":"","sources":["../../../src/modes/rpc/jsonl.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Readable } from \"node:stream\";\nimport { StringDecoder } from \"node:string_decoder\";\n\n/**\n * Serialize a single strict JSONL record.\n *\n * Framing is LF-only. Payload strings may contain other Unicode separators such as\n * U+2028 and U+2029. Clients must split records on `\\n` only.\n */\nexport function serializeJsonLine(value: unknown): string {\n\treturn `${JSON.stringify(value)}\\n`;\n}\n\nexport interface JsonlLineReaderOptions {\n\t/**\n\t * Cap (in UTF-16 code units) on the un-terminated line buffer. A line that\n\t * grows past this without a newline is dropped in its entirety (the rest of\n\t * it is skipped up to the next newline) instead of being buffered without\n\t * bound — a misbehaving writer must not be able to OOM the reader. Omit for\n\t * an unbounded buffer (trusted peers, e.g. the RPC channel).\n\t */\n\tmaxBuffer?: number;\n}\n\n/**\n * Attach an LF-only JSONL reader to a stream.\n *\n * This intentionally does not use Node readline. Readline splits on additional\n * Unicode separators that are valid inside JSON strings and therefore does not\n * implement strict JSONL framing.\n *\n * The reader detaches itself on stream `error` (which would otherwise crash the\n * process as an unhandled 'error' event) and flushes + detaches on `close`, so\n * a child that dies mid-line cannot leak listeners or buffered data. The\n * returned function detaches manually.\n */\nexport function attachJsonlLineReader(\n\tstream: Readable,\n\tonLine: (line: string) => void,\n\toptions: JsonlLineReaderOptions = {},\n): () => void {\n\tconst decoder = new StringDecoder(\"utf8\");\n\tconst maxBuffer = options.maxBuffer;\n\tlet buffer = \"\";\n\t/** True while skipping the remainder of a line that overflowed maxBuffer. */\n\tlet discarding = false;\n\tlet flushed = false;\n\tlet detached = false;\n\n\tconst emitLine = (line: string) => {\n\t\tonLine(line.endsWith(\"\\r\") ? line.slice(0, -1) : line);\n\t};\n\n\tconst onData = (chunk: string | Buffer) => {\n\t\tbuffer += typeof chunk === \"string\" ? chunk : decoder.write(chunk);\n\n\t\twhile (true) {\n\t\t\tconst newlineIndex = buffer.indexOf(\"\\n\");\n\t\t\tif (newlineIndex === -1) {\n\t\t\t\tif (maxBuffer !== undefined && buffer.length > maxBuffer) {\n\t\t\t\t\tbuffer = \"\";\n\t\t\t\t\tdiscarding = true;\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst line = buffer.slice(0, newlineIndex);\n\t\t\tbuffer = buffer.slice(newlineIndex + 1);\n\t\t\tif (discarding) {\n\t\t\t\t// The extracted piece is the tail of an oversized, already-dropped line.\n\t\t\t\tdiscarding = false;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\temitLine(line);\n\t\t}\n\t};\n\n\tconst flush = () => {\n\t\tif (flushed) return;\n\t\tflushed = true;\n\t\tbuffer += decoder.end();\n\t\tif (buffer.length > 0 && !discarding) {\n\t\t\temitLine(buffer);\n\t\t}\n\t\tbuffer = \"\";\n\t};\n\n\tconst detach = () => {\n\t\tif (detached) return;\n\t\tdetached = true;\n\t\tstream.off(\"data\", onData);\n\t\tstream.off(\"end\", onEnd);\n\t\tstream.off(\"close\", onClose);\n\t\tstream.off(\"error\", onError);\n\t\tbuffer = \"\";\n\t};\n\n\tconst onEnd = () => {\n\t\tflush();\n\t};\n\n\tconst onClose = () => {\n\t\tflush();\n\t\tdetach();\n\t};\n\n\tconst onError = () => {\n\t\t// The stream is dead; drop partial data and release the listeners. Having\n\t\t// this handler also keeps a stream 'error' from becoming an uncaught\n\t\t// exception in the parent.\n\t\tdetach();\n\t};\n\n\tstream.on(\"data\", onData);\n\tstream.on(\"end\", onEnd);\n\tstream.on(\"close\", onClose);\n\tstream.on(\"error\", onError);\n\n\treturn detach;\n}\n"]}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Write a file atomically: write to a unique temp file in the same directory,
3
+ * then rename over the target. The same-directory temp keeps the rename on one
4
+ * filesystem (rename across mounts is a non-atomic copy), so a reader can never
5
+ * observe a torn, half-written file — it sees either the old content or the
6
+ * new. Used for handshake files like a subagent's result.json, where a reader
7
+ * (the parent pool) may race a writer that gets SIGKILLed mid-write.
8
+ *
9
+ * Throws on failure like writeFileSync; callers that treat persistence as
10
+ * best-effort keep their own try/catch.
11
+ */
12
+ export declare function writeFileAtomicSync(path: string, data: string): void;
13
+ //# sourceMappingURL=atomic-file.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"atomic-file.d.ts","sourceRoot":"","sources":["../../src/utils/atomic-file.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAUpE","sourcesContent":["import { mkdirSync, renameSync, rmSync, writeFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\n\n/**\n * Write a file atomically: write to a unique temp file in the same directory,\n * then rename over the target. The same-directory temp keeps the rename on one\n * filesystem (rename across mounts is a non-atomic copy), so a reader can never\n * observe a torn, half-written file — it sees either the old content or the\n * new. Used for handshake files like a subagent's result.json, where a reader\n * (the parent pool) may race a writer that gets SIGKILLed mid-write.\n *\n * Throws on failure like writeFileSync; callers that treat persistence as\n * best-effort keep their own try/catch.\n */\nexport function writeFileAtomicSync(path: string, data: string): void {\n\tmkdirSync(dirname(path), { recursive: true });\n\tconst tempPath = join(dirname(path), `.${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}.tmp`);\n\ttry {\n\t\twriteFileSync(tempPath, data);\n\t\trenameSync(tempPath, path);\n\t} catch (error) {\n\t\trmSync(tempPath, { force: true });\n\t\tthrow error;\n\t}\n}\n"]}
@@ -0,0 +1,26 @@
1
+ import { mkdirSync, renameSync, rmSync, writeFileSync } from "node:fs";
2
+ import { dirname, join } from "node:path";
3
+ /**
4
+ * Write a file atomically: write to a unique temp file in the same directory,
5
+ * then rename over the target. The same-directory temp keeps the rename on one
6
+ * filesystem (rename across mounts is a non-atomic copy), so a reader can never
7
+ * observe a torn, half-written file — it sees either the old content or the
8
+ * new. Used for handshake files like a subagent's result.json, where a reader
9
+ * (the parent pool) may race a writer that gets SIGKILLed mid-write.
10
+ *
11
+ * Throws on failure like writeFileSync; callers that treat persistence as
12
+ * best-effort keep their own try/catch.
13
+ */
14
+ export function writeFileAtomicSync(path, data) {
15
+ mkdirSync(dirname(path), { recursive: true });
16
+ const tempPath = join(dirname(path), `.${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}.tmp`);
17
+ try {
18
+ writeFileSync(tempPath, data);
19
+ renameSync(tempPath, path);
20
+ }
21
+ catch (error) {
22
+ rmSync(tempPath, { force: true });
23
+ throw error;
24
+ }
25
+ }
26
+ //# sourceMappingURL=atomic-file.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"atomic-file.js","sourceRoot":"","sources":["../../src/utils/atomic-file.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACvE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAE1C;;;;;;;;;;GAUG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAAY,EAAE,IAAY,EAAQ;IACrE,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9C,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC;IAClH,IAAI,CAAC;QACJ,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QAC9B,UAAU,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IAC5B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,MAAM,CAAC,QAAQ,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QAClC,MAAM,KAAK,CAAC;IACb,CAAC;AAAA,CACD","sourcesContent":["import { mkdirSync, renameSync, rmSync, writeFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\n\n/**\n * Write a file atomically: write to a unique temp file in the same directory,\n * then rename over the target. The same-directory temp keeps the rename on one\n * filesystem (rename across mounts is a non-atomic copy), so a reader can never\n * observe a torn, half-written file — it sees either the old content or the\n * new. Used for handshake files like a subagent's result.json, where a reader\n * (the parent pool) may race a writer that gets SIGKILLed mid-write.\n *\n * Throws on failure like writeFileSync; callers that treat persistence as\n * best-effort keep their own try/catch.\n */\nexport function writeFileAtomicSync(path: string, data: string): void {\n\tmkdirSync(dirname(path), { recursive: true });\n\tconst tempPath = join(dirname(path), `.${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}.tmp`);\n\ttry {\n\t\twriteFileSync(tempPath, data);\n\t\trenameSync(tempPath, path);\n\t} catch (error) {\n\t\trmSync(tempPath, { force: true });\n\t\tthrow error;\n\t}\n}\n"]}
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@kolisachint/hoocode-extension-custom-provider-anthropic",
3
3
  "private": true,
4
- "version": "0.2.107",
4
+ "version": "0.2.109",
5
5
  "type": "module",
6
6
  "engines": {
7
7
  "bun": ">=1.0.0"
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@kolisachint/hoocode-extension-custom-provider-gitlab-duo",
3
3
  "private": true,
4
- "version": "0.2.107",
4
+ "version": "0.2.109",
5
5
  "type": "module",
6
6
  "engines": {
7
7
  "bun": ">=1.0.0"
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@kolisachint/hoocode-extension-sandbox",
3
3
  "private": true,
4
- "version": "0.2.107",
4
+ "version": "0.2.109",
5
5
  "type": "module",
6
6
  "engines": {
7
7
  "bun": ">=1.0.0"
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@kolisachint/hoocode-extension-with-deps",
3
3
  "private": true,
4
- "version": "0.2.107",
4
+ "version": "0.2.109",
5
5
  "type": "module",
6
6
  "engines": {
7
7
  "bun": ">=1.0.0"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kolisachint/hoocode-agent",
3
- "version": "0.4.110",
3
+ "version": "0.4.112",
4
4
  "description": "Coding agent CLI with read, bash, edit, write tools and session management",
5
5
  "type": "module",
6
6
  "hoocodeConfig": {
@@ -45,9 +45,9 @@
45
45
  "prepublishOnly": "npm run clean && npm run build"
46
46
  },
47
47
  "dependencies": {
48
- "@kolisachint/hoocode-agent-core": "^0.4.110",
49
- "@kolisachint/hoocode-ai": "^0.4.110",
50
- "@kolisachint/hoocode-tui": "^0.4.110",
48
+ "@kolisachint/hoocode-agent-core": "^0.4.112",
49
+ "@kolisachint/hoocode-ai": "^0.4.112",
50
+ "@kolisachint/hoocode-tui": "^0.4.112",
51
51
  "@silvia-odwyer/photon-node": "^0.3.4",
52
52
  "chalk": "^5.5.0",
53
53
  "cli-highlight": "^2.1.11",