@kolisachint/hoocode-agent 0.4.105 → 0.4.107

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 (289) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/cli/args.d.ts.map +1 -1
  3. package/dist/cli/args.js +4 -4
  4. package/dist/cli/args.js.map +1 -1
  5. package/dist/cli.d.ts.map +1 -1
  6. package/dist/cli.js +0 -1
  7. package/dist/cli.js.map +1 -1
  8. package/dist/config.d.ts.map +1 -1
  9. package/dist/config.js +2 -2
  10. package/dist/config.js.map +1 -1
  11. package/dist/core/agent-session.d.ts +1 -3
  12. package/dist/core/agent-session.d.ts.map +1 -1
  13. package/dist/core/agent-session.js +2 -2
  14. package/dist/core/agent-session.js.map +1 -1
  15. package/dist/core/bash-executor.d.ts.map +1 -1
  16. package/dist/core/bash-executor.js +1 -1
  17. package/dist/core/bash-executor.js.map +1 -1
  18. package/dist/core/extension-mcp-servers.d.ts +1 -1
  19. package/dist/core/extension-mcp-servers.d.ts.map +1 -1
  20. package/dist/core/extension-mcp-servers.js +1 -1
  21. package/dist/core/extension-mcp-servers.js.map +1 -1
  22. package/dist/core/extensions/plugins/index.d.ts.map +1 -1
  23. package/dist/core/extensions/plugins/index.js +1 -1
  24. package/dist/core/extensions/plugins/index.js.map +1 -1
  25. package/dist/core/extensions/types.d.ts +1 -3
  26. package/dist/core/extensions/types.d.ts.map +1 -1
  27. package/dist/core/extensions/types.js.map +1 -1
  28. package/dist/core/index.d.ts +1 -1
  29. package/dist/core/index.d.ts.map +1 -1
  30. package/dist/core/index.js.map +1 -1
  31. package/dist/core/mode-prompts.d.ts +1 -1
  32. package/dist/core/mode-prompts.d.ts.map +1 -1
  33. package/dist/core/mode-prompts.js +1 -1
  34. package/dist/core/mode-prompts.js.map +1 -1
  35. package/dist/core/package-manager.d.ts.map +1 -1
  36. package/dist/core/package-manager.js +1 -1
  37. package/dist/core/package-manager.js.map +1 -1
  38. package/dist/core/sdk.d.ts.map +1 -1
  39. package/dist/core/sdk.js +1 -2
  40. package/dist/core/sdk.js.map +1 -1
  41. package/dist/core/session-manager.d.ts +2 -97
  42. package/dist/core/session-manager.d.ts.map +1 -1
  43. package/dist/core/session-manager.js +4 -65
  44. package/dist/core/session-manager.js.map +1 -1
  45. package/dist/core/settings-manager.d.ts.map +1 -1
  46. package/dist/core/settings-manager.js +2 -3
  47. package/dist/core/settings-manager.js.map +1 -1
  48. package/dist/core/telemetry.d.ts.map +1 -1
  49. package/dist/core/telemetry.js +1 -1
  50. package/dist/core/telemetry.js.map +1 -1
  51. package/dist/core/timings.d.ts +1 -1
  52. package/dist/core/timings.d.ts.map +1 -1
  53. package/dist/core/timings.js +2 -2
  54. package/dist/core/timings.js.map +1 -1
  55. package/dist/core/tools/{browser-continue.d.ts → browser/browser-continue.d.ts} +1 -1
  56. package/dist/core/tools/browser/browser-continue.d.ts.map +1 -0
  57. package/dist/core/tools/{browser-continue.js → browser/browser-continue.js} +2 -2
  58. package/dist/core/tools/browser/browser-continue.js.map +1 -0
  59. package/dist/core/tools/{browser-run.d.ts → browser/browser-run.d.ts} +2 -2
  60. package/dist/core/tools/browser/browser-run.d.ts.map +1 -0
  61. package/dist/core/tools/{browser-run.js → browser/browser-run.js} +2 -2
  62. package/dist/core/tools/browser/browser-run.js.map +1 -0
  63. package/dist/core/tools/browser/browsertools-shared.d.ts.map +1 -0
  64. package/dist/core/tools/{browsertools-shared.js → browser/browsertools-shared.js} +1 -1
  65. package/dist/core/tools/browser/browsertools-shared.js.map +1 -0
  66. package/dist/core/tools/browser/index.d.ts +8 -0
  67. package/dist/core/tools/browser/index.d.ts.map +1 -0
  68. package/dist/core/tools/browser/index.js +8 -0
  69. package/dist/core/tools/browser/index.js.map +1 -0
  70. package/dist/core/tools/{docedit.d.ts → doc/docedit.d.ts} +2 -2
  71. package/dist/core/tools/doc/docedit.d.ts.map +1 -0
  72. package/dist/core/tools/{docedit.js → doc/docedit.js} +7 -7
  73. package/dist/core/tools/doc/docedit.js.map +1 -0
  74. package/dist/core/tools/{docgrep.d.ts → doc/docgrep.d.ts} +2 -2
  75. package/dist/core/tools/doc/docgrep.d.ts.map +1 -0
  76. package/dist/core/tools/{docgrep.js → doc/docgrep.js} +6 -6
  77. package/dist/core/tools/doc/docgrep.js.map +1 -0
  78. package/dist/core/tools/{docpeek.d.ts → doc/docpeek.d.ts} +2 -2
  79. package/dist/core/tools/{docpeek.js.map → doc/docpeek.d.ts.map} +1 -1
  80. package/dist/core/tools/{docpeek.js → doc/docpeek.js} +6 -6
  81. package/dist/core/tools/doc/docpeek.js.map +1 -0
  82. package/dist/core/tools/{docread.d.ts → doc/docread.d.ts} +2 -2
  83. package/dist/core/tools/doc/docread.d.ts.map +1 -0
  84. package/dist/core/tools/{docread.js → doc/docread.js} +6 -6
  85. package/dist/core/tools/doc/docread.js.map +1 -0
  86. package/dist/core/tools/{docscan.d.ts → doc/docscan.d.ts} +2 -2
  87. package/dist/core/tools/doc/docscan.d.ts.map +1 -0
  88. package/dist/core/tools/{docscan.js → doc/docscan.js} +6 -6
  89. package/dist/core/tools/doc/docscan.js.map +1 -0
  90. package/dist/core/tools/{docwrite.d.ts → doc/docwrite.d.ts} +1 -1
  91. package/dist/core/tools/doc/docwrite.d.ts.map +1 -0
  92. package/dist/core/tools/{docwrite.js → doc/docwrite.js} +6 -6
  93. package/dist/core/tools/doc/docwrite.js.map +1 -0
  94. package/dist/core/tools/doc/index.d.ts +11 -0
  95. package/dist/core/tools/doc/index.d.ts.map +1 -0
  96. package/dist/core/tools/doc/index.js +11 -0
  97. package/dist/core/tools/doc/index.js.map +1 -0
  98. package/dist/core/tools/grep.d.ts.map +1 -1
  99. package/dist/core/tools/grep.js +1 -1
  100. package/dist/core/tools/grep.js.map +1 -1
  101. package/dist/core/tools/index.d.ts +42 -28
  102. package/dist/core/tools/index.d.ts.map +1 -1
  103. package/dist/core/tools/index.js +61 -199
  104. package/dist/core/tools/index.js.map +1 -1
  105. package/dist/core/tools/output-accumulator.d.ts.map +1 -1
  106. package/dist/core/tools/output-accumulator.js +1 -1
  107. package/dist/core/tools/output-accumulator.js.map +1 -1
  108. package/dist/core/tools/read.d.ts.map +1 -1
  109. package/dist/core/tools/read.js +1 -1
  110. package/dist/core/tools/read.js.map +1 -1
  111. package/dist/extensions/core/ask-options.d.ts +10 -0
  112. package/dist/extensions/core/ask-options.d.ts.map +1 -0
  113. package/dist/extensions/core/ask-options.js +83 -0
  114. package/dist/extensions/core/ask-options.js.map +1 -0
  115. package/dist/extensions/core/config.d.ts +80 -0
  116. package/dist/extensions/core/config.d.ts.map +1 -0
  117. package/dist/extensions/core/config.js +110 -0
  118. package/dist/extensions/core/config.js.map +1 -0
  119. package/dist/extensions/core/cost.d.ts +11 -0
  120. package/dist/extensions/core/cost.d.ts.map +1 -0
  121. package/dist/extensions/core/cost.js +64 -0
  122. package/dist/extensions/core/cost.js.map +1 -0
  123. package/dist/extensions/core/hoo-core.d.ts +16 -179
  124. package/dist/extensions/core/hoo-core.d.ts.map +1 -1
  125. package/dist/extensions/core/hoo-core.js +27 -1663
  126. package/dist/extensions/core/hoo-core.js.map +1 -1
  127. package/dist/extensions/core/loop.d.ts +15 -0
  128. package/dist/extensions/core/loop.d.ts.map +1 -0
  129. package/dist/extensions/core/loop.js +235 -0
  130. package/dist/extensions/core/loop.js.map +1 -0
  131. package/dist/extensions/core/marketplace.d.ts +17 -0
  132. package/dist/extensions/core/marketplace.d.ts.map +1 -0
  133. package/dist/extensions/core/marketplace.js +167 -0
  134. package/dist/extensions/core/marketplace.js.map +1 -0
  135. package/dist/extensions/core/mcp-loader.d.ts +26 -0
  136. package/dist/extensions/core/mcp-loader.d.ts.map +1 -0
  137. package/dist/extensions/core/mcp-loader.js +391 -0
  138. package/dist/extensions/core/mcp-loader.js.map +1 -0
  139. package/dist/extensions/core/modes.d.ts +56 -0
  140. package/dist/extensions/core/modes.d.ts.map +1 -0
  141. package/dist/extensions/core/modes.js +268 -0
  142. package/dist/extensions/core/modes.js.map +1 -0
  143. package/dist/extensions/core/permission-gate.d.ts +10 -0
  144. package/dist/extensions/core/permission-gate.d.ts.map +1 -0
  145. package/dist/extensions/core/permission-gate.js +167 -0
  146. package/dist/extensions/core/permission-gate.js.map +1 -0
  147. package/dist/extensions/core/scaffold.d.ts +9 -0
  148. package/dist/extensions/core/scaffold.d.ts.map +1 -0
  149. package/dist/extensions/core/scaffold.js +146 -0
  150. package/dist/extensions/core/scaffold.js.map +1 -0
  151. package/dist/extensions/core/thinking-escalation.d.ts +18 -0
  152. package/dist/extensions/core/thinking-escalation.d.ts.map +1 -0
  153. package/dist/extensions/core/thinking-escalation.js +70 -0
  154. package/dist/extensions/core/thinking-escalation.js.map +1 -0
  155. package/dist/index.d.ts +2 -3
  156. package/dist/index.d.ts.map +1 -1
  157. package/dist/index.js +3 -4
  158. package/dist/index.js.map +1 -1
  159. package/dist/main.d.ts.map +1 -1
  160. package/dist/main.js +2 -2
  161. package/dist/main.js.map +1 -1
  162. package/dist/modes/interactive/command-executor.d.ts +0 -5
  163. package/dist/modes/interactive/command-executor.d.ts.map +1 -1
  164. package/dist/modes/interactive/command-executor.js +0 -7
  165. package/dist/modes/interactive/command-executor.js.map +1 -1
  166. package/dist/modes/interactive/components/branch-summary-message.d.ts +1 -1
  167. package/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -1
  168. package/dist/modes/interactive/components/branch-summary-message.js.map +1 -1
  169. package/dist/modes/interactive/components/compaction-summary-message.d.ts +1 -1
  170. package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -1
  171. package/dist/modes/interactive/components/compaction-summary-message.js.map +1 -1
  172. package/dist/modes/interactive/components/custom-message.d.ts +1 -1
  173. package/dist/modes/interactive/components/custom-message.d.ts.map +1 -1
  174. package/dist/modes/interactive/components/custom-message.js.map +1 -1
  175. package/dist/modes/interactive/components/index.d.ts +0 -2
  176. package/dist/modes/interactive/components/index.d.ts.map +1 -1
  177. package/dist/modes/interactive/components/index.js +0 -2
  178. package/dist/modes/interactive/components/index.js.map +1 -1
  179. package/dist/modes/interactive/extension-chrome.d.ts +56 -0
  180. package/dist/modes/interactive/extension-chrome.d.ts.map +1 -0
  181. package/dist/modes/interactive/extension-chrome.js +172 -0
  182. package/dist/modes/interactive/extension-chrome.js.map +1 -0
  183. package/dist/modes/interactive/extension-dialogs.d.ts +82 -0
  184. package/dist/modes/interactive/extension-dialogs.d.ts.map +1 -0
  185. package/dist/modes/interactive/extension-dialogs.js +259 -0
  186. package/dist/modes/interactive/extension-dialogs.js.map +1 -0
  187. package/dist/modes/interactive/interactive-mode.d.ts +11 -161
  188. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  189. package/dist/modes/interactive/interactive-mode.js +263 -1571
  190. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  191. package/dist/modes/interactive/resource-display.d.ts +59 -0
  192. package/dist/modes/interactive/resource-display.d.ts.map +1 -0
  193. package/dist/modes/interactive/resource-display.js +499 -0
  194. package/dist/modes/interactive/resource-display.js.map +1 -0
  195. package/dist/modes/interactive/startup-checks.d.ts +17 -0
  196. package/dist/modes/interactive/startup-checks.d.ts.map +1 -0
  197. package/dist/modes/interactive/startup-checks.js +102 -0
  198. package/dist/modes/interactive/startup-checks.js.map +1 -0
  199. package/dist/modes/interactive/team-focus.d.ts +56 -0
  200. package/dist/modes/interactive/team-focus.d.ts.map +1 -0
  201. package/dist/modes/interactive/team-focus.js +167 -0
  202. package/dist/modes/interactive/team-focus.js.map +1 -0
  203. package/dist/modes/interactive/voice/voice-controller.d.ts +66 -0
  204. package/dist/modes/interactive/voice/voice-controller.d.ts.map +1 -0
  205. package/dist/modes/interactive/voice/voice-controller.js +312 -0
  206. package/dist/modes/interactive/voice/voice-controller.js.map +1 -0
  207. package/dist/modes/interactive/voice/voice-panel.d.ts.map +1 -0
  208. package/dist/modes/interactive/voice/voice-panel.js.map +1 -0
  209. package/dist/modes/interactive/{voice-transcribe.d.ts → voice/voice-transcribe.d.ts} +16 -1
  210. package/dist/modes/interactive/voice/voice-transcribe.d.ts.map +1 -0
  211. package/dist/modes/interactive/{voice-transcribe.js → voice/voice-transcribe.js} +31 -4
  212. package/dist/modes/interactive/voice/voice-transcribe.js.map +1 -0
  213. package/dist/modes/rpc/rpc-client.d.ts +1 -2
  214. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  215. package/dist/modes/rpc/rpc-client.js.map +1 -1
  216. package/dist/modes/rpc/rpc-types.d.ts +1 -2
  217. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  218. package/dist/modes/rpc/rpc-types.js.map +1 -1
  219. package/dist/resources-cli.d.ts.map +1 -1
  220. package/dist/resources-cli.js +1 -1
  221. package/dist/resources-cli.js.map +1 -1
  222. package/dist/utils/tools-manager.d.ts.map +1 -1
  223. package/dist/utils/tools-manager.js +1 -1
  224. package/dist/utils/tools-manager.js.map +1 -1
  225. package/dist/utils/version-check.d.ts.map +1 -1
  226. package/dist/utils/version-check.js +2 -2
  227. package/dist/utils/version-check.js.map +1 -1
  228. package/docs/project-local-resources.md +1 -1
  229. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  230. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  231. package/examples/extensions/sandbox/package.json +1 -1
  232. package/examples/extensions/with-deps/package.json +1 -1
  233. package/package.json +4 -4
  234. package/dist/core/compaction/branch-summarization.d.ts +0 -88
  235. package/dist/core/compaction/branch-summarization.d.ts.map +0 -1
  236. package/dist/core/compaction/branch-summarization.js +0 -243
  237. package/dist/core/compaction/branch-summarization.js.map +0 -1
  238. package/dist/core/compaction/compaction.d.ts +0 -123
  239. package/dist/core/compaction/compaction.d.ts.map +0 -1
  240. package/dist/core/compaction/compaction.js +0 -635
  241. package/dist/core/compaction/compaction.js.map +0 -1
  242. package/dist/core/compaction/index.d.ts +0 -7
  243. package/dist/core/compaction/index.d.ts.map +0 -1
  244. package/dist/core/compaction/index.js +0 -7
  245. package/dist/core/compaction/index.js.map +0 -1
  246. package/dist/core/compaction/utils.d.ts +0 -38
  247. package/dist/core/compaction/utils.d.ts.map +0 -1
  248. package/dist/core/compaction/utils.js +0 -156
  249. package/dist/core/compaction/utils.js.map +0 -1
  250. package/dist/core/messages.d.ts +0 -124
  251. package/dist/core/messages.d.ts.map +0 -1
  252. package/dist/core/messages.js +0 -221
  253. package/dist/core/messages.js.map +0 -1
  254. package/dist/core/tools/browser-continue.d.ts.map +0 -1
  255. package/dist/core/tools/browser-continue.js.map +0 -1
  256. package/dist/core/tools/browser-run.d.ts.map +0 -1
  257. package/dist/core/tools/browser-run.js.map +0 -1
  258. package/dist/core/tools/browsertools-shared.d.ts.map +0 -1
  259. package/dist/core/tools/browsertools-shared.js.map +0 -1
  260. package/dist/core/tools/docedit.d.ts.map +0 -1
  261. package/dist/core/tools/docedit.js.map +0 -1
  262. package/dist/core/tools/docgrep.d.ts.map +0 -1
  263. package/dist/core/tools/docgrep.js.map +0 -1
  264. package/dist/core/tools/docpeek.d.ts.map +0 -1
  265. package/dist/core/tools/docread.d.ts.map +0 -1
  266. package/dist/core/tools/docread.js.map +0 -1
  267. package/dist/core/tools/docscan.d.ts.map +0 -1
  268. package/dist/core/tools/docscan.js.map +0 -1
  269. package/dist/core/tools/docwrite.d.ts.map +0 -1
  270. package/dist/core/tools/docwrite.js.map +0 -1
  271. package/dist/core/tools/output-compression.d.ts +0 -53
  272. package/dist/core/tools/output-compression.d.ts.map +0 -1
  273. package/dist/core/tools/output-compression.js +0 -392
  274. package/dist/core/tools/output-compression.js.map +0 -1
  275. package/dist/modes/interactive/components/armin.d.ts +0 -34
  276. package/dist/modes/interactive/components/armin.d.ts.map +0 -1
  277. package/dist/modes/interactive/components/armin.js +0 -333
  278. package/dist/modes/interactive/components/armin.js.map +0 -1
  279. package/dist/modes/interactive/components/daxnuts.d.ts +0 -23
  280. package/dist/modes/interactive/components/daxnuts.d.ts.map +0 -1
  281. package/dist/modes/interactive/components/daxnuts.js +0 -140
  282. package/dist/modes/interactive/components/daxnuts.js.map +0 -1
  283. package/dist/modes/interactive/components/voice-panel.d.ts.map +0 -1
  284. package/dist/modes/interactive/components/voice-panel.js.map +0 -1
  285. package/dist/modes/interactive/voice-transcribe.d.ts.map +0 -1
  286. package/dist/modes/interactive/voice-transcribe.js.map +0 -1
  287. /package/dist/core/tools/{browsertools-shared.d.ts → browser/browsertools-shared.d.ts} +0 -0
  288. /package/dist/modes/interactive/{components → voice}/voice-panel.d.ts +0 -0
  289. /package/dist/modes/interactive/{components → voice}/voice-panel.js +0 -0
