@within-7/minto 0.3.10 → 0.4.0

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 (306) hide show
  1. package/dist/Tool.js.map +2 -2
  2. package/dist/commands/agents/AgentsCommand.js +2 -2
  3. package/dist/commands/agents/AgentsCommand.js.map +2 -2
  4. package/dist/commands/ctx_viz.js +1 -1
  5. package/dist/commands/effort.js +87 -0
  6. package/dist/commands/effort.js.map +7 -0
  7. package/dist/commands/export.js +19 -9
  8. package/dist/commands/export.js.map +2 -2
  9. package/dist/commands/ide.js +18 -0
  10. package/dist/commands/ide.js.map +7 -0
  11. package/dist/commands/mcp-interactive.js +14 -8
  12. package/dist/commands/mcp-interactive.js.map +2 -2
  13. package/dist/commands/memory.js +168 -0
  14. package/dist/commands/memory.js.map +7 -0
  15. package/dist/commands/model.js +45 -2
  16. package/dist/commands/model.js.map +2 -2
  17. package/dist/commands/outputStyle.js +64 -0
  18. package/dist/commands/outputStyle.js.map +7 -0
  19. package/dist/commands/plugin/utils.js +33 -1
  20. package/dist/commands/plugin/utils.js.map +2 -2
  21. package/dist/commands/plugin.js +10 -1
  22. package/dist/commands/plugin.js.map +2 -2
  23. package/dist/commands/refreshCommands.js +2 -0
  24. package/dist/commands/refreshCommands.js.map +2 -2
  25. package/dist/commands/review.js +51 -0
  26. package/dist/commands/review.js.map +7 -0
  27. package/dist/commands/terminalSetup.js +6 -0
  28. package/dist/commands/terminalSetup.js.map +2 -2
  29. package/dist/commands/undo.js +8 -0
  30. package/dist/commands/undo.js.map +2 -2
  31. package/dist/commands/vim.js +22 -0
  32. package/dist/commands/vim.js.map +7 -0
  33. package/dist/commands.js +12 -0
  34. package/dist/commands.js.map +2 -2
  35. package/dist/components/HighlightedCode.js +1 -0
  36. package/dist/components/HighlightedCode.js.map +2 -2
  37. package/dist/components/ModelSelector/ModelSelector.js +250 -143
  38. package/dist/components/ModelSelector/ModelSelector.js.map +2 -2
  39. package/dist/components/PromptInput.js +21 -6
  40. package/dist/components/PromptInput.js.map +2 -2
  41. package/dist/components/PulseLabel.js +44 -0
  42. package/dist/components/PulseLabel.js.map +7 -0
  43. package/dist/components/RequestStatusIndicator.js +1 -1
  44. package/dist/components/RequestStatusIndicator.js.map +1 -1
  45. package/dist/components/Spinner.js +12 -42
  46. package/dist/components/Spinner.js.map +3 -3
  47. package/dist/components/StartupStatus.js +57 -0
  48. package/dist/components/StartupStatus.js.map +7 -0
  49. package/dist/components/SubagentBlock.js +43 -6
  50. package/dist/components/SubagentBlock.js.map +2 -2
  51. package/dist/components/TabbedListView/TabBar.js +13 -8
  52. package/dist/components/TabbedListView/TabBar.js.map +2 -2
  53. package/dist/components/TabbedListView/TabbedListView.js +1 -1
  54. package/dist/components/TabbedListView/TabbedListView.js.map +2 -2
  55. package/dist/components/TodoPanel.js +1 -1
  56. package/dist/components/TodoPanel.js.map +1 -1
  57. package/dist/components/ToolUseLoader.js +5 -0
  58. package/dist/components/ToolUseLoader.js.map +2 -2
  59. package/dist/components/TrustDialog.js +0 -2
  60. package/dist/components/TrustDialog.js.map +2 -2
  61. package/dist/components/messages/TaskInModuleView.js +1 -1
  62. package/dist/components/messages/TaskInModuleView.js.map +2 -2
  63. package/dist/components/messages/TaskToolMessage.js +1 -1
  64. package/dist/components/messages/TaskToolMessage.js.map +2 -2
  65. package/dist/components/messages/UserPromptMessage.js +6 -1
  66. package/dist/components/messages/UserPromptMessage.js.map +2 -2
  67. package/dist/constants/modelCapabilities.js +103 -18
  68. package/dist/constants/modelCapabilities.js.map +2 -2
  69. package/dist/constants/product.js +2 -0
  70. package/dist/constants/product.js.map +2 -2
  71. package/dist/constants/prompts/agentPrompt.js +30 -0
  72. package/dist/constants/prompts/agentPrompt.js.map +7 -0
  73. package/dist/constants/prompts/codeConventions.js +27 -0
  74. package/dist/constants/prompts/codeConventions.js.map +7 -0
  75. package/dist/constants/prompts/doingTasks.js +15 -0
  76. package/dist/constants/prompts/doingTasks.js.map +7 -0
  77. package/dist/constants/prompts/envInfo.js +17 -0
  78. package/dist/constants/prompts/envInfo.js.map +7 -0
  79. package/dist/constants/prompts/executingWithCare.js +17 -0
  80. package/dist/constants/prompts/executingWithCare.js.map +7 -0
  81. package/dist/constants/prompts/identity.js +10 -0
  82. package/dist/constants/prompts/identity.js.map +7 -0
  83. package/dist/constants/prompts/index.js +78 -0
  84. package/dist/constants/prompts/index.js.map +7 -0
  85. package/dist/constants/prompts/taskManagement.js +60 -0
  86. package/dist/constants/prompts/taskManagement.js.map +7 -0
  87. package/dist/constants/prompts/toneAndStyle.js +62 -0
  88. package/dist/constants/prompts/toneAndStyle.js.map +7 -0
  89. package/dist/constants/prompts/toolUsagePolicy.js +38 -0
  90. package/dist/constants/prompts/toolUsagePolicy.js.map +7 -0
  91. package/dist/constants/prompts.js +5 -176
  92. package/dist/constants/prompts.js.map +2 -2
  93. package/dist/constants/providerRegistry.js +235 -0
  94. package/dist/constants/providerRegistry.js.map +7 -0
  95. package/dist/constants/providers.js +35 -0
  96. package/dist/constants/providers.js.map +7 -0
  97. package/dist/context/PermissionContext.js +0 -1
  98. package/dist/context/PermissionContext.js.map +2 -2
  99. package/dist/context.js +87 -31
  100. package/dist/context.js.map +2 -2
  101. package/dist/core/backupHook.js +2 -2
  102. package/dist/core/backupHook.js.map +2 -2
  103. package/dist/core/config/defaults.js +4 -1
  104. package/dist/core/config/defaults.js.map +2 -2
  105. package/dist/core/config/schema.js +7 -1
  106. package/dist/core/config/schema.js.map +2 -2
  107. package/dist/core/costTracker.js +18 -0
  108. package/dist/core/costTracker.js.map +2 -2
  109. package/dist/core/index.js +0 -1
  110. package/dist/core/index.js.map +2 -2
  111. package/dist/core/tokenStatsManager.js +22 -4
  112. package/dist/core/tokenStatsManager.js.map +2 -2
  113. package/dist/entrypoints/cli.js +65 -84
  114. package/dist/entrypoints/cli.js.map +2 -2
  115. package/dist/hooks/useAgentTokenStats.js +1 -1
  116. package/dist/hooks/useAgentTokenStats.js.map +2 -2
  117. package/dist/hooks/useAgentTranscripts.js +2 -1
  118. package/dist/hooks/useAgentTranscripts.js.map +2 -2
  119. package/dist/hooks/useBackgroundShells.js +29 -0
  120. package/dist/hooks/useBackgroundShells.js.map +7 -0
  121. package/dist/hooks/useCanUseTool.js +1 -1
  122. package/dist/hooks/useCanUseTool.js.map +2 -2
  123. package/dist/hooks/useDeferredLoading.js +64 -0
  124. package/dist/hooks/useDeferredLoading.js.map +7 -0
  125. package/dist/hooks/useHookStatus.js +1 -1
  126. package/dist/hooks/useHookStatus.js.map +2 -2
  127. package/dist/hooks/useSessionTracking.js +55 -0
  128. package/dist/hooks/useSessionTracking.js.map +7 -0
  129. package/dist/hooks/useTerminalSize.js +21 -0
  130. package/dist/hooks/useTerminalSize.js.map +2 -2
  131. package/dist/hooks/useTextInput.js +1 -0
  132. package/dist/hooks/useTextInput.js.map +2 -2
  133. package/dist/hooks/useUnifiedCompletion.js +3 -2
  134. package/dist/hooks/useUnifiedCompletion.js.map +2 -2
  135. package/dist/i18n/locales/en.js +8 -9
  136. package/dist/i18n/locales/en.js.map +2 -2
  137. package/dist/i18n/locales/zh-CN.js +8 -9
  138. package/dist/i18n/locales/zh-CN.js.map +2 -2
  139. package/dist/i18n/types.js.map +1 -1
  140. package/dist/messages.js +41 -17
  141. package/dist/messages.js.map +2 -2
  142. package/dist/permissions.js +94 -1
  143. package/dist/permissions.js.map +2 -2
  144. package/dist/query.js +27 -19
  145. package/dist/query.js.map +2 -2
  146. package/dist/screens/REPL.js +83 -74
  147. package/dist/screens/REPL.js.map +2 -2
  148. package/dist/services/adapters/responsesAPI.js +6 -0
  149. package/dist/services/adapters/responsesAPI.js.map +2 -2
  150. package/dist/services/agentTeams/index.js +35 -0
  151. package/dist/services/agentTeams/index.js.map +7 -0
  152. package/dist/services/agentTeams/mailbox.js +114 -0
  153. package/dist/services/agentTeams/mailbox.js.map +7 -0
  154. package/dist/services/agentTeams/teamManager.js +149 -0
  155. package/dist/services/agentTeams/teamManager.js.map +7 -0
  156. package/dist/services/agentTeams/teamTaskStore.js +114 -0
  157. package/dist/services/agentTeams/teamTaskStore.js.map +7 -0
  158. package/dist/services/agentTeams/teammateSpawner.js +80 -0
  159. package/dist/services/agentTeams/teammateSpawner.js.map +7 -0
  160. package/dist/services/checkpointManager.js +16 -3
  161. package/dist/services/checkpointManager.js.map +2 -2
  162. package/dist/services/claude.js +19 -1728
  163. package/dist/services/claude.js.map +3 -3
  164. package/dist/services/gpt5ConnectionTest.js +4 -2
  165. package/dist/services/gpt5ConnectionTest.js.map +2 -2
  166. package/dist/services/hookExecutor.js +411 -127
  167. package/dist/services/hookExecutor.js.map +2 -2
  168. package/dist/services/llm/anthropicProvider.js +807 -0
  169. package/dist/services/llm/anthropicProvider.js.map +7 -0
  170. package/dist/services/llm/dispatch.js +218 -0
  171. package/dist/services/llm/dispatch.js.map +7 -0
  172. package/dist/services/llm/index.js +44 -0
  173. package/dist/services/llm/index.js.map +7 -0
  174. package/dist/services/llm/mintoContext.js +69 -0
  175. package/dist/services/llm/mintoContext.js.map +7 -0
  176. package/dist/services/llm/openaiProvider.js +622 -0
  177. package/dist/services/llm/openaiProvider.js.map +7 -0
  178. package/dist/services/llm/types.js +157 -0
  179. package/dist/services/llm/types.js.map +7 -0
  180. package/dist/services/mcpClient.js +183 -33
  181. package/dist/services/mcpClient.js.map +2 -2
  182. package/dist/services/notifier.js +14 -0
  183. package/dist/services/notifier.js.map +2 -2
  184. package/dist/services/oauth.js +4 -2
  185. package/dist/services/oauth.js.map +2 -2
  186. package/dist/services/openai.js +66 -56
  187. package/dist/services/openai.js.map +3 -3
  188. package/dist/services/outputStyles.js +102 -21
  189. package/dist/services/outputStyles.js.map +2 -2
  190. package/dist/services/plugins/skillMarketplace.js +4 -1
  191. package/dist/services/plugins/skillMarketplace.js.map +2 -2
  192. package/dist/services/sentry.js +1 -1
  193. package/dist/services/sentry.js.map +2 -2
  194. package/dist/services/sessionMemory.js +16 -3
  195. package/dist/services/sessionMemory.js.map +2 -2
  196. package/dist/services/systemReminder.js +350 -3
  197. package/dist/services/systemReminder.js.map +2 -2
  198. package/dist/services/taskStore.js +19 -0
  199. package/dist/services/taskStore.js.map +2 -2
  200. package/dist/tools/ArchitectTool/ArchitectTool.js.map +1 -1
  201. package/dist/tools/AskUserQuestionTool/AskUserQuestionTool.js.map +1 -1
  202. package/dist/tools/BashOutputTool/BashOutputTool.js.map +1 -1
  203. package/dist/tools/BashTool/BashTool.js +28 -0
  204. package/dist/tools/BashTool/BashTool.js.map +2 -2
  205. package/dist/tools/FileEditTool/FileEditTool.js +1 -1
  206. package/dist/tools/FileEditTool/FileEditTool.js.map +2 -2
  207. package/dist/tools/FileReadTool/FileReadTool.js +14 -0
  208. package/dist/tools/FileReadTool/FileReadTool.js.map +2 -2
  209. package/dist/tools/FileWriteTool/FileWriteTool.js +3 -1
  210. package/dist/tools/FileWriteTool/FileWriteTool.js.map +2 -2
  211. package/dist/tools/GlobTool/GlobTool.js.map +1 -1
  212. package/dist/tools/GrepTool/GrepTool.js.map +1 -1
  213. package/dist/tools/KillShellTool/KillShellTool.js.map +1 -1
  214. package/dist/tools/ListMcpResourcesTool/ListMcpResourcesTool.js.map +2 -2
  215. package/dist/tools/LspTool/LspTool.js +11 -2
  216. package/dist/tools/LspTool/LspTool.js.map +2 -2
  217. package/dist/tools/MCPTool/MCPTool.js.map +1 -1
  218. package/dist/tools/MemoryReadTool/MemoryReadTool.js +2 -1
  219. package/dist/tools/MemoryReadTool/MemoryReadTool.js.map +2 -2
  220. package/dist/tools/MemoryWriteTool/MemoryWriteTool.js +2 -1
  221. package/dist/tools/MemoryWriteTool/MemoryWriteTool.js.map +2 -2
  222. package/dist/tools/MultiEditTool/MultiEditTool.js.map +1 -1
  223. package/dist/tools/NotebookEditTool/NotebookEditTool.js.map +1 -1
  224. package/dist/tools/NotebookReadTool/NotebookReadTool.js.map +1 -1
  225. package/dist/tools/PlanModeTool/EnterPlanModeTool.js +8 -2
  226. package/dist/tools/PlanModeTool/EnterPlanModeTool.js.map +2 -2
  227. package/dist/tools/PlanModeTool/ExitPlanModeTool.js +2 -0
  228. package/dist/tools/PlanModeTool/ExitPlanModeTool.js.map +2 -2
  229. package/dist/tools/ReadMcpResourceTool/ReadMcpResourceTool.js.map +1 -1
  230. package/dist/tools/SlashCommandTool/SlashCommandTool.js +174 -18
  231. package/dist/tools/SlashCommandTool/SlashCommandTool.js.map +3 -3
  232. package/dist/tools/TaskCreateTool/TaskCreateTool.js.map +1 -1
  233. package/dist/tools/TaskGetTool/TaskGetTool.js.map +1 -1
  234. package/dist/tools/TaskListTool/TaskListTool.js.map +1 -1
  235. package/dist/tools/TaskOutputTool/TaskOutputTool.js.map +1 -1
  236. package/dist/tools/TaskStopTool/TaskStopTool.js.map +1 -1
  237. package/dist/tools/TaskTool/TaskTool.js +75 -5
  238. package/dist/tools/TaskTool/TaskTool.js.map +2 -2
  239. package/dist/tools/TaskTool/prompt.js +12 -6
  240. package/dist/tools/TaskTool/prompt.js.map +2 -2
  241. package/dist/tools/TaskUpdateTool/TaskUpdateTool.js.map +1 -1
  242. package/dist/tools/ThinkTool/ThinkTool.js.map +1 -1
  243. package/dist/tools/TodoWriteTool/TodoWriteTool.js.map +1 -1
  244. package/dist/tools/URLFetcherTool/URLFetcherTool.js.map +1 -1
  245. package/dist/tools/WebSearchTool/WebSearchTool.js.map +1 -1
  246. package/dist/tools/WebSearchTool/searchProviders.js +2 -1
  247. package/dist/tools/WebSearchTool/searchProviders.js.map +2 -2
  248. package/dist/tools/lsTool/lsTool.js.map +2 -2
  249. package/dist/tools/lsTool/prompt.js.map +1 -1
  250. package/dist/tools.js +14 -3
  251. package/dist/tools.js.map +2 -2
  252. package/dist/types/PermissionMode.js +21 -1
  253. package/dist/types/PermissionMode.js.map +2 -2
  254. package/dist/types/agentTeams.js +1 -0
  255. package/dist/types/agentTeams.js.map +7 -0
  256. package/dist/types/hooks.js +8 -2
  257. package/dist/types/hooks.js.map +2 -2
  258. package/dist/types/plugin.js +1 -1
  259. package/dist/types/plugin.js.map +2 -2
  260. package/dist/utils/agentLoader.js +25 -3
  261. package/dist/utils/agentLoader.js.map +2 -2
  262. package/dist/utils/animationManager.js +1 -1
  263. package/dist/utils/animationManager.js.map +2 -2
  264. package/dist/utils/ask.js +1 -1
  265. package/dist/utils/async.js +5 -1
  266. package/dist/utils/async.js.map +2 -2
  267. package/dist/utils/autoCompactCore.js +60 -0
  268. package/dist/utils/autoCompactCore.js.map +2 -2
  269. package/dist/utils/config.js +26 -128
  270. package/dist/utils/config.js.map +2 -2
  271. package/dist/utils/configSchema.js +227 -0
  272. package/dist/utils/configSchema.js.map +7 -0
  273. package/dist/utils/debugLogger.js.map +2 -2
  274. package/dist/utils/env.js +4 -3
  275. package/dist/utils/env.js.map +2 -2
  276. package/dist/utils/envConfig.js +34 -0
  277. package/dist/utils/envConfig.js.map +3 -3
  278. package/dist/utils/gpt5.js +146 -0
  279. package/dist/utils/gpt5.js.map +7 -0
  280. package/dist/utils/hookManager.js +374 -140
  281. package/dist/utils/hookManager.js.map +2 -2
  282. package/dist/utils/markdown.js +47 -0
  283. package/dist/utils/markdown.js.map +2 -2
  284. package/dist/utils/memoizeWithTTL.js +25 -0
  285. package/dist/utils/memoizeWithTTL.js.map +7 -0
  286. package/dist/utils/model.js +34 -9
  287. package/dist/utils/model.js.map +2 -2
  288. package/dist/utils/pluginInstaller.js +34 -5
  289. package/dist/utils/pluginInstaller.js.map +2 -2
  290. package/dist/utils/pluginLoader.js +201 -32
  291. package/dist/utils/pluginLoader.js.map +2 -2
  292. package/dist/utils/safeFetch.js +45 -0
  293. package/dist/utils/safeFetch.js.map +7 -0
  294. package/dist/utils/skillLoader.js +59 -6
  295. package/dist/utils/skillLoader.js.map +2 -2
  296. package/dist/utils/streamingState.js +52 -0
  297. package/dist/utils/streamingState.js.map +7 -0
  298. package/dist/utils/style.js +6 -3
  299. package/dist/utils/style.js.map +2 -2
  300. package/dist/utils/teamConfig.js +9 -3
  301. package/dist/utils/teamConfig.js.map +2 -2
  302. package/dist/utils/toolRiskClassification.js +0 -6
  303. package/dist/utils/toolRiskClassification.js.map +2 -2
  304. package/dist/version.js +2 -2
  305. package/dist/version.js.map +1 -1
  306. package/package.json +2 -1