@@ -0,0 +1,110 @@
1
+ /**
2
+ * hoo-config.json — types, I/O, and merge rules shared by the hoo-core extensions.
3
+ *
4
+ * Config merge order (lowest → highest priority):
5
+ * 1. ~/.hoocode/hoo-config.json (global defaults)
6
+ * 2. ./.hoocode/hoo-config.json (project overrides — scalars win; arrays union)
7
+ */
8
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
9
+ import { join } from "node:path";
10
+ import { getHooCodeDir } from "../../config.js";
11
+ const HOOCODE_DIR = getHooCodeDir();
12
+ const GLOBAL_CONFIG_PATH = join(HOOCODE_DIR, "hoo-config.json");
13
+ export function readConfig() {
14
+ try {
15
+ return JSON.parse(readFileSync(GLOBAL_CONFIG_PATH, "utf8"));
16
+ }
17
+ catch {
18
+ return {};
19
+ }
20
+ }
21
+ export function writeConfig(config) {
22
+ if (!existsSync(HOOCODE_DIR))
23
+ mkdirSync(HOOCODE_DIR, { recursive: true });
24
+ writeFileSync(GLOBAL_CONFIG_PATH, `${JSON.stringify(config, null, 2)}\n`, "utf8");
25
+ }
26
+ /**
27
+ * Deep-merges a project-local config on top of the global config.
28
+ *
29
+ * Merge rules:
30
+ * - active_mode: project wins if set
31
+ * - modes[x].auto_allow: union of global + project arrays
32
+ * - modes[x].allowed_write_paths: union of global + project arrays
33
+ * - modes[x].enabled_tools: project wins if set, else falls back to global
34
+ * - mode_paths: project list is prepended so project paths are searched first
35
+ * - thinking_escalation: project wins as a whole if set, else inherit global
36
+ */
37
+ export function mergeConfigs(global, project) {
38
+ const merged = { ...global };
39
+ if (project.active_mode !== undefined)
40
+ merged.active_mode = project.active_mode;
41
+ // thinking_escalation: project wins as a whole if set, else inherit global.
42
+ if (project.thinking_escalation !== undefined)
43
+ merged.thinking_escalation = project.thinking_escalation;
44
+ if (project.modes) {
45
+ merged.modes = { ...(global.modes ?? {}) };
46
+ for (const [mode, projectCfg] of Object.entries(project.modes)) {
47
+ const globalCfg = global.modes?.[mode] ?? {};
48
+ merged.modes[mode] = {
49
+ ...globalCfg,
50
+ ...projectCfg,
51
+ // Union both auto_allow lists so project can extend, not just replace
52
+ auto_allow: Array.from(new Set([...(globalCfg.auto_allow ?? []), ...(projectCfg.auto_allow ?? [])])),
53
+ // Union allowed_write_paths so project can extend
54
+ allowed_write_paths: Array.from(new Set([...(globalCfg.allowed_write_paths ?? []), ...(projectCfg.allowed_write_paths ?? [])])),
55
+ // enabled_tools: project wins if set, else falls back to global
56
+ enabled_tools: projectCfg.enabled_tools ?? globalCfg.enabled_tools,
57
+ // denied_tools: union so project can add more denied tools on top of global
58
+ denied_tools: Array.from(new Set([...(globalCfg.denied_tools ?? []), ...(projectCfg.denied_tools ?? [])])),
59
+ // allowed_bash_commands: project wins if set, else falls back to global
60
+ allowed_bash_commands: projectCfg.allowed_bash_commands ?? globalCfg.allowed_bash_commands,
61
+ // denied_bash_commands: union so project can add more denied patterns on top of global
62
+ denied_bash_commands: Array.from(new Set([...(globalCfg.denied_bash_commands ?? []), ...(projectCfg.denied_bash_commands ?? [])])),
63
+ };
64
+ }
65
+ }
66
+ if (project.mode_paths || global.mode_paths) {
67
+ // Project paths first so they're searched before global paths
68
+ merged.mode_paths = dedupePaths([...(project.mode_paths ?? []), ...(global.mode_paths ?? [])]);
69
+ }
70
+ return merged;
71
+ }
72
+ function dedupePaths(paths) {
73
+ const seen = new Set();
74
+ const out = [];
75
+ for (const p of paths) {
76
+ if (!seen.has(p)) {
77
+ seen.add(p);
78
+ out.push(p);
79
+ }
80
+ }
81
+ return out;
82
+ }
83
+ export function mergeSearchPaths(...sources) {
84
+ const merged = [];
85
+ for (const source of sources) {
86
+ if (!source)
87
+ continue;
88
+ merged.push(...source);
89
+ }
90
+ return dedupePaths(merged);
91
+ }
92
+ /**
93
+ * Reads the global config and optionally overlays the project-local config at
94
+ * `./.hoocode/hoo-config.json`. Project values win on all scalar fields; arrays are
95
+ * unioned (see mergeConfigs for full rules).
96
+ */
97
+ export function readMergedConfig(cwd) {
98
+ const global = readConfig();
99
+ const projectPath = join(cwd, ".hoocode", "hoo-config.json");
100
+ if (!existsSync(projectPath))
101
+ return global;
102
+ try {
103
+ const project = JSON.parse(readFileSync(projectPath, "utf8"));
104
+ return mergeConfigs(global, project);
105
+ }
106
+ catch {
107
+ return global;
108
+ }
109
+ }
110
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../../src/extensions/core/config.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * hoo-config.json — types, I/O, and merge rules shared by the hoo-core extensions.\n *\n * Config merge order (lowest → highest priority):\n * 1. ~/.hoocode/hoo-config.json (global defaults)\n * 2. ./.hoocode/hoo-config.json (project overrides — scalars win; arrays union)\n */\n\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { ThinkingLevel } from \"@kolisachint/hoocode-agent-core\";\nimport { getHooCodeDir } from \"../../config.js\";\n\nconst HOOCODE_DIR = getHooCodeDir();\nconst GLOBAL_CONFIG_PATH = join(HOOCODE_DIR, \"hoo-config.json\");\n\nexport interface ModeConfig {\n\t/** Tool names that bypass the permission gate in this mode */\n\tauto_allow?: string[];\n\t/** Tool names available in this mode (if set, only these tools are active) */\n\tenabled_tools?: string[];\n\t/** Tool names explicitly blocked in this mode regardless of enabled_tools */\n\tdenied_tools?: string[];\n\t/** Allowed write paths in this mode (glob patterns, only applies if write/edit is enabled) */\n\tallowed_write_paths?: string[];\n\t/** Regex patterns for allowed bash commands. If set, a command must match at least one to execute. */\n\tallowed_bash_commands?: string[];\n\t/** Regex patterns for denied bash commands. A command matching any pattern is blocked. */\n\tdenied_bash_commands?: string[];\n}\n\n/**\n * Tool-outcome-driven thinking escalation (on by default).\n *\n * The default (fast) path keeps thinking low so mechanical tool turns — reads,\n * greps, successful edits — don't pay the extended-thinking prefill tax. When a\n * tool *fails*, the next turn(s) escalate to a higher thinking level so the model\n * reasons through the failure, then the level is restored. This buys low latency\n * on the happy path while preserving deep reasoning exactly when something breaks.\n *\n * Enabled by default; set `thinking_escalation.enabled` to `false` to turn it off.\n *\n * Note: escalation uses the same setter as manual thinking control, so the\n * escalated level is briefly written to settings and restored when the window\n * ends. If a run is interrupted mid-window, the escalated level may persist until\n * the next change.\n */\nexport interface ThinkingEscalationConfig {\n\t/** Master switch. Default: true (set to false to disable). */\n\tenabled?: boolean;\n\t/** Level to escalate to after a tool error. Default: \"high\". */\n\ton_error?: ThinkingLevel;\n\t/** Restrict escalation to errors from these tool names. Default: any tool. */\n\ttools?: string[];\n\t/** Number of subsequent turns to stay escalated after an error. Default: 1. */\n\tcooldown_turns?: number;\n}\n\nexport interface HooConfig {\n\t/** Manually-pinned active mode (overrides default \"build\") */\n\tactive_mode?: string;\n\t/** Per-mode configuration keyed by mode name */\n\tmodes?: Record<string, ModeConfig>;\n\t/** Extra directories to search for `{name}/system.md` mode files (after project + user). */\n\tmode_paths?: string[];\n\t/** Raise thinking after tool failures, restore on success. On by default; set `enabled: false` to disable. */\n\tthinking_escalation?: ThinkingEscalationConfig;\n}\n\nexport function readConfig(): HooConfig {\n\ttry {\n\t\treturn JSON.parse(readFileSync(GLOBAL_CONFIG_PATH, \"utf8\")) as HooConfig;\n\t} catch {\n\t\treturn {};\n\t}\n}\n\nexport function writeConfig(config: HooConfig): void {\n\tif (!existsSync(HOOCODE_DIR)) mkdirSync(HOOCODE_DIR, { recursive: true });\n\twriteFileSync(GLOBAL_CONFIG_PATH, `${JSON.stringify(config, null, 2)}\\n`, \"utf8\");\n}\n\n/**\n * Deep-merges a project-local config on top of the global config.\n *\n * Merge rules:\n * - active_mode: project wins if set\n * - modes[x].auto_allow: union of global + project arrays\n * - modes[x].allowed_write_paths: union of global + project arrays\n * - modes[x].enabled_tools: project wins if set, else falls back to global\n * - mode_paths: project list is prepended so project paths are searched first\n * - thinking_escalation: project wins as a whole if set, else inherit global\n */\nexport function mergeConfigs(global: HooConfig, project: HooConfig): HooConfig {\n\tconst merged: HooConfig = { ...global };\n\n\tif (project.active_mode !== undefined) merged.active_mode = project.active_mode;\n\n\t// thinking_escalation: project wins as a whole if set, else inherit global.\n\tif (project.thinking_escalation !== undefined) merged.thinking_escalation = project.thinking_escalation;\n\n\tif (project.modes) {\n\t\tmerged.modes = { ...(global.modes ?? {}) };\n\t\tfor (const [mode, projectCfg] of Object.entries(project.modes)) {\n\t\t\tconst globalCfg = global.modes?.[mode] ?? {};\n\t\t\tmerged.modes[mode] = {\n\t\t\t\t...globalCfg,\n\t\t\t\t...projectCfg,\n\t\t\t\t// Union both auto_allow lists so project can extend, not just replace\n\t\t\t\tauto_allow: Array.from(new Set([...(globalCfg.auto_allow ?? []), ...(projectCfg.auto_allow ?? [])])),\n\t\t\t\t// Union allowed_write_paths so project can extend\n\t\t\t\tallowed_write_paths: Array.from(\n\t\t\t\t\tnew Set([...(globalCfg.allowed_write_paths ?? []), ...(projectCfg.allowed_write_paths ?? [])]),\n\t\t\t\t),\n\t\t\t\t// enabled_tools: project wins if set, else falls back to global\n\t\t\t\tenabled_tools: projectCfg.enabled_tools ?? globalCfg.enabled_tools,\n\t\t\t\t// denied_tools: union so project can add more denied tools on top of global\n\t\t\t\tdenied_tools: Array.from(new Set([...(globalCfg.denied_tools ?? []), ...(projectCfg.denied_tools ?? [])])),\n\t\t\t\t// allowed_bash_commands: project wins if set, else falls back to global\n\t\t\t\tallowed_bash_commands: projectCfg.allowed_bash_commands ?? globalCfg.allowed_bash_commands,\n\t\t\t\t// denied_bash_commands: union so project can add more denied patterns on top of global\n\t\t\t\tdenied_bash_commands: Array.from(\n\t\t\t\t\tnew Set([...(globalCfg.denied_bash_commands ?? []), ...(projectCfg.denied_bash_commands ?? [])]),\n\t\t\t\t),\n\t\t\t};\n\t\t}\n\t}\n\n\tif (project.mode_paths || global.mode_paths) {\n\t\t// Project paths first so they're searched before global paths\n\t\tmerged.mode_paths = dedupePaths([...(project.mode_paths ?? []), ...(global.mode_paths ?? [])]);\n\t}\n\n\treturn merged;\n}\n\nfunction dedupePaths(paths: string[]): string[] {\n\tconst seen = new Set<string>();\n\tconst out: string[] = [];\n\tfor (const p of paths) {\n\t\tif (!seen.has(p)) {\n\t\t\tseen.add(p);\n\t\t\tout.push(p);\n\t\t}\n\t}\n\treturn out;\n}\n\nexport function mergeSearchPaths(...sources: (string[] | undefined)[]): string[] {\n\tconst merged: string[] = [];\n\tfor (const source of sources) {\n\t\tif (!source) continue;\n\t\tmerged.push(...source);\n\t}\n\treturn dedupePaths(merged);\n}\n\n/**\n * Reads the global config and optionally overlays the project-local config at\n * `./.hoocode/hoo-config.json`. Project values win on all scalar fields; arrays are\n * unioned (see mergeConfigs for full rules).\n */\nexport function readMergedConfig(cwd: string): HooConfig {\n\tconst global = readConfig();\n\tconst projectPath = join(cwd, \".hoocode\", \"hoo-config.json\");\n\tif (!existsSync(projectPath)) return global;\n\ttry {\n\t\tconst project = JSON.parse(readFileSync(projectPath, \"utf8\")) as HooConfig;\n\t\treturn mergeConfigs(global, project);\n\t} catch {\n\t\treturn global;\n\t}\n}\n"]}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * /cost — session token and cost totals.
3
+ *
4
+ * Walks every assistant message in the current session and sums tokens + cost,
5
+ * then prints a session total followed by a per-model breakdown.
6
+ * Per-tool attribution is intentionally not shown — tokens aren't tracked
7
+ * per-tool, and any heuristic would be misleading.
8
+ */
9
+ import type { ExtensionAPI } from "../../core/extensions/types.js";
10
+ export declare function setupCost(pi: ExtensionAPI): void;
11
+ //# sourceMappingURL=cost.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cost.d.ts","sourceRoot":"","sources":["../../../src/extensions/core/cost.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,YAAY,EAA2B,MAAM,gCAAgC,CAAC;AAE5F,wBAAgB,SAAS,CAAC,EAAE,EAAE,YAAY,GAAG,IAAI,CA2DhD","sourcesContent":["/**\n * /cost — session token and cost totals.\n *\n * Walks every assistant message in the current session and sums tokens + cost,\n * then prints a session total followed by a per-model breakdown.\n * Per-tool attribution is intentionally not shown — tokens aren't tracked\n * per-tool, and any heuristic would be misleading.\n */\n\nimport type { ExtensionAPI, ExtensionCommandContext } from \"../../core/extensions/types.js\";\n\nexport function setupCost(pi: ExtensionAPI): void {\n\tpi.registerCommand(\"cost\", {\n\t\tdescription: \"Show session token and cost totals, broken down by model.\",\n\t\tgetArgumentCompletions: () => [],\n\t\thandler: async (_args: string, ctx: ExtensionCommandContext): Promise<void> => {\n\t\t\ttype Totals = { input: number; output: number; cacheRead: number; cacheWrite: number; cost: number };\n\t\t\tconst empty = (): Totals => ({ input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0 });\n\t\t\tconst total = empty();\n\t\t\tconst perModel = new Map<string, Totals>();\n\t\t\tlet assistantTurns = 0;\n\n\t\t\tfor (const entry of ctx.sessionManager.getEntries()) {\n\t\t\t\tif (entry.type !== \"message\" || entry.message.role !== \"assistant\") continue;\n\t\t\t\tconst u = entry.message.usage;\n\t\t\t\tif (!u) continue;\n\t\t\t\tassistantTurns++;\n\t\t\t\ttotal.input += u.input;\n\t\t\t\ttotal.output += u.output;\n\t\t\t\ttotal.cacheRead += u.cacheRead;\n\t\t\t\ttotal.cacheWrite += u.cacheWrite;\n\t\t\t\ttotal.cost += u.cost.total;\n\n\t\t\t\tconst key = `${entry.message.provider}/${entry.message.model}`;\n\t\t\t\tconst t = perModel.get(key) ?? empty();\n\t\t\t\tt.input += u.input;\n\t\t\t\tt.output += u.output;\n\t\t\t\tt.cacheRead += u.cacheRead;\n\t\t\t\tt.cacheWrite += u.cacheWrite;\n\t\t\t\tt.cost += u.cost.total;\n\t\t\t\tperModel.set(key, t);\n\t\t\t}\n\n\t\t\tif (assistantTurns === 0) {\n\t\t\t\tctx.ui.notify(\"No assistant turns yet — nothing to cost.\", \"info\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst fmt = (n: number) => n.toLocaleString();\n\t\t\tconst fmtCost = (n: number) => `$${n.toFixed(4)}`;\n\t\t\tconst lines: string[] = [];\n\t\t\tlines.push(`Session totals (${assistantTurns} assistant turn${assistantTurns === 1 ? \"\" : \"s\"})`);\n\t\t\tlines.push(` Input ${fmt(total.input)}`);\n\t\t\tlines.push(` Output ${fmt(total.output)}`);\n\t\t\tlines.push(` Cache read ${fmt(total.cacheRead)}`);\n\t\t\tlines.push(` Cache write ${fmt(total.cacheWrite)}`);\n\t\t\tlines.push(` Cost ${fmtCost(total.cost)}`);\n\n\t\t\tif (perModel.size > 1) {\n\t\t\t\tlines.push(\"\");\n\t\t\t\tlines.push(\"By model:\");\n\t\t\t\tconst sorted = [...perModel.entries()].sort((a, b) => b[1].cost - a[1].cost);\n\t\t\t\tfor (const [key, t] of sorted) {\n\t\t\t\t\tlines.push(` ${key}: ${fmt(t.input)} in / ${fmt(t.output)} out ${fmtCost(t.cost)}`);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tctx.ui.notify(lines.join(\"\\n\"), \"info\");\n\t\t},\n\t});\n}\n"]}
@@ -0,0 +1,64 @@
1
+ /**
2
+ * /cost — session token and cost totals.
3
+ *
4
+ * Walks every assistant message in the current session and sums tokens + cost,
5
+ * then prints a session total followed by a per-model breakdown.
6
+ * Per-tool attribution is intentionally not shown — tokens aren't tracked
7
+ * per-tool, and any heuristic would be misleading.
8
+ */
9
+ export function setupCost(pi) {
10
+ pi.registerCommand("cost", {
11
+ description: "Show session token and cost totals, broken down by model.",
12
+ getArgumentCompletions: () => [],
13
+ handler: async (_args, ctx) => {
14
+ const empty = () => ({ input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0 });
15
+ const total = empty();
16
+ const perModel = new Map();
17
+ let assistantTurns = 0;
18
+ for (const entry of ctx.sessionManager.getEntries()) {
19
+ if (entry.type !== "message" || entry.message.role !== "assistant")
20
+ continue;
21
+ const u = entry.message.usage;
22
+ if (!u)
23
+ continue;
24
+ assistantTurns++;
25
+ total.input += u.input;
26
+ total.output += u.output;
27
+ total.cacheRead += u.cacheRead;
28
+ total.cacheWrite += u.cacheWrite;
29
+ total.cost += u.cost.total;
30
+ const key = `${entry.message.provider}/${entry.message.model}`;
31
+ const t = perModel.get(key) ?? empty();
32
+ t.input += u.input;
33
+ t.output += u.output;
34
+ t.cacheRead += u.cacheRead;
35
+ t.cacheWrite += u.cacheWrite;
36
+ t.cost += u.cost.total;
37
+ perModel.set(key, t);
38
+ }
39
+ if (assistantTurns === 0) {
40
+ ctx.ui.notify("No assistant turns yet — nothing to cost.", "info");
41
+ return;
42
+ }
43
+ const fmt = (n) => n.toLocaleString();
44
+ const fmtCost = (n) => `$${n.toFixed(4)}`;
45
+ const lines = [];
46
+ lines.push(`Session totals (${assistantTurns} assistant turn${assistantTurns === 1 ? "" : "s"})`);
47
+ lines.push(` Input ${fmt(total.input)}`);
48
+ lines.push(` Output ${fmt(total.output)}`);
49
+ lines.push(` Cache read ${fmt(total.cacheRead)}`);
50
+ lines.push(` Cache write ${fmt(total.cacheWrite)}`);
51
+ lines.push(` Cost ${fmtCost(total.cost)}`);
52
+ if (perModel.size > 1) {
53
+ lines.push("");
54
+ lines.push("By model:");
55
+ const sorted = [...perModel.entries()].sort((a, b) => b[1].cost - a[1].cost);
56
+ for (const [key, t] of sorted) {
57
+ lines.push(` ${key}: ${fmt(t.input)} in / ${fmt(t.output)} out ${fmtCost(t.cost)}`);
58
+ }
59
+ }
60
+ ctx.ui.notify(lines.join("\n"), "info");
61
+ },
62
+ });
63
+ }
64
+ //# sourceMappingURL=cost.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cost.js","sourceRoot":"","sources":["../../../src/extensions/core/cost.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,MAAM,UAAU,SAAS,CAAC,EAAgB,EAAQ;IACjD,EAAE,CAAC,eAAe,CAAC,MAAM,EAAE;QAC1B,WAAW,EAAE,2DAA2D;QACxE,sBAAsB,EAAE,GAAG,EAAE,CAAC,EAAE;QAChC,OAAO,EAAE,KAAK,EAAE,KAAa,EAAE,GAA4B,EAAiB,EAAE,CAAC;YAE9E,MAAM,KAAK,GAAG,GAAW,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC;YAC5F,MAAM,KAAK,GAAG,KAAK,EAAE,CAAC;YACtB,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAkB,CAAC;YAC3C,IAAI,cAAc,GAAG,CAAC,CAAC;YAEvB,KAAK,MAAM,KAAK,IAAI,GAAG,CAAC,cAAc,CAAC,UAAU,EAAE,EAAE,CAAC;gBACrD,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,KAAK,WAAW;oBAAE,SAAS;gBAC7E,MAAM,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;gBAC9B,IAAI,CAAC,CAAC;oBAAE,SAAS;gBACjB,cAAc,EAAE,CAAC;gBACjB,KAAK,CAAC,KAAK,IAAI,CAAC,CAAC,KAAK,CAAC;gBACvB,KAAK,CAAC,MAAM,IAAI,CAAC,CAAC,MAAM,CAAC;gBACzB,KAAK,CAAC,SAAS,IAAI,CAAC,CAAC,SAAS,CAAC;gBAC/B,KAAK,CAAC,UAAU,IAAI,CAAC,CAAC,UAAU,CAAC;gBACjC,KAAK,CAAC,IAAI,IAAI,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC;gBAE3B,MAAM,GAAG,GAAG,GAAG,KAAK,CAAC,OAAO,CAAC,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;gBAC/D,MAAM,CAAC,GAAG,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,KAAK,EAAE,CAAC;gBACvC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,KAAK,CAAC;gBACnB,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,MAAM,CAAC;gBACrB,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC,SAAS,CAAC;gBAC3B,CAAC,CAAC,UAAU,IAAI,CAAC,CAAC,UAAU,CAAC;gBAC7B,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC;gBACvB,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;YACtB,CAAC;YAED,IAAI,cAAc,KAAK,CAAC,EAAE,CAAC;gBAC1B,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC,6CAA2C,EAAE,MAAM,CAAC,CAAC;gBACnE,OAAO;YACR,CAAC;YAED,MAAM,GAAG,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,cAAc,EAAE,CAAC;YAC9C,MAAM,OAAO,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;YAClD,MAAM,KAAK,GAAa,EAAE,CAAC;YAC3B,KAAK,CAAC,IAAI,CAAC,mBAAmB,cAAc,kBAAkB,cAAc,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC;YAClG,KAAK,CAAC,IAAI,CAAC,mBAAmB,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;YAClD,KAAK,CAAC,IAAI,CAAC,mBAAmB,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YACnD,KAAK,CAAC,IAAI,CAAC,mBAAmB,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;YACtD,KAAK,CAAC,IAAI,CAAC,mBAAmB,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;YACvD,KAAK,CAAC,IAAI,CAAC,mBAAmB,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAErD,IAAI,QAAQ,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC;gBACvB,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;gBACf,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;gBACxB,MAAM,MAAM,GAAG,CAAC,GAAG,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;gBAC7E,KAAK,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC,IAAI,MAAM,EAAE,CAAC;oBAC/B,KAAK,CAAC,IAAI,CAAC,KAAK,GAAG,KAAK,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;gBACvF,CAAC;YACF,CAAC;YAED,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,CAAC;QAAA,CACxC;KACD,CAAC,CAAC;AAAA,CACH","sourcesContent":["/**\n * /cost — session token and cost totals.\n *\n * Walks every assistant message in the current session and sums tokens + cost,\n * then prints a session total followed by a per-model breakdown.\n * Per-tool attribution is intentionally not shown — tokens aren't tracked\n * per-tool, and any heuristic would be misleading.\n */\n\nimport type { ExtensionAPI, ExtensionCommandContext } from \"../../core/extensions/types.js\";\n\nexport function setupCost(pi: ExtensionAPI): void {\n\tpi.registerCommand(\"cost\", {\n\t\tdescription: \"Show session token and cost totals, broken down by model.\",\n\t\tgetArgumentCompletions: () => [],\n\t\thandler: async (_args: string, ctx: ExtensionCommandContext): Promise<void> => {\n\t\t\ttype Totals = { input: number; output: number; cacheRead: number; cacheWrite: number; cost: number };\n\t\t\tconst empty = (): Totals => ({ input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0 });\n\t\t\tconst total = empty();\n\t\t\tconst perModel = new Map<string, Totals>();\n\t\t\tlet assistantTurns = 0;\n\n\t\t\tfor (const entry of ctx.sessionManager.getEntries()) {\n\t\t\t\tif (entry.type !== \"message\" || entry.message.role !== \"assistant\") continue;\n\t\t\t\tconst u = entry.message.usage;\n\t\t\t\tif (!u) continue;\n\t\t\t\tassistantTurns++;\n\t\t\t\ttotal.input += u.input;\n\t\t\t\ttotal.output += u.output;\n\t\t\t\ttotal.cacheRead += u.cacheRead;\n\t\t\t\ttotal.cacheWrite += u.cacheWrite;\n\t\t\t\ttotal.cost += u.cost.total;\n\n\t\t\t\tconst key = `${entry.message.provider}/${entry.message.model}`;\n\t\t\t\tconst t = perModel.get(key) ?? empty();\n\t\t\t\tt.input += u.input;\n\t\t\t\tt.output += u.output;\n\t\t\t\tt.cacheRead += u.cacheRead;\n\t\t\t\tt.cacheWrite += u.cacheWrite;\n\t\t\t\tt.cost += u.cost.total;\n\t\t\t\tperModel.set(key, t);\n\t\t\t}\n\n\t\t\tif (assistantTurns === 0) {\n\t\t\t\tctx.ui.notify(\"No assistant turns yet — nothing to cost.\", \"info\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst fmt = (n: number) => n.toLocaleString();\n\t\t\tconst fmtCost = (n: number) => `$${n.toFixed(4)}`;\n\t\t\tconst lines: string[] = [];\n\t\t\tlines.push(`Session totals (${assistantTurns} assistant turn${assistantTurns === 1 ? \"\" : \"s\"})`);\n\t\t\tlines.push(` Input ${fmt(total.input)}`);\n\t\t\tlines.push(` Output ${fmt(total.output)}`);\n\t\t\tlines.push(` Cache read ${fmt(total.cacheRead)}`);\n\t\t\tlines.push(` Cache write ${fmt(total.cacheWrite)}`);\n\t\t\tlines.push(` Cost ${fmtCost(total.cost)}`);\n\n\t\t\tif (perModel.size > 1) {\n\t\t\t\tlines.push(\"\");\n\t\t\t\tlines.push(\"By model:\");\n\t\t\t\tconst sorted = [...perModel.entries()].sort((a, b) => b[1].cost - a[1].cost);\n\t\t\t\tfor (const [key, t] of sorted) {\n\t\t\t\t\tlines.push(` ${key}: ${fmt(t.input)} in / ${fmt(t.output)} out ${fmtCost(t.cost)}`);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tctx.ui.notify(lines.join(\"\\n\"), \"info\");\n\t\t},\n\t});\n}\n"]}
@@ -1,185 +1,22 @@
1
1
  /**
2
- * hoo-core — HooCode built-in core extension
3
- *
4
- * A. Permission Gate — prompts before bash/write/edit; checks modes.{mode}.auto_allow
5
- * from the merged (global + project) config; persists "always"
6
- * choices back to the global config
7
- * B. MCP Server Loader — discovers ~/.hoocode/mcp-servers and ./.hoocode/mcp-servers JSON
8
- * configs, connects via JSON-RPC 2.0, registers server tools
9
- * C. Mode resolves active mode (ask/plan/build/debug), loads the mode's
10
- * system prompt, filters active tools, and exposes /mode, /plan,
11
- * and /approve commands
12
- *
13
- * Config merge order (lowest → highest priority):
14
- * 1. ~/.hoocode/hoo-config.json (global defaults)
15
- * 2. ./.hoocode/hoo-config.json (project overrides — scalars win; arrays union)
2
+ * hoo-core — HooCode built-in core extension (composition root).
3
+ *
4
+ * Each concern lives in its own module in this directory:
5
+ * - permission-gate.ts — prompts before bash/write/edit; hard tool/command policy
6
+ * - mcp-loader.ts — discovers MCP server configs, connects, registers tools
7
+ * - modes.ts — active mode resolution, mode prompts, /mode /plan /approve
8
+ * - cost.ts — /cost session token + cost totals
9
+ * - scaffold.ts /new-skill /new-agent /new-command
10
+ * - ask-options.ts — the ask_options tool (inline decision pane)
11
+ * - thinking-escalation.ts — raise thinking after tool errors, then restore
12
+ * - loop.ts — /loop cron scheduling + autonomous continuation
13
+ * - marketplace.ts — /plugin marketplace + install/remove
14
+ * - config.ts — hoo-config.json types, I/O, and merge rules
15
+ *
16
+ * `bin/hoocode.js` loads this module's default export as the built-in
17
+ * extension factory.
16
18
  */
17
- import type { ThinkingLevel } from "@kolisachint/hoocode-agent-core";
18
19
  import type { ExtensionAPI } from "../../core/extensions/types.js";
19
- interface ModeConfig {
20
- /** Tool names that bypass the permission gate in this mode */
21
- auto_allow?: string[];
22
- /** Tool names available in this mode (if set, only these tools are active) */
23
- enabled_tools?: string[];
24
- /** Tool names explicitly blocked in this mode regardless of enabled_tools */
25
- denied_tools?: string[];
26
- /** Allowed write paths in this mode (glob patterns, only applies if write/edit is enabled) */
27
- allowed_write_paths?: string[];
28
- /** Regex patterns for allowed bash commands. If set, a command must match at least one to execute. */
29
- allowed_bash_commands?: string[];
30
- /** Regex patterns for denied bash commands. A command matching any pattern is blocked. */
31
- denied_bash_commands?: string[];
32
- }
33
- /**
34
- * Tool-outcome-driven thinking escalation (on by default).
35
- *
36
- * The default (fast) path keeps thinking low so mechanical tool turns — reads,
37
- * greps, successful edits — don't pay the extended-thinking prefill tax. When a
38
- * tool *fails*, the next turn(s) escalate to a higher thinking level so the model
39
- * reasons through the failure, then the level is restored. This buys low latency
40
- * on the happy path while preserving deep reasoning exactly when something breaks.
41
- *
42
- * Enabled by default; set `thinking_escalation.enabled` to `false` to turn it off.
43
- *
44
- * Note: escalation uses the same setter as manual thinking control, so the
45
- * escalated level is briefly written to settings and restored when the window
46
- * ends. If a run is interrupted mid-window, the escalated level may persist until
47
- * the next change.
48
- */
49
- interface ThinkingEscalationConfig {
50
- /** Master switch. Default: true (set to false to disable). */
51
- enabled?: boolean;
52
- /** Level to escalate to after a tool error. Default: "high". */
53
- on_error?: ThinkingLevel;
54
- /** Restrict escalation to errors from these tool names. Default: any tool. */
55
- tools?: string[];
56
- /** Number of subsequent turns to stay escalated after an error. Default: 1. */
57
- cooldown_turns?: number;
58
- }
59
- export interface HooConfig {
60
- /** Manually-pinned active mode (overrides default "build") */
61
- active_mode?: string;
62
- /** Per-mode configuration keyed by mode name */
63
- modes?: Record<string, ModeConfig>;
64
- /** Extra directories to search for `{name}/system.md` mode files (after project + user). */
65
- mode_paths?: string[];
66
- /** Raise thinking after tool failures, restore on success. On by default; set `enabled: false` to disable. */
67
- thinking_escalation?: ThinkingEscalationConfig;
68
- }
69
- /**
70
- * Deep-merges a project-local config on top of the global config.
71
- *
72
- * Merge rules:
73
- * - active_mode: project wins if set
74
- * - modes[x].auto_allow: union of global + project arrays
75
- * - modes[x].allowed_write_paths: union of global + project arrays
76
- * - modes[x].enabled_tools: project wins if set, else falls back to global
77
- * - mode_paths: project list is prepended so project paths are searched first
78
- * - thinking_escalation: project wins as a whole if set, else inherit global
79
- */
80
- export declare function mergeConfigs(global: HooConfig, project: HooConfig): HooConfig;
81
- /**
82
- * Reads the global config and optionally overlays the project-local config at
83
- * `./.hoocode/hoo-config.json`. Project values win on all scalar fields; arrays are
84
- * unioned (see mergeConfigs for full rules).
85
- */
86
- export declare function readMergedConfig(cwd: string): HooConfig;
87
- export declare function setupPermissionGate(pi: ExtensionAPI): void;
88
- export interface McpServerConfig {
89
- /** Unique server identifier used as prefix for registered tool names */
90
- name: string;
91
- /** Executable to spawn */
92
- command: string;
93
- /** Optional arguments passed to the command */
94
- args?: string[];
95
- /** Optional extra environment variables for the server process */
96
- env?: Record<string, string>;
97
- /** Run MCP tools in background by default (default: true for MCP servers) */
98
- background?: boolean;
99
- }
100
- export declare function setupMcpLoader(pi: ExtensionAPI): void;
101
- /**
102
- * Returns the system prompt for the active mode.
103
- *
104
- * Search order (first hit wins):
105
- * - `./.hoocode/modes/{mode}/system.md`
106
- * - `~/.hoocode/modes/{mode}/system.md`
107
- * - each of `externalDirs` in declared order (config + CLI + extension contributions)
108
- * - built-in MODE_DEFAULTS for the four known modes
109
- */
110
- export declare function buildSystemPrompt(mode: string, cwd: string, options?: {
111
- modePaths?: string[];
112
- }): string | undefined;
113
- export interface PlanSections {
114
- goal?: string;
115
- filesToModify?: string;
116
- newFiles?: string;
117
- tests?: string;
118
- verification?: string;
119
- /** Original full text, used as fallback if no sections parsed */
120
- raw: string;
121
- }
122
- /**
123
- * Parses `.hoocode/plan.md` into named sections.
124
- *
125
- * Recognises both ATX headings (`## Goal`) and bold labels (`**Goal**`).
126
- * Section names matched (case-insensitive): Goal, Files to modify, New files,
127
- * Tests, Verification.
128
- */
129
- export declare function parsePlanSections(planContent: string): PlanSections;
130
- /**
131
- * Builds the user message sent to the agent when `/approve` is run.
132
- *
133
- * If the plan has recognisable sections, each is presented as a numbered step
134
- * so the agent works through them sequentially. Otherwise the raw plan is used.
135
- *
136
- * Execution order:
137
- * 1. Modify existing files
138
- * 2. Create new files
139
- * 3. Update / add tests
140
- * 4. Run verification commands
141
- */
142
- export declare function buildApproveMessage(sections: PlanSections): string;
143
- export declare function setupMode(pi: ExtensionAPI): void;
144
- export declare function setupAskOptions(pi: ExtensionAPI): void;
145
- /**
146
- * Raise the thinking level for the turn(s) following a tool error, then restore
147
- * it. Opt-in via `thinking_escalation.enabled` in hoo-config.json.
148
- *
149
- * Timing model (per agent run):
150
- * turn N: assistant calls tools → tool_execution_end(s) → turn_end
151
- * a failing tool here escalates the level used by turn N+1
152
- * turn N+1: runs escalated; its turn_end decrements the cooldown and, at zero,
153
- * restores the captured baseline so turn N+2 is fast again
154
- *
155
- * `escalatedThisTurn` ensures the error turn's own `turn_end` does not consume a
156
- * cooldown step — the cooldown counts the turns *after* the failure.
157
- */
158
- export declare function setupThinkingEscalation(pi: ExtensionAPI): void;
159
- /**
160
- * `/loop` schedules prompts via cron and drives autonomous continuation. The same
161
- * scheduler backs the agent-callable CronCreate/CronList/CronDelete tools.
162
- *
163
- * /loop "<cron>" <prompt> schedule recurring (5-field cron, local time)
164
- * /loop <5m|2h|1d> <prompt> schedule recurring at a simple interval
165
- * /loop once "<cron>" <prompt> schedule a one-shot
166
- * /loop list | /loop delete <id> | /loop stop
167
- * /loop auto [--max-turns N] <task> keep iterating until the task says LOOP_DONE
168
- */
169
- export declare function setupLoop(pi: ExtensionAPI): void;
170
- /**
171
- * `/plugin` installs plugins from marketplaces (a git repo or local dir with a
172
- * Claude `.claude-plugin/marketplace.json` or Copilot-style `.github/marketplace.json`
173
- * index). Installed plugins are placed in `.hoocode/plugins/<name>` and loaded by the
174
- * plugin loader after a reload.
175
- *
176
- * /plugin marketplace add <git-url|path>
177
- * /plugin marketplace list
178
- * /plugin list list available plugins across marketplaces
179
- * /plugin install <name>
180
- * /plugin remove <name>
181
- */
182
- export declare function setupMarketplace(pi: ExtensionAPI): void;
183
20
  declare function hooCore(pi: ExtensionAPI): void;
184
21
  declare namespace hooCore {
185
22
  var displayName: string;