@@ -1,27 +1,80 @@
1
- import { HookEvent } from "../types/hooks.js";
1
+ import { existsSync, readFileSync } from "fs";
2
+ import { join } from "path";
3
+ import { homedir } from "os";
4
+ import { HookEvent, HooksConfigSchema } from "../types/hooks.js";
5
+ import { emitReminderEvent } from "../services/systemReminder.js";
2
6
  import {
3
7
  executeHooksForEvent,
4
8
  processHookDecisions,
5
9
  createPreToolUseInput,
6
10
  createPostToolUseInput,
11
+ createPostToolUseFailureInput,
12
+ createPermissionRequestInput,
7
13
  createUserPromptSubmitInput,
8
14
  createSessionStartInput,
9
- createSessionEndInput
15
+ createSessionEndInput,
16
+ createStopInput,
17
+ createSubagentStartInput,
18
+ createSubagentStopInput,
19
+ createNotificationInput,
20
+ createPreCompactInput,
21
+ createPostCompactInput,
22
+ createConfigChangeInput,
23
+ createTaskCompletedInput,
24
+ createTeammateIdleInput,
25
+ resetOnceHooks
10
26
  } from "../services/hookExecutor.js";
27
+ import { safeParseJSON } from "./json.js";
28
+ import { getCwd } from "./state.js";
11
29
  const logInfo = (msg) => {
12
30
  if (process.env.DEBUG) console.log(`[INFO] ${msg}`);
13
31
  };
14
32
  const logDebug = (msg, ...args) => {
15
33
  if (process.env.DEBUG) console.log(`[DEBUG] ${msg}`, ...args);
16
34
  };
35
+ function matchHook(hook, toolName, toolInput) {
36
+ if (hook.matcher) {
37
+ if (hook.matcher !== "*" && hook.matcher !== "") {
38
+ try {
39
+ const regex = new RegExp(hook.matcher);
40
+ if (!regex.test(toolName)) return false;
41
+ } catch {
42
+ if (hook.matcher !== toolName) return false;
43
+ }
44
+ }
45
+ }
46
+ const hookConfig = hook.config;
47
+ if (hookConfig.toolInput && toolInput) {
48
+ for (const [field, pattern] of Object.entries(
49
+ hookConfig.toolInput
50
+ )) {
51
+ const fieldValue = String(toolInput[field] ?? "");
52
+ try {
53
+ const regex = new RegExp(pattern);
54
+ if (!regex.test(fieldValue)) return false;
55
+ } catch {
56
+ if (fieldValue !== pattern) return false;
57
+ }
58
+ }
59
+ }
60
+ return true;
61
+ }
62
+ const ALLOW_RESULT = {
63
+ shouldContinue: true,
64
+ shouldAskUser: false
65
+ };
17
66
  class HookManager {
18
67
  plugins = [];
68
+ settingsHooks = [];
69
+ managedHooks = [];
19
70
  sessionId;
20
71
  transcriptPath;
21
72
  enabled = true;
73
+ disableAllHooks = false;
22
74
  constructor(sessionId, transcriptPath) {
23
75
  this.sessionId = sessionId;
24
76
  this.transcriptPath = transcriptPath;
77
+ resetOnceHooks();
25
78
  }
26
79
  /**
27
80
  * Register plugins with hooks
@@ -33,6 +86,94 @@ class HookManager {
33
86
  `HookManager: Registered ${plugins.length} plugin(s) with ${totalHooks} hook(s)`
34
87
  );
35
88
  }
89
+ /**
90
+ * Load hooks from settings.json files
91
+ * Priority (high to low): managed > .minto/settings.json > .claude/settings.json >
92
+ * .minto/settings.local.json > ~/.minto/settings.json > ~/.claude/settings.json
93
+ */
94
+ loadSettingsHooks() {
95
+ const cwd = getCwd();
96
+ const home = homedir();
97
+ const settingsPaths = [
98
+ join(home, ".claude", "settings.json"),
99
+ join(home, ".minto", "settings.json"),
100
+ join(cwd, ".minto", "settings.local.json"),
101
+ join(cwd, ".claude", "settings.json"),
102
+ join(cwd, ".minto", "settings.json")
103
+ ];
104
+ const allHooks = [];
105
+ for (const settingsPath of settingsPaths) {
106
+ try {
107
+ if (!existsSync(settingsPath)) continue;
108
+ const content = readFileSync(settingsPath, "utf-8");
109
+ const settings = safeParseJSON(content);
110
+ if (!settings || typeof settings !== "object") continue;
111
+ const hooksConfig = settings.hooks;
112
+ if (!hooksConfig || typeof hooksConfig !== "object") continue;
113
+ const parsed = HooksConfigSchema.safeParse({ hooks: hooksConfig });
114
+ if (!parsed.success) {
115
+ logDebug(`Invalid hooks in ${settingsPath}: ${parsed.error.message}`);
116
+ continue;
117
+ }
118
+ const hooks = parsed.data.hooks;
119
+ if (!hooks) continue;
120
+ for (const [eventName, matchers] of Object.entries(hooks)) {
121
+ const event = eventName;
122
+ for (const matcher of matchers) {
123
+ for (let i = 0; i < matcher.hooks.length; i++) {
124
+ const hookDef = matcher.hooks[i];
125
+ allHooks.push({
126
+ name: `settings:${eventName}:${i}`,
127
+ filePath: settingsPath,
128
+ config: {
129
+ event,
130
+ matcher: matcher.matcher,
131
+ type: hookDef.type,
132
+ command: hookDef.command,
133
+ timeout: hookDef.timeout,
134
+ once: hookDef.once,
135
+ async: hookDef.async
136
+ },
137
+ pluginName: "settings",
138
+ event,
139
+ matcher: matcher.matcher
140
+ });
141
+ }
142
+ }
143
+ }
144
+ } catch (err) {
145
+ logDebug(`Error loading hooks from ${settingsPath}: ${err}`);
146
+ }
147
+ }
148
+ this.settingsHooks = allHooks;
149
+ try {
150
+ const localSettings = join(cwd, ".minto", "settings.json");
151
+ const globalSettings = join(home, ".minto", "settings.json");
152
+ for (const p of [localSettings, globalSettings]) {
153
+ if (existsSync(p)) {
154
+ const content = safeParseJSON(readFileSync(p, "utf-8"));
155
+ if (content && content.disableAllHooks) {
156
+ this.disableAllHooks = true;
157
+ break;
158
+ }
159
+ }
160
+ }
161
+ } catch {
162
+ }
163
+ logInfo(`HookManager: Loaded ${allHooks.length} hook(s) from settings`);
164
+ }
165
+ /**
166
+ * Dynamically add temporary hooks (e.g., skill-level hooks)
167
+ * Returns a cleanup function to remove them
168
+ */
169
+ addHooks(hooks) {
170
+ this.settingsHooks.push(...hooks);
171
+ logDebug(`HookManager: Added ${hooks.length} temporary hook(s)`);
172
+ return () => {
173
+ this.settingsHooks = this.settingsHooks.filter((h) => !hooks.includes(h));
174
+ logDebug(`HookManager: Removed ${hooks.length} temporary hook(s)`);
175
+ };
176
+ }
36
177
  /**
37
178
  * Enable or disable hook execution
38
179
  */
@@ -41,80 +182,117 @@ class HookManager {
41
182
  logDebug(`HookManager: ${enabled ? "Enabled" : "Disabled"}`);
42
183
  }
43
184
  /**
44
- * Execute PreToolUse hooks
185
+ * Get all hooks (plugins + settings), respecting disableAllHooks
45
186
  */
46
- async executePreToolUse(toolName, toolInput) {
47
- if (!this.enabled) {
48
- return { shouldContinue: true, shouldAskUser: false };
187
+ getAllHooks() {
188
+ const managed = this.managedHooks;
189
+ if (this.disableAllHooks) {
190
+ return managed;
49
191
  }
50
- const allHooks = this.plugins.flatMap((p) => p.hooks);
51
- const matchingHooks = allHooks.filter((hook) => {
52
- if (hook.event !== HookEvent.PreToolUse) return false;
53
- if (!hook.matcher) return true;
54
- if (hook.matcher === "*" || hook.matcher === "") return true;
55
- try {
56
- const regex = new RegExp(hook.matcher);
57
- return regex.test(toolName);
58
- } catch {
59
- return hook.matcher === toolName;
60
- }
192
+ const pluginHooks = this.plugins.flatMap((p) => p.hooks);
193
+ return [...pluginHooks, ...this.settingsHooks, ...managed];
194
+ }
195
+ /**
196
+ * Get matching hooks for a tool-based event
197
+ */
198
+ getMatchingToolHooks(event, toolName, toolInput) {
199
+ const allHooks = this.getAllHooks();
200
+ const matching = allHooks.filter((hook) => {
201
+ if (hook.event !== event) return false;
202
+ return matchHook(hook, toolName, toolInput);
61
203
  });
62
- if (matchingHooks.length === 0) {
63
- return { shouldContinue: true, shouldAskUser: false };
64
- }
65
- logDebug(
66
- `PreToolUse: ${matchingHooks.length} hook(s) matched for tool: ${toolName}`
67
- );
68
- const input = createPreToolUseInput(
69
- this.sessionId,
70
- this.transcriptPath,
71
- toolName,
72
- toolInput
73
- );
204
+ return { hooks: matching, plugins: this.plugins };
205
+ }
206
+ /**
207
+ * Get matching hooks for a non-tool event
208
+ */
209
+ getMatchingEventHooks(event) {
210
+ return this.getAllHooks().filter((hook) => hook.event === event);
211
+ }
212
+ /**
213
+ * Find plugin root for a hook
214
+ */
215
+ getPluginRoot(hook) {
216
+ const plugin = this.plugins.find((p) => p.manifest.name === hook.pluginName);
217
+ return plugin?.location || getCwd();
218
+ }
219
+ /**
220
+ * Execute hooks for a tool-based event (PreToolUse, PostToolUse, etc.)
221
+ */
222
+ async executeToolEvent(event, toolName, input, toolInput) {
223
+ if (!this.enabled) return ALLOW_RESULT;
224
+ const { hooks } = this.getMatchingToolHooks(event, toolName, toolInput);
225
+ if (hooks.length === 0) return ALLOW_RESULT;
226
+ logDebug(`${event}: ${hooks.length} hook(s) matched for tool: ${toolName}`);
74
227
  const results = [];
75
- for (const hook of matchingHooks) {
76
- const plugin = this.plugins.find((p) => p.manifest.name === hook.pluginName);
77
- if (!plugin) continue;
228
+ for (const hook of hooks) {
78
229
  const options = {
79
230
  sessionId: this.sessionId,
80
231
  transcriptPath: this.transcriptPath,
81
- pluginRoot: plugin.location
232
+ pluginRoot: this.getPluginRoot(hook)
82
233
  };
83
- const result = await executeHooksForEvent(
84
- HookEvent.PreToolUse,
85
- [hook],
86
- input,
87
- options
88
- );
234
+ const result = await executeHooksForEvent(event, [hook], input, options);
89
235
  results.push(...result);
90
236
  }
91
237
  return processHookDecisions(results);
92
238
  }
93
239
  /**
94
- * Execute PostToolUse hooks
240
+ * Execute hooks for a non-tool event (fire-and-forget)
95
241
  */
96
- async executePostToolUse(toolName, toolInput, toolOutput) {
97
- if (!this.enabled) {
98
- return;
99
- }
100
- const allHooks = this.plugins.flatMap((p) => p.hooks);
101
- const matchingHooks = allHooks.filter((hook) => {
102
- if (hook.event !== HookEvent.PostToolUse) return false;
103
- if (!hook.matcher) return true;
104
- if (hook.matcher === "*" || hook.matcher === "") return true;
105
- try {
106
- const regex = new RegExp(hook.matcher);
107
- return regex.test(toolName);
108
- } catch {
109
- return hook.matcher === toolName;
110
- }
111
- });
112
- if (matchingHooks.length === 0) {
113
- return;
242
+ async executeFireAndForget(event, input) {
243
+ if (!this.enabled) return;
244
+ const hooks = this.getMatchingEventHooks(event);
245
+ if (hooks.length === 0) return;
246
+ logDebug(`${event}: ${hooks.length} hook(s)`);
247
+ await Promise.allSettled(
248
+ hooks.map((hook) => {
249
+ const options = {
250
+ sessionId: this.sessionId,
251
+ transcriptPath: this.transcriptPath,
252
+ pluginRoot: this.getPluginRoot(hook)
253
+ };
254
+ return executeHooksForEvent(event, [hook], input, options);
255
+ })
256
+ );
257
+ }
258
+ /**
259
+ * Execute hooks for a blockable non-tool event
260
+ */
261
+ async executeBlockableEvent(event, input) {
262
+ if (!this.enabled) return ALLOW_RESULT;
263
+ const hooks = this.getMatchingEventHooks(event);
264
+ if (hooks.length === 0) return ALLOW_RESULT;
265
+ logDebug(`${event}: ${hooks.length} hook(s)`);
266
+ const results = [];
267
+ for (const hook of hooks) {
268
+ const options = {
269
+ sessionId: this.sessionId,
270
+ transcriptPath: this.transcriptPath,
271
+ pluginRoot: this.getPluginRoot(hook)
272
+ };
273
+ const result = await executeHooksForEvent(event, [hook], input, options);
274
+ results.push(...result);
114
275
  }
115
- logDebug(
116
- `PostToolUse: ${matchingHooks.length} hook(s) matched for tool: ${toolName}`
276
+ return processHookDecisions(results);
277
+ }
278
+ // ========================================================================
279
+ // Public event methods
280
+ // ========================================================================
281
+ async executePreToolUse(toolName, toolInput) {
282
+ const input = createPreToolUseInput(
283
+ this.sessionId,
284
+ this.transcriptPath,
285
+ toolName,
286
+ toolInput
287
+ );
288
+ return this.executeToolEvent(
289
+ HookEvent.PreToolUse,
290
+ toolName,
291
+ input,
292
+ toolInput
117
293
  );
294
+ }
295
+ async executePostToolUse(toolName, toolInput, toolOutput) {
118
296
  const input = createPostToolUseInput(
119
297
  this.sessionId,
120
298
  this.transcriptPath,
@@ -122,119 +300,175 @@ class HookManager {
122
300
  toolInput,
123
301
  toolOutput
124
302
  );
125
- for (const hook of matchingHooks) {
126
- const plugin = this.plugins.find((p) => p.manifest.name === hook.pluginName);
127
- if (!plugin) continue;
128
- const options = {
129
- sessionId: this.sessionId,
130
- transcriptPath: this.transcriptPath,
131
- pluginRoot: plugin.location
132
- };
133
- await executeHooksForEvent(HookEvent.PostToolUse, [hook], input, options);
134
- }
303
+ await this.executeFireAndForget(HookEvent.PostToolUse, input);
304
+ }
305
+ async executePostToolUseFailure(toolName, toolInput, error, errorType) {
306
+ const input = createPostToolUseFailureInput(
307
+ this.sessionId,
308
+ this.transcriptPath,
309
+ toolName,
310
+ toolInput,
311
+ error,
312
+ errorType
313
+ );
314
+ await this.executeFireAndForget(HookEvent.PostToolUseFailure, input);
315
+ emitReminderEvent("diagnostics:new", {
316
+ source: toolName,
317
+ count: 1
318
+ });
319
+ }
320
+ async executePermissionRequest(toolName, toolInput, permissionType, description) {
321
+ const input = createPermissionRequestInput(
322
+ this.sessionId,
323
+ this.transcriptPath,
324
+ toolName,
325
+ toolInput,
326
+ permissionType,
327
+ description
328
+ );
329
+ return this.executeBlockableEvent(HookEvent.PermissionRequest, input);
135
330
  }
136
- /**
137
- * Execute UserPromptSubmit hooks
138
- */
139
331
  async executeUserPromptSubmit(userPrompt) {
140
- if (!this.enabled) {
141
- return { shouldContinue: true, shouldAskUser: false };
332
+ const input = createUserPromptSubmitInput(
333
+ this.sessionId,
334
+ this.transcriptPath,
335
+ userPrompt
336
+ );
337
+ return this.executeBlockableEvent(HookEvent.UserPromptSubmit, input);
338
+ }
339
+ async executeSessionStart() {
340
+ const input = createSessionStartInput(this.sessionId, this.transcriptPath);
341
+ await this.executeFireAndForget(HookEvent.SessionStart, input);
342
+ }
343
+ async executeSessionEnd(reason = "other") {
344
+ const input = createSessionEndInput(
345
+ this.sessionId,
346
+ this.transcriptPath,
347
+ reason
348
+ );
349
+ await this.executeFireAndForget(HookEvent.SessionEnd, input);
350
+ }
351
+ async executeStop(lastAssistantMessage) {
352
+ const input = createStopInput(this.sessionId, this.transcriptPath, true);
353
+ if (lastAssistantMessage) {
354
+ ;
355
+ input.last_assistant_message = lastAssistantMessage;
142
356
  }
143
- const allHooks = this.plugins.flatMap((p) => p.hooks);
144
- const matchingHooks = allHooks.filter(
145
- (hook) => hook.event === HookEvent.UserPromptSubmit
357
+ return this.executeBlockableEvent(HookEvent.Stop, input);
358
+ }
359
+ async executeSubagentStart(agentType, agentDescription, runInBackground) {
360
+ const input = createSubagentStartInput(
361
+ this.sessionId,
362
+ this.transcriptPath,
363
+ agentType,
364
+ agentDescription,
365
+ runInBackground
366
+ );
367
+ await this.executeFireAndForget(HookEvent.SubagentStart, input);
368
+ }
369
+ async executeSubagentStop(agentType, agentId, transcriptPath, lastMessage) {
370
+ const input = createSubagentStopInput(
371
+ this.sessionId,
372
+ this.transcriptPath,
373
+ agentType,
374
+ agentId
146
375
  );
147
- if (matchingHooks.length === 0) {
148
- return { shouldContinue: true, shouldAskUser: false };
376
+ if (transcriptPath) {
377
+ ;
378
+ input.agent_transcript_path = transcriptPath;
149
379
  }
150
- logDebug(`UserPromptSubmit: ${matchingHooks.length} hook(s)`);
151
- const input = createUserPromptSubmitInput(
380
+ if (lastMessage) {
381
+ ;
382
+ input.last_message = lastMessage;
383
+ }
384
+ return this.executeBlockableEvent(HookEvent.SubagentStop, input);
385
+ }
386
+ async executeNotification(message, title, type) {
387
+ if (!this.enabled) return {};
388
+ const input = createNotificationInput(
152
389
  this.sessionId,
153
390
  this.transcriptPath,
154
- userPrompt
391
+ message,
392
+ title,
393
+ type
155
394
  );
395
+ const hooks = this.getMatchingEventHooks(HookEvent.Notification);
396
+ if (hooks.length === 0) return {};
156
397
  const results = [];
157
- for (const hook of matchingHooks) {
158
- const plugin = this.plugins.find((p) => p.manifest.name === hook.pluginName);
159
- if (!plugin) continue;
398
+ for (const hook of hooks) {
160
399
  const options = {
161
400
  sessionId: this.sessionId,
162
401
  transcriptPath: this.transcriptPath,
163
- pluginRoot: plugin.location
402
+ pluginRoot: this.getPluginRoot(hook)
164
403
  };
165
404
  const result = await executeHooksForEvent(
166
- HookEvent.UserPromptSubmit,
405
+ HookEvent.Notification,
167
406
  [hook],
168
407
  input,
169
408
  options
170
409
  );
171
410
  results.push(...result);
172
411
  }
173
- return processHookDecisions(results);
412
+ const suppress = results.some(
413
+ (r) => r.success && r.output?.suppressNotification
414
+ );
415
+ return { suppressNotification: suppress };
174
416
  }
175
- /**
176
- * Execute SessionStart hooks
177
- */
178
- async executeSessionStart() {
179
- if (!this.enabled) {
180
- return;
181
- }
182
- const allHooks = this.plugins.flatMap((p) => p.hooks);
183
- const matchingHooks = allHooks.filter(
184
- (hook) => hook.event === HookEvent.SessionStart
417
+ async executePreCompact(trigger, customInstructions) {
418
+ const input = createPreCompactInput(
419
+ this.sessionId,
420
+ this.transcriptPath,
421
+ trigger,
422
+ customInstructions || ""
185
423
  );
186
- if (matchingHooks.length === 0) {
187
- return;
188
- }
189
- logInfo(`SessionStart: Executing ${matchingHooks.length} hook(s)`);
190
- const input = createSessionStartInput(this.sessionId, this.transcriptPath);
191
- for (const hook of matchingHooks) {
192
- const plugin = this.plugins.find((p) => p.manifest.name === hook.pluginName);
193
- if (!plugin) continue;
194
- const options = {
195
- sessionId: this.sessionId,
196
- transcriptPath: this.transcriptPath,
197
- pluginRoot: plugin.location
198
- };
199
- await executeHooksForEvent(HookEvent.SessionStart, [hook], input, options);
200
- }
424
+ await this.executeFireAndForget(HookEvent.PreCompact, input);
201
425
  }
202
- /**
203
- * Execute SessionEnd hooks
204
- */
205
- async executeSessionEnd(reason = "other") {
206
- if (!this.enabled) {
207
- return;
208
- }
209
- const allHooks = this.plugins.flatMap((p) => p.hooks);
210
- const matchingHooks = allHooks.filter(
211
- (hook) => hook.event === HookEvent.SessionEnd
426
+ async executePostCompact(trigger, summary, compressionRatio, originalTokens, compressedTokens) {
427
+ const input = createPostCompactInput(
428
+ this.sessionId,
429
+ this.transcriptPath,
430
+ trigger,
431
+ summary,
432
+ compressionRatio,
433
+ originalTokens,
434
+ compressedTokens
212
435
  );
213
- if (matchingHooks.length === 0) {
214
- return;
215
- }
216
- logInfo(`SessionEnd: Executing ${matchingHooks.length} hook(s)`);
217
- const input = createSessionEndInput(
436
+ await this.executeFireAndForget(HookEvent.PostCompact, input);
437
+ }
438
+ async executeConfigChange(source, filePath) {
439
+ const input = createConfigChangeInput(
218
440
  this.sessionId,
219
441
  this.transcriptPath,
220
- reason
442
+ source,
443
+ filePath
221
444
  );
222
- for (const hook of matchingHooks) {
223
- const plugin = this.plugins.find((p) => p.manifest.name === hook.pluginName);
224
- if (!plugin) continue;
225
- const options = {
226
- sessionId: this.sessionId,
227
- transcriptPath: this.transcriptPath,
228
- pluginRoot: plugin.location
229
- };
230
- await executeHooksForEvent(HookEvent.SessionEnd, [hook], input, options);
231
- }
445
+ return this.executeBlockableEvent(HookEvent.ConfigChange, input);
446
+ }
447
+ async executeTaskCompleted(taskId, subject, description) {
448
+ const input = createTaskCompletedInput(
449
+ this.sessionId,
450
+ this.transcriptPath,
451
+ taskId,
452
+ subject,
453
+ description
454
+ );
455
+ return this.executeBlockableEvent(HookEvent.TaskCompleted, input);
456
+ }
457
+ async executeTeammateIdle(teammateName, teamName) {
458
+ const input = createTeammateIdleInput(
459
+ this.sessionId,
460
+ this.transcriptPath,
461
+ teammateName,
462
+ teamName
463
+ );
464
+ return this.executeBlockableEvent(HookEvent.TeammateIdle, input);
232
465
  }
233
466
  }
234
467
  let globalHookManager = null;
235
468
  function initializeHookManager(sessionId, transcriptPath, plugins) {
236
469
  globalHookManager = new HookManager(sessionId, transcriptPath);
237
470
  globalHookManager.registerPlugins(plugins);
471
+ globalHookManager.loadSettingsHooks();
238
472
  return globalHookManager;
239
473
  }
240
474
  function getHookManager() {