@within-7/minto 0.1.5 → 0.1.6

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 (264) hide show
  1. package/dist/commands/agents/AgentsCommand.js +2342 -0
  2. package/dist/commands/agents/AgentsCommand.js.map +7 -0
  3. package/dist/commands/agents/constants.js +58 -0
  4. package/dist/commands/agents/constants.js.map +7 -0
  5. package/dist/commands/agents/index.js +37 -0
  6. package/dist/commands/agents/index.js.map +7 -0
  7. package/dist/commands/agents/types.js +10 -0
  8. package/dist/commands/agents/types.js.map +7 -0
  9. package/dist/commands/agents/utils/fileOperations.js +185 -0
  10. package/dist/commands/agents/utils/fileOperations.js.map +7 -0
  11. package/dist/commands/agents/utils/index.js +21 -0
  12. package/dist/commands/agents/utils/index.js.map +7 -0
  13. package/dist/commands/bug.js +2 -2
  14. package/dist/commands/bug.js.map +2 -2
  15. package/dist/commands/compact.js +5 -5
  16. package/dist/commands/compact.js.map +2 -2
  17. package/dist/commands/ctx_viz.js +55 -22
  18. package/dist/commands/ctx_viz.js.map +2 -2
  19. package/dist/commands/mcp-interactive.js +11 -11
  20. package/dist/commands/mcp-interactive.js.map +2 -2
  21. package/dist/commands/model.js +94 -32
  22. package/dist/commands/model.js.map +3 -3
  23. package/dist/commands/plugin/AddMarketplaceForm.js +49 -21
  24. package/dist/commands/plugin/AddMarketplaceForm.js.map +2 -2
  25. package/dist/commands/plugin/ConfirmDialog.js +38 -26
  26. package/dist/commands/plugin/ConfirmDialog.js.map +2 -2
  27. package/dist/commands/plugin/InstalledPluginsByMarketplace.js +24 -8
  28. package/dist/commands/plugin/InstalledPluginsByMarketplace.js.map +2 -2
  29. package/dist/commands/plugin/InstalledPluginsManager.js +3 -1
  30. package/dist/commands/plugin/InstalledPluginsManager.js.map +2 -2
  31. package/dist/commands/plugin/MainMenu.js +16 -7
  32. package/dist/commands/plugin/MainMenu.js.map +2 -2
  33. package/dist/commands/plugin/MarketplaceManager.js +84 -39
  34. package/dist/commands/plugin/MarketplaceManager.js.map +2 -2
  35. package/dist/commands/plugin/MarketplaceSelector.js +7 -3
  36. package/dist/commands/plugin/MarketplaceSelector.js.map +2 -2
  37. package/dist/commands/plugin/PlaceholderScreen.js +16 -2
  38. package/dist/commands/plugin/PlaceholderScreen.js.map +2 -2
  39. package/dist/commands/plugin/PluginBrowser.js +4 -2
  40. package/dist/commands/plugin/PluginBrowser.js.map +2 -2
  41. package/dist/commands/plugin/PluginDetailsInstall.js +12 -6
  42. package/dist/commands/plugin/PluginDetailsInstall.js.map +2 -2
  43. package/dist/commands/plugin/PluginDetailsManage.js +14 -5
  44. package/dist/commands/plugin/PluginDetailsManage.js.map +2 -2
  45. package/dist/commands/plugin/example-usage.js.map +2 -2
  46. package/dist/commands/plugin/utils.js.map +2 -2
  47. package/dist/commands/plugin.js +226 -46
  48. package/dist/commands/plugin.js.map +2 -2
  49. package/dist/commands/refreshCommands.js +6 -3
  50. package/dist/commands/refreshCommands.js.map +2 -2
  51. package/dist/commands/resume.js +2 -1
  52. package/dist/commands/resume.js.map +2 -2
  53. package/dist/commands/setup.js +19 -5
  54. package/dist/commands/setup.js.map +2 -2
  55. package/dist/commands/terminalSetup.js +2 -2
  56. package/dist/commands/terminalSetup.js.map +1 -1
  57. package/dist/commands.js +14 -30
  58. package/dist/commands.js.map +2 -2
  59. package/dist/components/AskUserQuestionDialog/AskUserQuestionDialog.js.map +2 -2
  60. package/dist/components/AskUserQuestionDialog/QuestionView.js +10 -1
  61. package/dist/components/AskUserQuestionDialog/QuestionView.js.map +2 -2
  62. package/dist/components/BackgroundTasksPanel.js +5 -1
  63. package/dist/components/BackgroundTasksPanel.js.map +2 -2
  64. package/dist/components/Config.js +17 -4
  65. package/dist/components/Config.js.map +2 -2
  66. package/dist/components/ConsoleOAuthFlow.js.map +2 -2
  67. package/dist/components/CustomSelect/select-option.js +4 -1
  68. package/dist/components/CustomSelect/select-option.js.map +2 -2
  69. package/dist/components/Help.js +6 -8
  70. package/dist/components/Help.js.map +2 -2
  71. package/dist/components/Logo.js +1 -1
  72. package/dist/components/Logo.js.map +2 -2
  73. package/dist/components/ModelListManager.js.map +2 -2
  74. package/dist/components/ModelSelector/ModelSelector.js +2030 -0
  75. package/dist/components/ModelSelector/ModelSelector.js.map +7 -0
  76. package/dist/components/ModelSelector/ScreenContainer.js +27 -0
  77. package/dist/components/ModelSelector/ScreenContainer.js.map +7 -0
  78. package/dist/components/ModelSelector/constants.js +37 -0
  79. package/dist/components/ModelSelector/constants.js.map +7 -0
  80. package/dist/components/ModelSelector/hooks/index.js +5 -0
  81. package/dist/components/ModelSelector/hooks/index.js.map +7 -0
  82. package/dist/components/ModelSelector/hooks/useEscapeNavigation.js +21 -0
  83. package/dist/components/ModelSelector/hooks/useEscapeNavigation.js.map +7 -0
  84. package/dist/components/ModelSelector/index.js +17 -0
  85. package/dist/components/ModelSelector/index.js.map +7 -0
  86. package/dist/components/ModelSelector/types.js +1 -0
  87. package/dist/components/ModelSelector/types.js.map +7 -0
  88. package/dist/components/PressEnterToContinue.js +1 -1
  89. package/dist/components/PressEnterToContinue.js.map +2 -2
  90. package/dist/components/ProjectOnboarding.js +1 -1
  91. package/dist/components/ProjectOnboarding.js.map +2 -2
  92. package/dist/components/PromptInput.js +88 -37
  93. package/dist/components/PromptInput.js.map +2 -2
  94. package/dist/components/QuitSummary.js +17 -10
  95. package/dist/components/QuitSummary.js.map +2 -2
  96. package/dist/components/SentryErrorBoundary.js.map +2 -2
  97. package/dist/components/StreamingBashOutput.js.map +2 -2
  98. package/dist/components/StructuredDiff.js.map +2 -2
  99. package/dist/components/SubagentProgress.js.map +2 -2
  100. package/dist/components/TaskCard.js.map +2 -2
  101. package/dist/components/TextInput.js.map +1 -1
  102. package/dist/components/TodoItem.js.map +1 -1
  103. package/dist/components/binary-feedback/BinaryFeedbackOption.js +1 -3
  104. package/dist/components/binary-feedback/BinaryFeedbackOption.js.map +2 -2
  105. package/dist/components/messages/AssistantLocalCommandOutputMessage.js.map +1 -1
  106. package/dist/components/messages/AssistantToolUseMessage.js +3 -1
  107. package/dist/components/messages/AssistantToolUseMessage.js.map +2 -2
  108. package/dist/components/messages/TaskProgressMessage.js.map +2 -2
  109. package/dist/components/messages/TaskToolMessage.js.map +2 -2
  110. package/dist/components/messages/UserToolResultMessage/utils.js.map +2 -2
  111. package/dist/components/permissions/FileEditPermissionRequest/FileEditToolDiff.js.map +2 -2
  112. package/dist/components/permissions/FileWritePermissionRequest/FileWriteToolDiff.js.map +2 -2
  113. package/dist/components/permissions/hooks.js.map +2 -2
  114. package/dist/constants/modelCapabilities.js +1 -1
  115. package/dist/constants/modelCapabilities.js.map +2 -2
  116. package/dist/constants/prompts.js.map +1 -1
  117. package/dist/constants/timing.js +34 -0
  118. package/dist/constants/timing.js.map +7 -0
  119. package/dist/entrypoints/cli.js +128 -33
  120. package/dist/entrypoints/cli.js.map +3 -3
  121. package/dist/entrypoints/mcp.js +13 -18
  122. package/dist/entrypoints/mcp.js.map +2 -2
  123. package/dist/hooks/useCanUseTool.js.map +2 -2
  124. package/dist/hooks/useCancelRequest.js.map +1 -1
  125. package/dist/hooks/useHistorySearch.js.map +2 -2
  126. package/dist/hooks/useLogStartupTime.js.map +2 -2
  127. package/dist/hooks/usePermissionRequestLogging.js.map +2 -2
  128. package/dist/hooks/useTextInput.js.map +1 -1
  129. package/dist/hooks/useUnifiedCompletion.js +493 -394
  130. package/dist/hooks/useUnifiedCompletion.js.map +2 -2
  131. package/dist/index.js.map +2 -2
  132. package/dist/permissions.js +4 -7
  133. package/dist/permissions.js.map +2 -2
  134. package/dist/query.js +6 -1
  135. package/dist/query.js.map +2 -2
  136. package/dist/screens/REPL.js +72 -36
  137. package/dist/screens/REPL.js.map +2 -2
  138. package/dist/screens/ResumeConversation.js +2 -1
  139. package/dist/screens/ResumeConversation.js.map +2 -2
  140. package/dist/services/adapters/base.js.map +2 -2
  141. package/dist/services/adapters/chatCompletions.js.map +2 -2
  142. package/dist/services/adapters/responsesAPI.js +3 -1
  143. package/dist/services/adapters/responsesAPI.js.map +2 -2
  144. package/dist/services/claude.js +327 -328
  145. package/dist/services/claude.js.map +2 -2
  146. package/dist/services/customCommands.js +6 -1
  147. package/dist/services/customCommands.js.map +2 -2
  148. package/dist/services/fileFreshness.js.map +2 -2
  149. package/dist/services/gpt5ConnectionTest.js +20 -7
  150. package/dist/services/gpt5ConnectionTest.js.map +2 -2
  151. package/dist/services/hookExecutor.js +6 -12
  152. package/dist/services/hookExecutor.js.map +2 -2
  153. package/dist/services/mcpClient.js +29 -2
  154. package/dist/services/mcpClient.js.map +2 -2
  155. package/dist/services/mentionProcessor.js +23 -10
  156. package/dist/services/mentionProcessor.js.map +2 -2
  157. package/dist/services/modelAdapterFactory.js.map +2 -2
  158. package/dist/services/oauth.js.map +2 -2
  159. package/dist/services/openai.js +109 -72
  160. package/dist/services/openai.js.map +3 -3
  161. package/dist/services/responseStateManager.js.map +2 -2
  162. package/dist/services/systemReminder.js.map +2 -2
  163. package/dist/tools/ArchitectTool/ArchitectTool.js.map +1 -1
  164. package/dist/tools/AskExpertModelTool/AskExpertModelTool.js +14 -8
  165. package/dist/tools/AskExpertModelTool/AskExpertModelTool.js.map +2 -2
  166. package/dist/tools/BashOutputTool/BashOutputTool.js.map +2 -2
  167. package/dist/tools/BashTool/BashTool.js.map +2 -2
  168. package/dist/tools/FileReadTool/FileReadTool.js.map +1 -1
  169. package/dist/tools/FileWriteTool/FileWriteTool.js.map +2 -2
  170. package/dist/tools/GrepTool/GrepTool.js +1 -4
  171. package/dist/tools/GrepTool/GrepTool.js.map +2 -2
  172. package/dist/tools/MultiEditTool/MultiEditTool.js +4 -1
  173. package/dist/tools/MultiEditTool/MultiEditTool.js.map +2 -2
  174. package/dist/tools/NotebookReadTool/NotebookReadTool.js +3 -1
  175. package/dist/tools/NotebookReadTool/NotebookReadTool.js.map +2 -2
  176. package/dist/tools/SkillTool/SkillTool.js +12 -6
  177. package/dist/tools/SkillTool/SkillTool.js.map +2 -2
  178. package/dist/tools/TaskTool/TaskTool.js +14 -5
  179. package/dist/tools/TaskTool/TaskTool.js.map +2 -2
  180. package/dist/tools/TaskTool/prompt.js.map +2 -2
  181. package/dist/tools/ThinkTool/ThinkTool.js +6 -1
  182. package/dist/tools/ThinkTool/ThinkTool.js.map +2 -2
  183. package/dist/tools/TodoWriteTool/TodoWriteTool.js +23 -3
  184. package/dist/tools/TodoWriteTool/TodoWriteTool.js.map +2 -2
  185. package/dist/tools/URLFetcherTool/URLFetcherTool.js +2 -2
  186. package/dist/tools/URLFetcherTool/URLFetcherTool.js.map +2 -2
  187. package/dist/tools/URLFetcherTool/cache.js +6 -3
  188. package/dist/tools/URLFetcherTool/cache.js.map +2 -2
  189. package/dist/tools/URLFetcherTool/htmlToMarkdown.js +3 -1
  190. package/dist/tools/URLFetcherTool/htmlToMarkdown.js.map +2 -2
  191. package/dist/tools/WebSearchTool/WebSearchTool.js.map +2 -2
  192. package/dist/tools/WebSearchTool/prompt.js.map +2 -2
  193. package/dist/tools/WebSearchTool/searchProviders.js +15 -6
  194. package/dist/tools/WebSearchTool/searchProviders.js.map +2 -2
  195. package/dist/tools.js +4 -1
  196. package/dist/tools.js.map +2 -2
  197. package/dist/types/core.js +1 -0
  198. package/dist/types/core.js.map +7 -0
  199. package/dist/types/hooks.js +1 -4
  200. package/dist/types/hooks.js.map +2 -2
  201. package/dist/types/marketplace.js +8 -2
  202. package/dist/types/marketplace.js.map +2 -2
  203. package/dist/types/plugin.js +9 -6
  204. package/dist/types/plugin.js.map +2 -2
  205. package/dist/utils/BackgroundShellManager.js +76 -10
  206. package/dist/utils/BackgroundShellManager.js.map +2 -2
  207. package/dist/utils/PersistentShell.js +7 -2
  208. package/dist/utils/PersistentShell.js.map +2 -2
  209. package/dist/utils/advancedFuzzyMatcher.js +4 -1
  210. package/dist/utils/advancedFuzzyMatcher.js.map +2 -2
  211. package/dist/utils/agentLoader.js +69 -35
  212. package/dist/utils/agentLoader.js.map +2 -2
  213. package/dist/utils/agentStorage.js.map +2 -2
  214. package/dist/utils/async.js +163 -0
  215. package/dist/utils/async.js.map +7 -0
  216. package/dist/utils/autoUpdater.js +8 -2
  217. package/dist/utils/autoUpdater.js.map +2 -2
  218. package/dist/utils/commands.js +23 -11
  219. package/dist/utils/commands.js.map +2 -2
  220. package/dist/utils/commonUnixCommands.js +3 -1
  221. package/dist/utils/commonUnixCommands.js.map +2 -2
  222. package/dist/utils/compressionMode.js.map +2 -2
  223. package/dist/utils/config.js +30 -14
  224. package/dist/utils/config.js.map +2 -2
  225. package/dist/utils/debugLogger.js.map +2 -2
  226. package/dist/utils/env.js.map +2 -2
  227. package/dist/utils/envConfig.js +82 -0
  228. package/dist/utils/envConfig.js.map +7 -0
  229. package/dist/utils/errorHandling.js +89 -0
  230. package/dist/utils/errorHandling.js.map +7 -0
  231. package/dist/utils/expertChatStorage.js.map +2 -2
  232. package/dist/utils/fuzzyMatcher.js +13 -7
  233. package/dist/utils/fuzzyMatcher.js.map +2 -2
  234. package/dist/utils/hookManager.js +14 -4
  235. package/dist/utils/hookManager.js.map +2 -2
  236. package/dist/utils/log.js.map +2 -2
  237. package/dist/utils/marketplaceManager.js +44 -9
  238. package/dist/utils/marketplaceManager.js.map +2 -2
  239. package/dist/utils/messageContextManager.js.map +1 -1
  240. package/dist/utils/messages.js +6 -3
  241. package/dist/utils/messages.js.map +2 -2
  242. package/dist/utils/model.js +3 -1
  243. package/dist/utils/model.js.map +2 -2
  244. package/dist/utils/pluginInstaller.js +3 -15
  245. package/dist/utils/pluginInstaller.js.map +2 -2
  246. package/dist/utils/pluginLoader.js +41 -13
  247. package/dist/utils/pluginLoader.js.map +2 -2
  248. package/dist/utils/pluginRegistry.js.map +2 -2
  249. package/dist/utils/pluginValidator.js +71 -49
  250. package/dist/utils/pluginValidator.js.map +2 -2
  251. package/dist/utils/ptyCompat.js.map +2 -2
  252. package/dist/utils/roundConverter.js.map +2 -2
  253. package/dist/utils/secureFile.js +43 -14
  254. package/dist/utils/secureFile.js.map +2 -2
  255. package/dist/utils/sessionState.js.map +2 -2
  256. package/dist/utils/skillLoader.js.map +2 -2
  257. package/dist/utils/teamConfig.js +7 -4
  258. package/dist/utils/teamConfig.js.map +2 -2
  259. package/dist/utils/theme.js.map +2 -2
  260. package/dist/utils/thinking.js.map +2 -2
  261. package/dist/utils/unaryLogging.js.map +2 -2
  262. package/dist/version.js +2 -2
  263. package/dist/version.js.map +1 -1
  264. package/package.json +5 -5
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/utils/pluginValidator.ts"],
4
- "sourcesContent": ["/**\n * Plugin Validation System\n *\n * Validates plugin manifests and component files for correctness.\n * Checks for proper structure, required fields, and file existence.\n */\n\nimport fs from 'fs'\nimport path from 'path'\nimport matter from 'gray-matter'\nimport semver from 'semver'\nimport { z } from 'zod'\nimport { PluginManifestSchema, type PluginManifest, type HookEvent } from '../types/plugin.js'\n\n/**\n * Validation error details\n */\nexport interface ValidationError {\n type: 'manifest' | 'agent' | 'command' | 'skill' | 'hook' | 'mcp' | 'dependency' | 'engine'\n file?: string\n message: string\n details?: any\n}\n\n/**\n * Validation warning details\n */\nexport interface ValidationWarning {\n type: string\n file?: string\n message: string\n}\n\n/**\n * Complete validation result\n */\nexport interface ValidationResult {\n valid: boolean\n errors: ValidationError[]\n warnings: ValidationWarning[]\n}\n\n/**\n * Agent YAML frontmatter schema\n */\nconst AgentFrontmatterSchema = z.object({\n name: z.string().min(1, 'Agent name is required'),\n description: z.string().min(1, 'Agent description is required'),\n tools: z.union([\n z.string(), // \"*\" for all tools\n z.array(z.string()).min(1, 'At least one tool must be specified'),\n ]).optional(),\n model: z.string().optional(),\n})\n\n/**\n * Command YAML frontmatter schema\n */\nconst CommandFrontmatterSchema = z.object({\n name: z.string().min(1, 'Command name is required'),\n description: z.string().optional(),\n aliases: z.array(z.string()).optional(),\n enabled: z.boolean().optional(),\n hidden: z.boolean().optional(),\n progressMessage: z.string().optional(),\n argNames: z.array(z.string()).optional(),\n 'allowed-tools': z.array(z.string()).optional(),\n})\n\n/**\n * Skill YAML frontmatter schema\n */\nconst SkillFrontmatterSchema = z.object({\n name: z.string().min(1, 'Skill name is required'),\n description: z.string().min(1, 'Skill description is required'),\n})\n\n/**\n * Hook configuration schema\n */\nconst HookConfigSchema = z.object({\n event: z.enum([\n 'PreToolUse',\n 'PostToolUse',\n 'UserPromptSubmit',\n 'SessionStart',\n 'SessionEnd',\n 'Stop',\n 'SubagentStop',\n 'Notification',\n 'PreCompact',\n ] as const),\n matcher: z.string().optional(),\n type: z.enum(['command', 'message', 'notification']),\n command: z.string().optional(),\n message: z.string().optional(),\n blocking: z.boolean().optional(),\n timeout: z.number().optional(),\n})\n\n/**\n * MCP Server configuration schema\n */\nconst MCPServerConfigSchema = z.object({\n command: z.string().min(1, 'MCP server command is required'),\n args: z.array(z.string()).optional(),\n env: z.record(z.string()).optional(),\n timeout: z.number().optional(),\n})\n\n/**\n * Validate complete plugin including manifest and all components\n */\nexport async function validatePlugin(pluginPath: string): Promise<ValidationResult> {\n const errors: ValidationError[] = []\n const warnings: ValidationWarning[] = []\n\n // Check if plugin directory exists\n if (!fs.existsSync(pluginPath)) {\n return {\n valid: false,\n errors: [{\n type: 'manifest',\n message: `Plugin directory does not exist: ${pluginPath}`,\n }],\n warnings: [],\n }\n }\n\n // Check if it's a directory\n const stats = fs.statSync(pluginPath)\n if (!stats.isDirectory()) {\n return {\n valid: false,\n errors: [{\n type: 'manifest',\n message: `Plugin path is not a directory: ${pluginPath}`,\n }],\n warnings: [],\n }\n }\n\n // Validate manifest\n const manifestPath = path.join(pluginPath, 'plugin.json')\n if (!fs.existsSync(manifestPath)) {\n return {\n valid: false,\n errors: [{\n type: 'manifest',\n file: 'plugin.json',\n message: 'Plugin manifest (plugin.json) not found',\n }],\n warnings: [],\n }\n }\n\n let manifest: PluginManifest\n try {\n const manifestContent = fs.readFileSync(manifestPath, 'utf-8')\n const manifestData = JSON.parse(manifestContent)\n\n // Validate with Zod schema\n const manifestResult = validateManifest(manifestData)\n errors.push(...manifestResult.errors)\n warnings.push(...manifestResult.warnings)\n\n if (!manifestResult.valid) {\n return { valid: false, errors, warnings }\n }\n\n manifest = manifestData as PluginManifest\n } catch (error) {\n return {\n valid: false,\n errors: [{\n type: 'manifest',\n file: 'plugin.json',\n message: `Failed to parse manifest: ${error instanceof Error ? error.message : String(error)}`,\n details: error,\n }],\n warnings: [],\n }\n }\n\n // Validate engines compatibility\n if (manifest.engines) {\n const engineResult = await validateEngines(manifest.engines)\n errors.push(...engineResult.errors)\n warnings.push(...engineResult.warnings)\n }\n\n // Validate agent files\n if (manifest.agents && manifest.agents.length > 0) {\n for (const agentFile of manifest.agents) {\n const agentPath = path.join(pluginPath, agentFile)\n const result = validateAgentFile(agentPath)\n errors.push(...result.errors)\n warnings.push(...result.warnings)\n }\n }\n\n // Validate command files\n if (manifest.commands && manifest.commands.length > 0) {\n for (const commandFile of manifest.commands) {\n const commandPath = path.join(pluginPath, commandFile)\n const result = validateCommandFile(commandPath)\n errors.push(...result.errors)\n warnings.push(...result.warnings)\n }\n }\n\n // Validate skill files\n if (manifest.skills && manifest.skills.length > 0) {\n for (const skillFile of manifest.skills) {\n const skillPath = path.join(pluginPath, skillFile)\n const result = validateSkillFile(skillPath)\n errors.push(...result.errors)\n warnings.push(...result.warnings)\n }\n }\n\n // Validate hook files\n if (manifest.hooks && manifest.hooks.length > 0) {\n for (const hookFile of manifest.hooks) {\n const hookPath = path.join(pluginPath, hookFile)\n const result = validateHookFile(hookPath)\n errors.push(...result.errors)\n warnings.push(...result.warnings)\n }\n }\n\n // Validate MCP server configs\n // mcpServers can be either an array of file paths or an object with inline configs\n if (manifest.mcpServers) {\n if (Array.isArray(manifest.mcpServers) && manifest.mcpServers.length > 0) {\n for (const mcpFile of manifest.mcpServers) {\n const mcpPath = path.join(pluginPath, mcpFile)\n const result = validateMCPServerFile(mcpPath)\n errors.push(...result.errors)\n warnings.push(...result.warnings)\n }\n }\n // If it's an object (inline configs), skip file validation\n }\n\n // Check for unused files warning\n const componentFiles = new Set([\n ...(manifest.agents || []),\n ...(manifest.commands || []),\n ...(manifest.skills || []),\n ...(manifest.hooks || []),\n ...(Array.isArray(manifest.mcpServers) ? manifest.mcpServers : []),\n ])\n\n if (componentFiles.size === 0) {\n warnings.push({\n type: 'manifest',\n message: 'Plugin has no components (agents, commands, skills, hooks, or MCP servers)',\n })\n }\n\n return {\n valid: errors.length === 0,\n errors,\n warnings,\n }\n}\n\n/**\n * Validate plugin manifest schema\n */\nexport function validateManifest(manifest: any): ValidationResult {\n const errors: ValidationError[] = []\n const warnings: ValidationWarning[] = []\n\n // Check if manifest is an object\n if (!manifest || typeof manifest !== 'object') {\n return {\n valid: false,\n errors: [{\n type: 'manifest',\n message: 'Manifest must be a valid JSON object',\n }],\n warnings: [],\n }\n }\n\n // Validate with Zod schema\n try {\n PluginManifestSchema.parse(manifest)\n } catch (error) {\n if (error instanceof z.ZodError) {\n for (const issue of error.errors) {\n errors.push({\n type: 'manifest',\n message: `${issue.path.join('.')}: ${issue.message}`,\n details: issue,\n })\n }\n } else {\n errors.push({\n type: 'manifest',\n message: `Manifest validation failed: ${error instanceof Error ? error.message : String(error)}`,\n details: error,\n })\n }\n }\n\n // Additional validations\n if (manifest.name && !/^[a-z0-9-]+$/.test(manifest.name)) {\n errors.push({\n type: 'manifest',\n message: 'Plugin name must be lowercase alphanumeric with hyphens only',\n details: { name: manifest.name },\n })\n }\n\n // Validate version format (semver)\n if (manifest.version && !semver.valid(manifest.version)) {\n errors.push({\n type: 'manifest',\n message: `Invalid version format: ${manifest.version}. Must follow semver (e.g., 1.0.0)`,\n details: { version: manifest.version },\n })\n }\n\n // Validate URLs\n if (manifest.homepage) {\n try {\n new URL(manifest.homepage)\n } catch {\n errors.push({\n type: 'manifest',\n message: `Invalid homepage URL: ${manifest.homepage}`,\n })\n }\n }\n\n if (manifest.repository) {\n try {\n new URL(manifest.repository)\n } catch {\n errors.push({\n type: 'manifest',\n message: `Invalid repository URL: ${manifest.repository}`,\n })\n }\n }\n\n // Warn about missing optional but recommended fields\n if (!manifest.description) {\n warnings.push({\n type: 'manifest',\n message: 'Missing recommended field: description',\n })\n }\n\n if (!manifest.author) {\n warnings.push({\n type: 'manifest',\n message: 'Missing recommended field: author',\n })\n }\n\n if (!manifest.license) {\n warnings.push({\n type: 'manifest',\n message: 'Missing recommended field: license',\n })\n }\n\n return {\n valid: errors.length === 0,\n errors,\n warnings,\n }\n}\n\n/**\n * Validate engine version requirements\n */\nasync function validateEngines(engines: { minto?: string; 'claude-code'?: string; node?: string }): Promise<ValidationResult> {\n const errors: ValidationError[] = []\n const warnings: ValidationWarning[] = []\n\n // Validate minto version\n if (engines.minto) {\n const validRange = semver.validRange(engines.minto)\n if (!validRange) {\n errors.push({\n type: 'engine',\n message: `Invalid minto version range: ${engines.minto}`,\n details: { engine: 'minto', range: engines.minto },\n })\n } else {\n // Check compatibility with current version\n try {\n const { VERSION } = await import('../version.js')\n if (!semver.satisfies(VERSION, engines.minto)) {\n warnings.push({\n type: 'engine',\n message: `Plugin requires minto ${engines.minto}, but current version is ${VERSION}`,\n })\n }\n } catch {\n // Unable to check current version, skip this validation\n }\n }\n }\n\n // Validate node version\n if (engines.node) {\n const validRange = semver.validRange(engines.node)\n if (!validRange) {\n errors.push({\n type: 'engine',\n message: `Invalid node version range: ${engines.node}`,\n details: { engine: 'node', range: engines.node },\n })\n } else {\n // Check compatibility with current node version\n if (!semver.satisfies(process.version, engines.node)) {\n warnings.push({\n type: 'engine',\n message: `Plugin requires node ${engines.node}, but current version is ${process.version}`,\n })\n }\n }\n }\n\n return {\n valid: errors.length === 0,\n errors,\n warnings,\n }\n}\n\n/**\n * Validate agent file\n */\nexport function validateAgentFile(filePath: string): ValidationResult {\n const errors: ValidationError[] = []\n const warnings: ValidationWarning[] = []\n\n // Check file exists\n if (!fs.existsSync(filePath)) {\n return {\n valid: false,\n errors: [{\n type: 'agent',\n file: filePath,\n message: `Agent file not found: ${filePath}`,\n }],\n warnings: [],\n }\n }\n\n // Check file extension\n const ext = path.extname(filePath)\n if (ext !== '.md' && ext !== '.markdown') {\n warnings.push({\n type: 'agent',\n file: filePath,\n message: `Agent file should have .md or .markdown extension, got: ${ext}`,\n })\n }\n\n // Parse YAML frontmatter\n try {\n const content = fs.readFileSync(filePath, 'utf-8')\n const parsed = matter(content)\n\n // Validate frontmatter exists\n if (!parsed.data || Object.keys(parsed.data).length === 0) {\n errors.push({\n type: 'agent',\n file: filePath,\n message: 'Agent file must have YAML frontmatter',\n })\n return { valid: false, errors, warnings }\n }\n\n // Validate frontmatter schema\n try {\n AgentFrontmatterSchema.parse(parsed.data)\n } catch (error) {\n if (error instanceof z.ZodError) {\n for (const issue of error.errors) {\n errors.push({\n type: 'agent',\n file: filePath,\n message: `Frontmatter ${issue.path.join('.')}: ${issue.message}`,\n details: issue,\n })\n }\n }\n }\n\n // Check for content\n if (!parsed.content || parsed.content.trim().length === 0) {\n warnings.push({\n type: 'agent',\n file: filePath,\n message: 'Agent file has no content after frontmatter',\n })\n }\n\n // Validate tools field\n if (parsed.data.tools) {\n if (Array.isArray(parsed.data.tools)) {\n if (parsed.data.tools.length === 0) {\n errors.push({\n type: 'agent',\n file: filePath,\n message: 'Tools array cannot be empty (use \"*\" for all tools or omit the field)',\n })\n }\n } else if (parsed.data.tools !== '*') {\n errors.push({\n type: 'agent',\n file: filePath,\n message: 'Tools must be an array of tool names or \"*\" for all tools',\n details: { tools: parsed.data.tools },\n })\n }\n }\n\n } catch (error) {\n errors.push({\n type: 'agent',\n file: filePath,\n message: `Failed to parse agent file: ${error instanceof Error ? error.message : String(error)}`,\n details: error,\n })\n }\n\n return {\n valid: errors.length === 0,\n errors,\n warnings,\n }\n}\n\n/**\n * Validate command file\n */\nexport function validateCommandFile(filePath: string): ValidationResult {\n const errors: ValidationError[] = []\n const warnings: ValidationWarning[] = []\n\n // Check file exists\n if (!fs.existsSync(filePath)) {\n return {\n valid: false,\n errors: [{\n type: 'command',\n file: filePath,\n message: `Command file not found: ${filePath}`,\n }],\n warnings: [],\n }\n }\n\n // Check file extension\n const ext = path.extname(filePath)\n if (ext !== '.md' && ext !== '.markdown') {\n warnings.push({\n type: 'command',\n file: filePath,\n message: `Command file should have .md or .markdown extension, got: ${ext}`,\n })\n }\n\n // Parse YAML frontmatter\n try {\n const content = fs.readFileSync(filePath, 'utf-8')\n const parsed = matter(content)\n\n // Validate frontmatter exists\n if (!parsed.data || Object.keys(parsed.data).length === 0) {\n errors.push({\n type: 'command',\n file: filePath,\n message: 'Command file must have YAML frontmatter',\n })\n return { valid: false, errors, warnings }\n }\n\n // Validate frontmatter schema\n try {\n CommandFrontmatterSchema.parse(parsed.data)\n } catch (error) {\n if (error instanceof z.ZodError) {\n for (const issue of error.errors) {\n errors.push({\n type: 'command',\n file: filePath,\n message: `Frontmatter ${issue.path.join('.')}: ${issue.message}`,\n details: issue,\n })\n }\n }\n }\n\n // Check for content\n if (!parsed.content || parsed.content.trim().length === 0) {\n warnings.push({\n type: 'command',\n file: filePath,\n message: 'Command file has no content after frontmatter',\n })\n }\n\n // Validate name matches filename\n const filename = path.basename(filePath, path.extname(filePath))\n if (parsed.data.name && parsed.data.name !== filename) {\n warnings.push({\n type: 'command',\n file: filePath,\n message: `Command name \"${parsed.data.name}\" does not match filename \"${filename}\"`,\n })\n }\n\n } catch (error) {\n errors.push({\n type: 'command',\n file: filePath,\n message: `Failed to parse command file: ${error instanceof Error ? error.message : String(error)}`,\n details: error,\n })\n }\n\n return {\n valid: errors.length === 0,\n errors,\n warnings,\n }\n}\n\n/**\n * Validate skill file\n */\nexport function validateSkillFile(filePath: string): ValidationResult {\n const errors: ValidationError[] = []\n const warnings: ValidationWarning[] = []\n\n // Check file exists\n if (!fs.existsSync(filePath)) {\n return {\n valid: false,\n errors: [{\n type: 'skill',\n file: filePath,\n message: `Skill file not found: ${filePath}`,\n }],\n warnings: [],\n }\n }\n\n // Check file extension\n const ext = path.extname(filePath)\n if (ext !== '.md' && ext !== '.markdown') {\n warnings.push({\n type: 'skill',\n file: filePath,\n message: `Skill file should have .md or .markdown extension, got: ${ext}`,\n })\n }\n\n // Parse YAML frontmatter\n try {\n const content = fs.readFileSync(filePath, 'utf-8')\n const parsed = matter(content)\n\n // Validate frontmatter exists\n if (!parsed.data || Object.keys(parsed.data).length === 0) {\n errors.push({\n type: 'skill',\n file: filePath,\n message: 'Skill file must have YAML frontmatter',\n })\n return { valid: false, errors, warnings }\n }\n\n // Validate frontmatter schema\n try {\n SkillFrontmatterSchema.parse(parsed.data)\n } catch (error) {\n if (error instanceof z.ZodError) {\n for (const issue of error.errors) {\n errors.push({\n type: 'skill',\n file: filePath,\n message: `Frontmatter ${issue.path.join('.')}: ${issue.message}`,\n details: issue,\n })\n }\n }\n }\n\n // Check for content\n if (!parsed.content || parsed.content.trim().length === 0) {\n warnings.push({\n type: 'skill',\n file: filePath,\n message: 'Skill file has no content after frontmatter',\n })\n }\n\n } catch (error) {\n errors.push({\n type: 'skill',\n file: filePath,\n message: `Failed to parse skill file: ${error instanceof Error ? error.message : String(error)}`,\n details: error,\n })\n }\n\n return {\n valid: errors.length === 0,\n errors,\n warnings,\n }\n}\n\n/**\n * Validate hook file\n */\nexport function validateHookFile(filePath: string): ValidationResult {\n const errors: ValidationError[] = []\n const warnings: ValidationWarning[] = []\n\n // Check file exists\n if (!fs.existsSync(filePath)) {\n return {\n valid: false,\n errors: [{\n type: 'hook',\n file: filePath,\n message: `Hook file not found: ${filePath}`,\n }],\n warnings: [],\n }\n }\n\n // Check file extension\n const ext = path.extname(filePath)\n const validExtensions = ['.js', '.mjs', '.ts', '.tsx', '.json']\n if (!validExtensions.includes(ext)) {\n errors.push({\n type: 'hook',\n file: filePath,\n message: `Hook file must have extension: ${validExtensions.join(', ')}. Got: ${ext}`,\n })\n return { valid: false, errors, warnings }\n }\n\n // For JSON hook configs, validate structure\n if (ext === '.json') {\n try {\n const content = fs.readFileSync(filePath, 'utf-8')\n const config = JSON.parse(content)\n\n // Validate hook config schema\n try {\n HookConfigSchema.parse(config)\n } catch (error) {\n if (error instanceof z.ZodError) {\n for (const issue of error.errors) {\n errors.push({\n type: 'hook',\n file: filePath,\n message: `Hook config ${issue.path.join('.')}: ${issue.message}`,\n details: issue,\n })\n }\n }\n }\n\n // Type-specific validation\n if (config.type === 'command' && !config.command) {\n errors.push({\n type: 'hook',\n file: filePath,\n message: 'Hook type \"command\" requires a \"command\" field',\n })\n }\n\n if (config.type === 'message' && !config.message) {\n errors.push({\n type: 'hook',\n file: filePath,\n message: 'Hook type \"message\" requires a \"message\" field',\n })\n }\n\n } catch (error) {\n errors.push({\n type: 'hook',\n file: filePath,\n message: `Failed to parse hook config: ${error instanceof Error ? error.message : String(error)}`,\n details: error,\n })\n }\n } else {\n // For JS/TS files, just check they're readable\n try {\n fs.readFileSync(filePath, 'utf-8')\n } catch (error) {\n errors.push({\n type: 'hook',\n file: filePath,\n message: `Failed to read hook file: ${error instanceof Error ? error.message : String(error)}`,\n details: error,\n })\n }\n\n // Warn about JS/TS hooks needing runtime validation\n warnings.push({\n type: 'hook',\n file: filePath,\n message: 'JavaScript/TypeScript hooks will be validated at runtime',\n })\n }\n\n return {\n valid: errors.length === 0,\n errors,\n warnings,\n }\n}\n\n/**\n * Validate MCP server configuration file\n */\nexport function validateMCPServerFile(filePath: string): ValidationResult {\n const errors: ValidationError[] = []\n const warnings: ValidationWarning[] = []\n\n // Check file exists\n if (!fs.existsSync(filePath)) {\n return {\n valid: false,\n errors: [{\n type: 'mcp',\n file: filePath,\n message: `MCP server config file not found: ${filePath}`,\n }],\n warnings: [],\n }\n }\n\n // Check file extension\n const ext = path.extname(filePath)\n if (ext !== '.json') {\n errors.push({\n type: 'mcp',\n file: filePath,\n message: `MCP server config must be a JSON file, got: ${ext}`,\n })\n return { valid: false, errors, warnings }\n }\n\n // Parse and validate JSON\n try {\n const content = fs.readFileSync(filePath, 'utf-8')\n const config = JSON.parse(content)\n\n // Validate MCP config schema\n try {\n MCPServerConfigSchema.parse(config)\n } catch (error) {\n if (error instanceof z.ZodError) {\n for (const issue of error.errors) {\n errors.push({\n type: 'mcp',\n file: filePath,\n message: `MCP config ${issue.path.join('.')}: ${issue.message}`,\n details: issue,\n })\n }\n }\n }\n\n // Validate command is not empty\n if (config.command && config.command.trim().length === 0) {\n errors.push({\n type: 'mcp',\n file: filePath,\n message: 'MCP server command cannot be empty',\n })\n }\n\n // Warn about timeout\n if (config.timeout && config.timeout < 1000) {\n warnings.push({\n type: 'mcp',\n file: filePath,\n message: `MCP server timeout is very low (${config.timeout}ms), consider increasing`,\n })\n }\n\n } catch (error) {\n errors.push({\n type: 'mcp',\n file: filePath,\n message: `Failed to parse MCP server config: ${error instanceof Error ? error.message : String(error)}`,\n details: error,\n })\n }\n\n return {\n valid: errors.length === 0,\n errors,\n warnings,\n }\n}\n\n/**\n * Validate plugin dependencies\n */\nexport function validateDependencies(\n dependencies: Record<string, string>,\n installedPackages?: Set<string>\n): ValidationResult {\n const errors: ValidationError[] = []\n const warnings: ValidationWarning[] = []\n\n for (const [pkg, version] of Object.entries(dependencies)) {\n // Validate version format\n const validRange = semver.validRange(version)\n if (!validRange) {\n errors.push({\n type: 'dependency',\n message: `Invalid version range for ${pkg}: ${version}`,\n details: { package: pkg, version },\n })\n }\n\n // Check if installed (if provided)\n if (installedPackages && !installedPackages.has(pkg)) {\n warnings.push({\n type: 'dependency',\n message: `Dependency not installed: ${pkg}@${version}`,\n })\n }\n }\n\n return {\n valid: errors.length === 0,\n errors,\n warnings,\n }\n}\n\n/**\n * Batch validate multiple plugins\n */\nexport async function validatePlugins(pluginPaths: string[]): Promise<Map<string, ValidationResult>> {\n const results = new Map<string, ValidationResult>()\n\n await Promise.all(\n pluginPaths.map(async (pluginPath) => {\n const result = await validatePlugin(pluginPath)\n results.set(pluginPath, result)\n })\n )\n\n return results\n}\n\n/**\n * Format validation result as human-readable string\n */\nexport function formatValidationResult(result: ValidationResult, pluginName?: string): string {\n const lines: string[] = []\n\n if (pluginName) {\n lines.push(`Plugin: ${pluginName}`)\n }\n\n if (result.valid) {\n lines.push('\u2713 Validation passed')\n } else {\n lines.push(`\u2717 Validation failed with ${result.errors.length} error(s)`)\n }\n\n if (result.errors.length > 0) {\n lines.push('\\nErrors:')\n for (const error of result.errors) {\n const location = error.file ? ` (${error.file})` : ''\n lines.push(` \u2022 [${error.type}]${location} ${error.message}`)\n }\n }\n\n if (result.warnings.length > 0) {\n lines.push('\\nWarnings:')\n for (const warning of result.warnings) {\n const location = warning.file ? ` (${warning.file})` : ''\n lines.push(` \u2022 [${warning.type}]${location} ${warning.message}`)\n }\n }\n\n return lines.join('\\n')\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * Plugin Validation System\n *\n * Validates plugin manifests and component files for correctness.\n * Checks for proper structure, required fields, and file existence.\n */\n\nimport fs from 'fs'\nimport path from 'path'\nimport matter from 'gray-matter'\nimport semver from 'semver'\nimport { z } from 'zod'\nimport {\n PluginManifestSchema,\n type PluginManifest,\n type HookEvent,\n} from '../types/plugin.js'\n\n/**\n * Validation error details\n */\nexport interface ValidationError {\n type:\n | 'manifest'\n | 'agent'\n | 'command'\n | 'skill'\n | 'hook'\n | 'mcp'\n | 'dependency'\n | 'engine'\n file?: string\n message: string\n details?: any\n}\n\n/**\n * Validation warning details\n */\nexport interface ValidationWarning {\n type: string\n file?: string\n message: string\n}\n\n/**\n * Complete validation result\n */\nexport interface ValidationResult {\n valid: boolean\n errors: ValidationError[]\n warnings: ValidationWarning[]\n}\n\n/**\n * Agent YAML frontmatter schema\n */\nconst AgentFrontmatterSchema = z.object({\n name: z.string().min(1, 'Agent name is required'),\n description: z.string().min(1, 'Agent description is required'),\n tools: z\n .union([\n z.string(), // \"*\" for all tools\n z.array(z.string()).min(1, 'At least one tool must be specified'),\n ])\n .optional(),\n model: z.string().optional(),\n})\n\n/**\n * Command YAML frontmatter schema\n */\nconst CommandFrontmatterSchema = z.object({\n name: z.string().min(1, 'Command name is required'),\n description: z.string().optional(),\n aliases: z.array(z.string()).optional(),\n enabled: z.boolean().optional(),\n hidden: z.boolean().optional(),\n progressMessage: z.string().optional(),\n argNames: z.array(z.string()).optional(),\n 'allowed-tools': z.array(z.string()).optional(),\n})\n\n/**\n * Skill YAML frontmatter schema\n */\nconst SkillFrontmatterSchema = z.object({\n name: z.string().min(1, 'Skill name is required'),\n description: z.string().min(1, 'Skill description is required'),\n})\n\n/**\n * Hook configuration schema\n */\nconst HookConfigSchema = z.object({\n event: z.enum([\n 'PreToolUse',\n 'PostToolUse',\n 'UserPromptSubmit',\n 'SessionStart',\n 'SessionEnd',\n 'Stop',\n 'SubagentStop',\n 'Notification',\n 'PreCompact',\n ] as const),\n matcher: z.string().optional(),\n type: z.enum(['command', 'message', 'notification']),\n command: z.string().optional(),\n message: z.string().optional(),\n blocking: z.boolean().optional(),\n timeout: z.number().optional(),\n})\n\n/**\n * MCP Server configuration schema\n */\nconst MCPServerConfigSchema = z.object({\n command: z.string().min(1, 'MCP server command is required'),\n args: z.array(z.string()).optional(),\n env: z.record(z.string()).optional(),\n timeout: z.number().optional(),\n})\n\n/**\n * Validate complete plugin including manifest and all components\n */\nexport async function validatePlugin(\n pluginPath: string,\n): Promise<ValidationResult> {\n const errors: ValidationError[] = []\n const warnings: ValidationWarning[] = []\n\n // Check if plugin directory exists\n if (!fs.existsSync(pluginPath)) {\n return {\n valid: false,\n errors: [\n {\n type: 'manifest',\n message: `Plugin directory does not exist: ${pluginPath}`,\n },\n ],\n warnings: [],\n }\n }\n\n // Check if it's a directory\n const stats = fs.statSync(pluginPath)\n if (!stats.isDirectory()) {\n return {\n valid: false,\n errors: [\n {\n type: 'manifest',\n message: `Plugin path is not a directory: ${pluginPath}`,\n },\n ],\n warnings: [],\n }\n }\n\n // Validate manifest\n const manifestPath = path.join(pluginPath, 'plugin.json')\n if (!fs.existsSync(manifestPath)) {\n return {\n valid: false,\n errors: [\n {\n type: 'manifest',\n file: 'plugin.json',\n message: 'Plugin manifest (plugin.json) not found',\n },\n ],\n warnings: [],\n }\n }\n\n let manifest: PluginManifest\n try {\n const manifestContent = fs.readFileSync(manifestPath, 'utf-8')\n const manifestData = JSON.parse(manifestContent)\n\n // Validate with Zod schema\n const manifestResult = validateManifest(manifestData)\n errors.push(...manifestResult.errors)\n warnings.push(...manifestResult.warnings)\n\n if (!manifestResult.valid) {\n return { valid: false, errors, warnings }\n }\n\n manifest = manifestData as PluginManifest\n } catch (error) {\n return {\n valid: false,\n errors: [\n {\n type: 'manifest',\n file: 'plugin.json',\n message: `Failed to parse manifest: ${error instanceof Error ? error.message : String(error)}`,\n details: error,\n },\n ],\n warnings: [],\n }\n }\n\n // Validate engines compatibility\n if (manifest.engines) {\n const engineResult = await validateEngines(manifest.engines)\n errors.push(...engineResult.errors)\n warnings.push(...engineResult.warnings)\n }\n\n // Validate agent files\n if (manifest.agents && manifest.agents.length > 0) {\n for (const agentFile of manifest.agents) {\n const agentPath = path.join(pluginPath, agentFile)\n const result = validateAgentFile(agentPath)\n errors.push(...result.errors)\n warnings.push(...result.warnings)\n }\n }\n\n // Validate command files\n if (manifest.commands && manifest.commands.length > 0) {\n for (const commandFile of manifest.commands) {\n const commandPath = path.join(pluginPath, commandFile)\n const result = validateCommandFile(commandPath)\n errors.push(...result.errors)\n warnings.push(...result.warnings)\n }\n }\n\n // Validate skill files\n if (manifest.skills && manifest.skills.length > 0) {\n for (const skillFile of manifest.skills) {\n const skillPath = path.join(pluginPath, skillFile)\n const result = validateSkillFile(skillPath)\n errors.push(...result.errors)\n warnings.push(...result.warnings)\n }\n }\n\n // Validate hook files\n if (manifest.hooks && manifest.hooks.length > 0) {\n for (const hookFile of manifest.hooks) {\n const hookPath = path.join(pluginPath, hookFile)\n const result = validateHookFile(hookPath)\n errors.push(...result.errors)\n warnings.push(...result.warnings)\n }\n }\n\n // Validate MCP server configs\n // mcpServers can be either an array of file paths or an object with inline configs\n if (manifest.mcpServers) {\n if (Array.isArray(manifest.mcpServers) && manifest.mcpServers.length > 0) {\n for (const mcpFile of manifest.mcpServers) {\n const mcpPath = path.join(pluginPath, mcpFile)\n const result = validateMCPServerFile(mcpPath)\n errors.push(...result.errors)\n warnings.push(...result.warnings)\n }\n }\n // If it's an object (inline configs), skip file validation\n }\n\n // Check for unused files warning\n const componentFiles = new Set([\n ...(manifest.agents || []),\n ...(manifest.commands || []),\n ...(manifest.skills || []),\n ...(manifest.hooks || []),\n ...(Array.isArray(manifest.mcpServers) ? manifest.mcpServers : []),\n ])\n\n if (componentFiles.size === 0) {\n warnings.push({\n type: 'manifest',\n message:\n 'Plugin has no components (agents, commands, skills, hooks, or MCP servers)',\n })\n }\n\n return {\n valid: errors.length === 0,\n errors,\n warnings,\n }\n}\n\n/**\n * Validate plugin manifest schema\n */\nexport function validateManifest(manifest: any): ValidationResult {\n const errors: ValidationError[] = []\n const warnings: ValidationWarning[] = []\n\n // Check if manifest is an object\n if (!manifest || typeof manifest !== 'object') {\n return {\n valid: false,\n errors: [\n {\n type: 'manifest',\n message: 'Manifest must be a valid JSON object',\n },\n ],\n warnings: [],\n }\n }\n\n // Validate with Zod schema\n try {\n PluginManifestSchema.parse(manifest)\n } catch (error) {\n if (error instanceof z.ZodError) {\n for (const issue of error.errors) {\n errors.push({\n type: 'manifest',\n message: `${issue.path.join('.')}: ${issue.message}`,\n details: issue,\n })\n }\n } else {\n errors.push({\n type: 'manifest',\n message: `Manifest validation failed: ${error instanceof Error ? error.message : String(error)}`,\n details: error,\n })\n }\n }\n\n // Additional validations\n if (manifest.name && !/^[a-z0-9-]+$/.test(manifest.name)) {\n errors.push({\n type: 'manifest',\n message: 'Plugin name must be lowercase alphanumeric with hyphens only',\n details: { name: manifest.name },\n })\n }\n\n // Validate version format (semver)\n if (manifest.version && !semver.valid(manifest.version)) {\n errors.push({\n type: 'manifest',\n message: `Invalid version format: ${manifest.version}. Must follow semver (e.g., 1.0.0)`,\n details: { version: manifest.version },\n })\n }\n\n // Validate URLs\n if (manifest.homepage) {\n try {\n new URL(manifest.homepage)\n } catch {\n errors.push({\n type: 'manifest',\n message: `Invalid homepage URL: ${manifest.homepage}`,\n })\n }\n }\n\n if (manifest.repository) {\n try {\n new URL(manifest.repository)\n } catch {\n errors.push({\n type: 'manifest',\n message: `Invalid repository URL: ${manifest.repository}`,\n })\n }\n }\n\n // Warn about missing optional but recommended fields\n if (!manifest.description) {\n warnings.push({\n type: 'manifest',\n message: 'Missing recommended field: description',\n })\n }\n\n if (!manifest.author) {\n warnings.push({\n type: 'manifest',\n message: 'Missing recommended field: author',\n })\n }\n\n if (!manifest.license) {\n warnings.push({\n type: 'manifest',\n message: 'Missing recommended field: license',\n })\n }\n\n return {\n valid: errors.length === 0,\n errors,\n warnings,\n }\n}\n\n/**\n * Validate engine version requirements\n */\nasync function validateEngines(engines: {\n minto?: string\n 'claude-code'?: string\n node?: string\n}): Promise<ValidationResult> {\n const errors: ValidationError[] = []\n const warnings: ValidationWarning[] = []\n\n // Validate minto version\n if (engines.minto) {\n const validRange = semver.validRange(engines.minto)\n if (!validRange) {\n errors.push({\n type: 'engine',\n message: `Invalid minto version range: ${engines.minto}`,\n details: { engine: 'minto', range: engines.minto },\n })\n } else {\n // Check compatibility with current version\n try {\n const { VERSION } = await import('../version.js')\n if (!semver.satisfies(VERSION, engines.minto)) {\n warnings.push({\n type: 'engine',\n message: `Plugin requires minto ${engines.minto}, but current version is ${VERSION}`,\n })\n }\n } catch {\n // Unable to check current version, skip this validation\n }\n }\n }\n\n // Validate node version\n if (engines.node) {\n const validRange = semver.validRange(engines.node)\n if (!validRange) {\n errors.push({\n type: 'engine',\n message: `Invalid node version range: ${engines.node}`,\n details: { engine: 'node', range: engines.node },\n })\n } else {\n // Check compatibility with current node version\n if (!semver.satisfies(process.version, engines.node)) {\n warnings.push({\n type: 'engine',\n message: `Plugin requires node ${engines.node}, but current version is ${process.version}`,\n })\n }\n }\n }\n\n return {\n valid: errors.length === 0,\n errors,\n warnings,\n }\n}\n\n/**\n * Validate agent file\n */\nexport function validateAgentFile(filePath: string): ValidationResult {\n const errors: ValidationError[] = []\n const warnings: ValidationWarning[] = []\n\n // Check file exists\n if (!fs.existsSync(filePath)) {\n return {\n valid: false,\n errors: [\n {\n type: 'agent',\n file: filePath,\n message: `Agent file not found: ${filePath}`,\n },\n ],\n warnings: [],\n }\n }\n\n // Check file extension\n const ext = path.extname(filePath)\n if (ext !== '.md' && ext !== '.markdown') {\n warnings.push({\n type: 'agent',\n file: filePath,\n message: `Agent file should have .md or .markdown extension, got: ${ext}`,\n })\n }\n\n // Parse YAML frontmatter\n try {\n const content = fs.readFileSync(filePath, 'utf-8')\n const parsed = matter(content)\n\n // Validate frontmatter exists\n if (!parsed.data || Object.keys(parsed.data).length === 0) {\n errors.push({\n type: 'agent',\n file: filePath,\n message: 'Agent file must have YAML frontmatter',\n })\n return { valid: false, errors, warnings }\n }\n\n // Validate frontmatter schema\n try {\n AgentFrontmatterSchema.parse(parsed.data)\n } catch (error) {\n if (error instanceof z.ZodError) {\n for (const issue of error.errors) {\n errors.push({\n type: 'agent',\n file: filePath,\n message: `Frontmatter ${issue.path.join('.')}: ${issue.message}`,\n details: issue,\n })\n }\n }\n }\n\n // Check for content\n if (!parsed.content || parsed.content.trim().length === 0) {\n warnings.push({\n type: 'agent',\n file: filePath,\n message: 'Agent file has no content after frontmatter',\n })\n }\n\n // Validate tools field\n if (parsed.data.tools) {\n if (Array.isArray(parsed.data.tools)) {\n if (parsed.data.tools.length === 0) {\n errors.push({\n type: 'agent',\n file: filePath,\n message:\n 'Tools array cannot be empty (use \"*\" for all tools or omit the field)',\n })\n }\n } else if (parsed.data.tools !== '*') {\n errors.push({\n type: 'agent',\n file: filePath,\n message: 'Tools must be an array of tool names or \"*\" for all tools',\n details: { tools: parsed.data.tools },\n })\n }\n }\n } catch (error) {\n errors.push({\n type: 'agent',\n file: filePath,\n message: `Failed to parse agent file: ${error instanceof Error ? error.message : String(error)}`,\n details: error,\n })\n }\n\n return {\n valid: errors.length === 0,\n errors,\n warnings,\n }\n}\n\n/**\n * Validate command file\n */\nexport function validateCommandFile(filePath: string): ValidationResult {\n const errors: ValidationError[] = []\n const warnings: ValidationWarning[] = []\n\n // Check file exists\n if (!fs.existsSync(filePath)) {\n return {\n valid: false,\n errors: [\n {\n type: 'command',\n file: filePath,\n message: `Command file not found: ${filePath}`,\n },\n ],\n warnings: [],\n }\n }\n\n // Check file extension\n const ext = path.extname(filePath)\n if (ext !== '.md' && ext !== '.markdown') {\n warnings.push({\n type: 'command',\n file: filePath,\n message: `Command file should have .md or .markdown extension, got: ${ext}`,\n })\n }\n\n // Parse YAML frontmatter\n try {\n const content = fs.readFileSync(filePath, 'utf-8')\n const parsed = matter(content)\n\n // Validate frontmatter exists\n if (!parsed.data || Object.keys(parsed.data).length === 0) {\n errors.push({\n type: 'command',\n file: filePath,\n message: 'Command file must have YAML frontmatter',\n })\n return { valid: false, errors, warnings }\n }\n\n // Validate frontmatter schema\n try {\n CommandFrontmatterSchema.parse(parsed.data)\n } catch (error) {\n if (error instanceof z.ZodError) {\n for (const issue of error.errors) {\n errors.push({\n type: 'command',\n file: filePath,\n message: `Frontmatter ${issue.path.join('.')}: ${issue.message}`,\n details: issue,\n })\n }\n }\n }\n\n // Check for content\n if (!parsed.content || parsed.content.trim().length === 0) {\n warnings.push({\n type: 'command',\n file: filePath,\n message: 'Command file has no content after frontmatter',\n })\n }\n\n // Validate name matches filename\n const filename = path.basename(filePath, path.extname(filePath))\n if (parsed.data.name && parsed.data.name !== filename) {\n warnings.push({\n type: 'command',\n file: filePath,\n message: `Command name \"${parsed.data.name}\" does not match filename \"${filename}\"`,\n })\n }\n } catch (error) {\n errors.push({\n type: 'command',\n file: filePath,\n message: `Failed to parse command file: ${error instanceof Error ? error.message : String(error)}`,\n details: error,\n })\n }\n\n return {\n valid: errors.length === 0,\n errors,\n warnings,\n }\n}\n\n/**\n * Validate skill file\n */\nexport function validateSkillFile(filePath: string): ValidationResult {\n const errors: ValidationError[] = []\n const warnings: ValidationWarning[] = []\n\n // Check file exists\n if (!fs.existsSync(filePath)) {\n return {\n valid: false,\n errors: [\n {\n type: 'skill',\n file: filePath,\n message: `Skill file not found: ${filePath}`,\n },\n ],\n warnings: [],\n }\n }\n\n // Check file extension\n const ext = path.extname(filePath)\n if (ext !== '.md' && ext !== '.markdown') {\n warnings.push({\n type: 'skill',\n file: filePath,\n message: `Skill file should have .md or .markdown extension, got: ${ext}`,\n })\n }\n\n // Parse YAML frontmatter\n try {\n const content = fs.readFileSync(filePath, 'utf-8')\n const parsed = matter(content)\n\n // Validate frontmatter exists\n if (!parsed.data || Object.keys(parsed.data).length === 0) {\n errors.push({\n type: 'skill',\n file: filePath,\n message: 'Skill file must have YAML frontmatter',\n })\n return { valid: false, errors, warnings }\n }\n\n // Validate frontmatter schema\n try {\n SkillFrontmatterSchema.parse(parsed.data)\n } catch (error) {\n if (error instanceof z.ZodError) {\n for (const issue of error.errors) {\n errors.push({\n type: 'skill',\n file: filePath,\n message: `Frontmatter ${issue.path.join('.')}: ${issue.message}`,\n details: issue,\n })\n }\n }\n }\n\n // Check for content\n if (!parsed.content || parsed.content.trim().length === 0) {\n warnings.push({\n type: 'skill',\n file: filePath,\n message: 'Skill file has no content after frontmatter',\n })\n }\n } catch (error) {\n errors.push({\n type: 'skill',\n file: filePath,\n message: `Failed to parse skill file: ${error instanceof Error ? error.message : String(error)}`,\n details: error,\n })\n }\n\n return {\n valid: errors.length === 0,\n errors,\n warnings,\n }\n}\n\n/**\n * Validate hook file\n */\nexport function validateHookFile(filePath: string): ValidationResult {\n const errors: ValidationError[] = []\n const warnings: ValidationWarning[] = []\n\n // Check file exists\n if (!fs.existsSync(filePath)) {\n return {\n valid: false,\n errors: [\n {\n type: 'hook',\n file: filePath,\n message: `Hook file not found: ${filePath}`,\n },\n ],\n warnings: [],\n }\n }\n\n // Check file extension\n const ext = path.extname(filePath)\n const validExtensions = ['.js', '.mjs', '.ts', '.tsx', '.json']\n if (!validExtensions.includes(ext)) {\n errors.push({\n type: 'hook',\n file: filePath,\n message: `Hook file must have extension: ${validExtensions.join(', ')}. Got: ${ext}`,\n })\n return { valid: false, errors, warnings }\n }\n\n // For JSON hook configs, validate structure\n if (ext === '.json') {\n try {\n const content = fs.readFileSync(filePath, 'utf-8')\n const config = JSON.parse(content)\n\n // Validate hook config schema\n try {\n HookConfigSchema.parse(config)\n } catch (error) {\n if (error instanceof z.ZodError) {\n for (const issue of error.errors) {\n errors.push({\n type: 'hook',\n file: filePath,\n message: `Hook config ${issue.path.join('.')}: ${issue.message}`,\n details: issue,\n })\n }\n }\n }\n\n // Type-specific validation\n if (config.type === 'command' && !config.command) {\n errors.push({\n type: 'hook',\n file: filePath,\n message: 'Hook type \"command\" requires a \"command\" field',\n })\n }\n\n if (config.type === 'message' && !config.message) {\n errors.push({\n type: 'hook',\n file: filePath,\n message: 'Hook type \"message\" requires a \"message\" field',\n })\n }\n } catch (error) {\n errors.push({\n type: 'hook',\n file: filePath,\n message: `Failed to parse hook config: ${error instanceof Error ? error.message : String(error)}`,\n details: error,\n })\n }\n } else {\n // For JS/TS files, just check they're readable\n try {\n fs.readFileSync(filePath, 'utf-8')\n } catch (error) {\n errors.push({\n type: 'hook',\n file: filePath,\n message: `Failed to read hook file: ${error instanceof Error ? error.message : String(error)}`,\n details: error,\n })\n }\n\n // Warn about JS/TS hooks needing runtime validation\n warnings.push({\n type: 'hook',\n file: filePath,\n message: 'JavaScript/TypeScript hooks will be validated at runtime',\n })\n }\n\n return {\n valid: errors.length === 0,\n errors,\n warnings,\n }\n}\n\n/**\n * Validate MCP server configuration file\n */\nexport function validateMCPServerFile(filePath: string): ValidationResult {\n const errors: ValidationError[] = []\n const warnings: ValidationWarning[] = []\n\n // Check file exists\n if (!fs.existsSync(filePath)) {\n return {\n valid: false,\n errors: [\n {\n type: 'mcp',\n file: filePath,\n message: `MCP server config file not found: ${filePath}`,\n },\n ],\n warnings: [],\n }\n }\n\n // Check file extension\n const ext = path.extname(filePath)\n if (ext !== '.json') {\n errors.push({\n type: 'mcp',\n file: filePath,\n message: `MCP server config must be a JSON file, got: ${ext}`,\n })\n return { valid: false, errors, warnings }\n }\n\n // Parse and validate JSON\n try {\n const content = fs.readFileSync(filePath, 'utf-8')\n const config = JSON.parse(content)\n\n // Validate MCP config schema\n try {\n MCPServerConfigSchema.parse(config)\n } catch (error) {\n if (error instanceof z.ZodError) {\n for (const issue of error.errors) {\n errors.push({\n type: 'mcp',\n file: filePath,\n message: `MCP config ${issue.path.join('.')}: ${issue.message}`,\n details: issue,\n })\n }\n }\n }\n\n // Validate command is not empty\n if (config.command && config.command.trim().length === 0) {\n errors.push({\n type: 'mcp',\n file: filePath,\n message: 'MCP server command cannot be empty',\n })\n }\n\n // Warn about timeout\n if (config.timeout && config.timeout < 1000) {\n warnings.push({\n type: 'mcp',\n file: filePath,\n message: `MCP server timeout is very low (${config.timeout}ms), consider increasing`,\n })\n }\n } catch (error) {\n errors.push({\n type: 'mcp',\n file: filePath,\n message: `Failed to parse MCP server config: ${error instanceof Error ? error.message : String(error)}`,\n details: error,\n })\n }\n\n return {\n valid: errors.length === 0,\n errors,\n warnings,\n }\n}\n\n/**\n * Validate plugin dependencies\n */\nexport function validateDependencies(\n dependencies: Record<string, string>,\n installedPackages?: Set<string>,\n): ValidationResult {\n const errors: ValidationError[] = []\n const warnings: ValidationWarning[] = []\n\n for (const [pkg, version] of Object.entries(dependencies)) {\n // Validate version format\n const validRange = semver.validRange(version)\n if (!validRange) {\n errors.push({\n type: 'dependency',\n message: `Invalid version range for ${pkg}: ${version}`,\n details: { package: pkg, version },\n })\n }\n\n // Check if installed (if provided)\n if (installedPackages && !installedPackages.has(pkg)) {\n warnings.push({\n type: 'dependency',\n message: `Dependency not installed: ${pkg}@${version}`,\n })\n }\n }\n\n return {\n valid: errors.length === 0,\n errors,\n warnings,\n }\n}\n\n/**\n * Batch validate multiple plugins\n */\nexport async function validatePlugins(\n pluginPaths: string[],\n): Promise<Map<string, ValidationResult>> {\n const results = new Map<string, ValidationResult>()\n\n await Promise.all(\n pluginPaths.map(async pluginPath => {\n const result = await validatePlugin(pluginPath)\n results.set(pluginPath, result)\n }),\n )\n\n return results\n}\n\n/**\n * Format validation result as human-readable string\n */\nexport function formatValidationResult(\n result: ValidationResult,\n pluginName?: string,\n): string {\n const lines: string[] = []\n\n if (pluginName) {\n lines.push(`Plugin: ${pluginName}`)\n }\n\n if (result.valid) {\n lines.push('\u2713 Validation passed')\n } else {\n lines.push(`\u2717 Validation failed with ${result.errors.length} error(s)`)\n }\n\n if (result.errors.length > 0) {\n lines.push('\\nErrors:')\n for (const error of result.errors) {\n const location = error.file ? ` (${error.file})` : ''\n lines.push(` \u2022 [${error.type}]${location} ${error.message}`)\n }\n }\n\n if (result.warnings.length > 0) {\n lines.push('\\nWarnings:')\n for (const warning of result.warnings) {\n const location = warning.file ? ` (${warning.file})` : ''\n lines.push(` \u2022 [${warning.type}]${location} ${warning.message}`)\n }\n }\n\n return lines.join('\\n')\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/utils/ptyCompat.ts"],
4
- "sourcesContent": ["/**\n * PTY Compatibility Utilities\n *\n * This module provides utilities to detect and handle environments where\n * setRawMode is not supported (e.g., Docker containers, VS Code terminals,\n * SSH sessions with incomplete TTY forwarding, etc.)\n */\n\nimport { ReadStream } from 'tty'\n\nexport interface RawModeCheckResult {\n supported: boolean\n reason?: string\n environmentHint?: string\n}\n\n/**\n * Validate if a stream truly supports raw mode\n *\n * Note: Some environments report isTTY=true but still fail on setRawMode\n * with EIO errors. This function performs an actual test.\n *\n * @param stream - The input stream to test (usually process.stdin)\n * @returns Check result with support status and diagnostic info\n */\nexport function validateRawModeSupport(\n stream: NodeJS.ReadStream,\n): RawModeCheckResult {\n // Check 1: Must be a TTY\n if (!stream.isTTY) {\n return {\n supported: false,\n reason: 'Not a TTY device',\n environmentHint: 'piped input or redirected stream',\n }\n }\n\n // Check 2: Must have setRawMode method\n if (typeof (stream as any).setRawMode !== 'function') {\n return {\n supported: false,\n reason: 'setRawMode method not available',\n environmentHint: 'non-standard TTY implementation',\n }\n }\n\n // Check 3: Actually test setRawMode (catches EIO errors)\n try {\n const ttyStream = stream as ReadStream\n ttyStream.setRawMode(true)\n ttyStream.setRawMode(false)\n return { supported: true }\n } catch (err) {\n const error = err as NodeJS.ErrnoException\n const errorCode = error.code || 'UNKNOWN'\n const errorMsg = error.message || 'Unknown error'\n\n // Detect common problematic environments\n let environmentHint = 'incompatible terminal environment'\n\n if (errorCode === 'EIO') {\n // EIO is the most common error in problematic PTY environments\n environmentHint = 'Docker container, VS Code terminal, or nested tmux/screen'\n } else if (errorCode === 'ENOTTY') {\n environmentHint = 'not connected to a terminal device'\n }\n\n return {\n supported: false,\n reason: `setRawMode failed: ${errorCode} - ${errorMsg}`,\n environmentHint,\n }\n }\n}\n\n/**\n * Quick check function that exits the process if raw mode is not supported\n * Use this for commands that absolutely require interactive mode\n *\n * @param stream - The input stream to check\n * @param exitOnFailure - Whether to exit process on failure (default: true)\n */\nexport function requireRawModeOrExit(\n stream: NodeJS.ReadStream,\n exitOnFailure = true,\n): boolean {\n const result = validateRawModeSupport(stream)\n\n if (!result.supported) {\n console.error('')\n console.error('\u274C Interactive mode is not supported in this environment')\n console.error(` Reason: ${result.reason}`)\n if (result.environmentHint) {\n console.error(` Likely: ${result.environmentHint}`)\n }\n console.error('')\n console.error('\uD83D\uDCCB Solutions:')\n console.error(' 1. Use a native terminal application:')\n console.error(' \u2022 macOS: Terminal.app, iTerm2, Warp')\n console.error(' \u2022 Linux: GNOME Terminal, Konsole, xterm')\n console.error(' \u2022 Windows: Windows Terminal, ConEmu')\n console.error('')\n console.error(' 2. Skip interactive onboarding:')\n console.error(' export ANTHROPIC_API_KEY=\"sk-ant-...\"')\n console.error(' minto config set -g hasCompletedOnboarding true')\n console.error('')\n console.error(' 3. Use non-interactive mode:')\n console.error(' minto --print \"your prompt here\"')\n console.error(' echo \"prompt\" | minto')\n console.error('')\n\n if (exitOnFailure) {\n process.exit(1)\n }\n return false\n }\n\n return true\n}\n\n/**\n * Get a user-friendly description of the current terminal environment\n */\nexport function describeTerminalEnvironment(): string {\n const descriptions: string[] = []\n\n if (process.stdin.isTTY) {\n descriptions.push('TTY')\n } else {\n descriptions.push('non-TTY')\n }\n\n if (process.env.TERM) {\n descriptions.push(`TERM=${process.env.TERM}`)\n }\n\n if (process.env.TERM_PROGRAM) {\n descriptions.push(`${process.env.TERM_PROGRAM}`)\n }\n\n // Detect common environments\n if (process.env.VSCODE_INJECTION) {\n descriptions.push('VS Code terminal')\n }\n\n if (process.env.TMUX) {\n descriptions.push('tmux session')\n }\n\n if (process.env.STY) {\n descriptions.push('GNU screen')\n }\n\n // Check if running in Docker\n try {\n const fs = require('fs')\n if (fs.existsSync('/.dockerenv')) {\n descriptions.push('Docker container')\n }\n } catch {\n // Ignore\n }\n\n return descriptions.join(', ')\n}\n\n/**\n * Log diagnostic information about the terminal environment\n * Useful for debugging PTY compatibility issues\n */\nexport function logTerminalDiagnostics(): void {\n console.log('')\n console.log('\uD83D\uDD0D Terminal Environment Diagnostics:')\n console.log(` Description: ${describeTerminalEnvironment()}`)\n console.log(` stdin.isTTY: ${process.stdin.isTTY}`)\n console.log(` stdout.isTTY: ${process.stdout.isTTY}`)\n console.log(\n ` setRawMode available: ${typeof (process.stdin as any).setRawMode === 'function'}`,\n )\n\n const result = validateRawModeSupport(process.stdin)\n console.log(` Raw mode supported: ${result.supported}`)\n if (!result.supported) {\n console.log(` Reason: ${result.reason}`)\n if (result.environmentHint) {\n console.log(` Hint: ${result.environmentHint}`)\n }\n }\n console.log('')\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * PTY Compatibility Utilities\n *\n * This module provides utilities to detect and handle environments where\n * setRawMode is not supported (e.g., Docker containers, VS Code terminals,\n * SSH sessions with incomplete TTY forwarding, etc.)\n */\n\nimport { ReadStream } from 'tty'\n\nexport interface RawModeCheckResult {\n supported: boolean\n reason?: string\n environmentHint?: string\n}\n\n/**\n * Validate if a stream truly supports raw mode\n *\n * Note: Some environments report isTTY=true but still fail on setRawMode\n * with EIO errors. This function performs an actual test.\n *\n * @param stream - The input stream to test (usually process.stdin)\n * @returns Check result with support status and diagnostic info\n */\nexport function validateRawModeSupport(\n stream: NodeJS.ReadStream,\n): RawModeCheckResult {\n // Check 1: Must be a TTY\n if (!stream.isTTY) {\n return {\n supported: false,\n reason: 'Not a TTY device',\n environmentHint: 'piped input or redirected stream',\n }\n }\n\n // Check 2: Must have setRawMode method\n if (typeof (stream as any).setRawMode !== 'function') {\n return {\n supported: false,\n reason: 'setRawMode method not available',\n environmentHint: 'non-standard TTY implementation',\n }\n }\n\n // Check 3: Actually test setRawMode (catches EIO errors)\n try {\n const ttyStream = stream as ReadStream\n ttyStream.setRawMode(true)\n ttyStream.setRawMode(false)\n return { supported: true }\n } catch (err) {\n const error = err as NodeJS.ErrnoException\n const errorCode = error.code || 'UNKNOWN'\n const errorMsg = error.message || 'Unknown error'\n\n // Detect common problematic environments\n let environmentHint = 'incompatible terminal environment'\n\n if (errorCode === 'EIO') {\n // EIO is the most common error in problematic PTY environments\n environmentHint =\n 'Docker container, VS Code terminal, or nested tmux/screen'\n } else if (errorCode === 'ENOTTY') {\n environmentHint = 'not connected to a terminal device'\n }\n\n return {\n supported: false,\n reason: `setRawMode failed: ${errorCode} - ${errorMsg}`,\n environmentHint,\n }\n }\n}\n\n/**\n * Quick check function that exits the process if raw mode is not supported\n * Use this for commands that absolutely require interactive mode\n *\n * @param stream - The input stream to check\n * @param exitOnFailure - Whether to exit process on failure (default: true)\n */\nexport function requireRawModeOrExit(\n stream: NodeJS.ReadStream,\n exitOnFailure = true,\n): boolean {\n const result = validateRawModeSupport(stream)\n\n if (!result.supported) {\n console.error('')\n console.error('\u274C Interactive mode is not supported in this environment')\n console.error(` Reason: ${result.reason}`)\n if (result.environmentHint) {\n console.error(` Likely: ${result.environmentHint}`)\n }\n console.error('')\n console.error('\uD83D\uDCCB Solutions:')\n console.error(' 1. Use a native terminal application:')\n console.error(' \u2022 macOS: Terminal.app, iTerm2, Warp')\n console.error(' \u2022 Linux: GNOME Terminal, Konsole, xterm')\n console.error(' \u2022 Windows: Windows Terminal, ConEmu')\n console.error('')\n console.error(' 2. Skip interactive onboarding:')\n console.error(' export ANTHROPIC_API_KEY=\"sk-ant-...\"')\n console.error(' minto config set -g hasCompletedOnboarding true')\n console.error('')\n console.error(' 3. Use non-interactive mode:')\n console.error(' minto --print \"your prompt here\"')\n console.error(' echo \"prompt\" | minto')\n console.error('')\n\n if (exitOnFailure) {\n process.exit(1)\n }\n return false\n }\n\n return true\n}\n\n/**\n * Get a user-friendly description of the current terminal environment\n */\nexport function describeTerminalEnvironment(): string {\n const descriptions: string[] = []\n\n if (process.stdin.isTTY) {\n descriptions.push('TTY')\n } else {\n descriptions.push('non-TTY')\n }\n\n if (process.env.TERM) {\n descriptions.push(`TERM=${process.env.TERM}`)\n }\n\n if (process.env.TERM_PROGRAM) {\n descriptions.push(`${process.env.TERM_PROGRAM}`)\n }\n\n // Detect common environments\n if (process.env.VSCODE_INJECTION) {\n descriptions.push('VS Code terminal')\n }\n\n if (process.env.TMUX) {\n descriptions.push('tmux session')\n }\n\n if (process.env.STY) {\n descriptions.push('GNU screen')\n }\n\n // Check if running in Docker\n try {\n const fs = require('fs')\n if (fs.existsSync('/.dockerenv')) {\n descriptions.push('Docker container')\n }\n } catch {\n // Ignore\n }\n\n return descriptions.join(', ')\n}\n\n/**\n * Log diagnostic information about the terminal environment\n * Useful for debugging PTY compatibility issues\n */\nexport function logTerminalDiagnostics(): void {\n console.log('')\n console.log('\uD83D\uDD0D Terminal Environment Diagnostics:')\n console.log(` Description: ${describeTerminalEnvironment()}`)\n console.log(` stdin.isTTY: ${process.stdin.isTTY}`)\n console.log(` stdout.isTTY: ${process.stdout.isTTY}`)\n console.log(\n ` setRawMode available: ${typeof (process.stdin as any).setRawMode === 'function'}`,\n )\n\n const result = validateRawModeSupport(process.stdin)\n console.log(` Raw mode supported: ${result.supported}`)\n if (!result.supported) {\n console.log(` Reason: ${result.reason}`)\n if (result.environmentHint) {\n console.log(` Hint: ${result.environmentHint}`)\n }\n }\n console.log('')\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/utils/roundConverter.ts"],
4
- "sourcesContent": ["/**\n * Round Converter Utility\n *\n * This module provides functions to convert flat message arrays into\n * hierarchical InteractionRound structures for improved UI rendering.\n *\n * Key responsibilities:\n * - Group messages into user-assistant interaction rounds\n * - Extract tool executions from assistant messages\n * - Extract subagent executions from TaskTool invocations\n * - Merge timeline items (tools + subagents) by timestamp\n * - Track todo changes across rounds\n *\n * @module utils/roundConverter\n */\n\nimport type { UUID } from 'crypto'\nimport type {\n Message,\n UserMessage,\n AssistantMessage,\n ProgressMessage,\n} from '../types/conversation'\nimport type {\n InteractionRound,\n ToolExecution,\n SubagentExecution,\n ThinkingState,\n TodoChange,\n TodoSnapshot,\n TimelineItem,\n} from '../types/interactionRound'\nimport type { SubagentState } from '../types/subagent'\nimport type { TodoItem } from '@utils/todoStorage'\n\n/**\n * Converts a flat array of messages into structured InteractionRound objects.\n *\n * Algorithm:\n * 1. Iterate through messages sequentially\n * 2. Each UserMessage starts a new round\n * 3. Following AssistantMessages and ProgressMessages belong to that round\n * 4. Extract tool executions, subagent executions, and thinking states\n * 5. Compute round-level metrics (duration, cost, etc.)\n *\n * @param messages - Array of conversation messages to convert\n * @param currentTodos - Optional: Current todo list for the latest round\n * @param subagents - Optional: Array of subagent states to include in rounds\n * @returns Array of structured interaction rounds\n */\nexport function convertMessagesToRounds(\n messages: Message[],\n currentTodos?: TodoItem[],\n subagents?: SubagentState[],\n): InteractionRound[] {\n const rounds: InteractionRound[] = []\n let currentRound: Partial<InteractionRound> | null = null\n let previousTodos: TodoItem[] = []\n\n for (let i = 0; i < messages.length; i++) {\n const message = messages[i]\n\n if (message.type === 'user') {\n // Save the previous round if it exists\n if (currentRound && currentRound.userMessage) {\n rounds.push(finalizeRound(currentRound))\n }\n\n // Start a new round\n currentRound = {\n uuid: message.uuid,\n userMessage: message,\n toolExecutions: [],\n subagentExecutions: [],\n startTime: Date.now(), // We'll update this from actual message timestamps if available\n }\n } else if (message.type === 'assistant' && currentRound) {\n // Process assistant message\n const assistantMsg = message as AssistantMessage\n\n // Check if this message contains tool_use blocks\n const hasToolUse = assistantMsg.message.content.some(\n (block: any) => block.type === 'tool_use',\n )\n\n // Check if this message has text content\n const hasText = assistantMsg.message.content.some(\n (block: any) => block.type === 'text' && block.text.trim().length > 0,\n )\n\n if (hasToolUse) {\n // This message contains tool executions\n // Extract tool executions from this message\n const toolExecutions = extractToolExecutions(assistantMsg)\n currentRound.toolExecutions = [\n ...(currentRound.toolExecutions || []),\n ...toolExecutions,\n ]\n\n // If this message also has text content, it might be thinking or commentary\n if (hasText && !currentRound.agentThinking) {\n const textContent = assistantMsg.message.content\n .filter((block: any) => block.type === 'text')\n .map((block: any) => block.text)\n .join('\\n')\n\n if (textContent.trim()) {\n currentRound.agentThinking = {\n message: textContent,\n startTime: Date.now(),\n durationMs: assistantMsg.durationMs,\n }\n }\n }\n } else if (hasText) {\n // This is a text-only assistant message\n const textContent = assistantMsg.message.content\n .filter((block: any) => block.type === 'text')\n .map((block: any) => block.text)\n .join('\\n')\n\n // Strategy: Look ahead to determine the role of this message\n // - If next message is user \u2192 this is final response (round complete)\n // - If next message is assistant with tools \u2192 this is thinking\n // - If this is the last message \u2192 this is final response (potentially in-progress)\n // - If we already have tools \u2192 this is final response after tools\n\n const nextMessage = messages[i + 1]\n const nextIsUser = nextMessage && nextMessage.type === 'user'\n const nextIsAssistantWithTools = nextMessage &&\n nextMessage.type === 'assistant' &&\n (nextMessage as AssistantMessage).message.content.some(\n (block: any) => block.type === 'tool_use',\n )\n const isLastMessage = i === messages.length - 1\n const hasToolsAlready = currentRound.toolExecutions && currentRound.toolExecutions.length > 0\n\n if (hasToolsAlready || nextIsUser || isLastMessage) {\n // This is the final response\n currentRound.agentResponse = assistantMsg\n currentRound.endTime = Date.now()\n currentRound.durationMs = assistantMsg.durationMs\n currentRound.totalCostUSD =\n (currentRound.totalCostUSD || 0) + assistantMsg.costUSD\n } else if (nextIsAssistantWithTools && !currentRound.agentThinking) {\n // This is thinking (agent reasoning before using tools)\n currentRound.agentThinking = {\n message: textContent,\n startTime: Date.now(),\n durationMs: assistantMsg.durationMs,\n }\n } else {\n // Default: treat as final response\n currentRound.agentResponse = assistantMsg\n currentRound.endTime = Date.now()\n currentRound.durationMs = assistantMsg.durationMs\n currentRound.totalCostUSD =\n (currentRound.totalCostUSD || 0) + assistantMsg.costUSD\n }\n }\n } else if (message.type === 'progress' && currentRound) {\n // Process progress message (tool results)\n // Subagent info is extracted from the subagents parameter instead\n }\n }\n\n // Finalize the last round\n if (currentRound && currentRound.userMessage) {\n // Add current todos to the last round if provided\n if (currentTodos) {\n const changes = calculateTodoChanges(previousTodos, currentTodos)\n currentRound.todoSnapshot = {\n todos: currentTodos,\n timestamp: Date.now(),\n changes,\n }\n }\n rounds.push(finalizeRound(currentRound))\n }\n\n // Assign subagents to rounds based on their start time\n // For now, assign all subagents to the last round if it exists\n // TODO: Improve this to assign subagents to the correct round based on timing\n if (rounds.length > 0 && subagents && subagents.length > 0) {\n const lastRound = rounds[rounds.length - 1]\n lastRound.subagentExecutions = subagents\n }\n\n return rounds\n}\n\n/**\n * Extracts tool execution records from an assistant message.\n *\n * Parses the message content blocks to find tool_use blocks and creates\n * ToolExecution objects with the appropriate status and metadata.\n *\n * @param message - Assistant message potentially containing tool uses\n * @returns Array of tool execution records\n */\nexport function extractToolExecutions(\n message: AssistantMessage,\n): ToolExecution[] {\n const toolExecutions: ToolExecution[] = []\n\n if (!message.message.content) {\n return toolExecutions\n }\n\n for (const block of message.message.content) {\n if (block.type === 'tool_use') {\n const toolExec: ToolExecution = {\n id: block.id,\n name: block.name,\n input: block.input,\n status: 'completed', // Assume completed if we're seeing it\n startTime: Date.now(), // Ideally extract from message metadata\n endTime: Date.now(),\n durationMs: message.durationMs,\n }\n toolExecutions.push(toolExec)\n }\n }\n\n return toolExecutions\n}\n\n/**\n * Extracts subagent execution records from TaskTool invocations.\n *\n * NOTE: Currently not used as we're getting SubagentState directly from REPL.tsx.\n * Keeping this for potential future use or reference.\n *\n * @param progressMessage - Progress message containing subagent event data\n * @returns Subagent execution record or null if not a subagent message\n */\n/*\nexport function extractSubagentExecution(\n progressMessage: ProgressMessage,\n): SubagentExecution | null {\n if (!progressMessage.subagentEvent) {\n return null\n }\n\n const event = progressMessage.subagentEvent\n const metrics = event.metrics || {\n toolUseCount: 0,\n tokenCount: 0,\n startTime: Date.now(),\n }\n\n // Build subagent execution from event data\n const subagentExec: SubagentExecution = {\n id: event.id,\n taskName: event.taskName || 'Unknown Task',\n agentType: event.agentType || 'unknown',\n status: mapEventTypeToStatus(event.eventType),\n messages: {\n toolExecutions: [],\n },\n metrics,\n }\n\n // Extract tool executions from the progress message content\n if (progressMessage.content && progressMessage.content.message) {\n const toolExecutions = extractToolExecutions(progressMessage.content)\n subagentExec.messages.toolExecutions = toolExecutions\n }\n\n // Add response message if this is a completion event\n if (event.eventType === 'complete' && progressMessage.content) {\n subagentExec.messages.response = progressMessage.content\n }\n\n // Add error if present\n if (event.eventType === 'error' && event.error) {\n subagentExec.error = event.error\n }\n\n return subagentExec\n}\n*/\n\n/**\n * Merges tool executions and subagent executions into a single timeline\n * sorted by start time.\n *\n * This enables chronological rendering where tools and subagents appear\n * in the order they were actually executed.\n *\n * @param toolExecutions - Array of tool execution records\n * @param subagentExecutions - Array of subagent state records\n * @returns Sorted array of timeline items\n */\nexport function mergeTimelineItems(\n toolExecutions: ToolExecution[],\n subagentExecutions: SubagentState[],\n): TimelineItem[] {\n const timeline: TimelineItem[] = []\n\n // Add tool executions to timeline\n for (const tool of toolExecutions) {\n timeline.push({\n type: 'tool',\n data: tool,\n time: tool.startTime,\n })\n }\n\n // Add subagent executions to timeline\n for (const subagent of subagentExecutions) {\n timeline.push({\n type: 'subagent',\n data: subagent,\n time: subagent.metrics.startTime,\n })\n }\n\n // Sort by start time\n timeline.sort((a, b) => a.time - b.time)\n\n return timeline\n}\n\n/**\n * Calculates the differences between two todo lists.\n *\n * Identifies added, removed, updated, and status-changed todos to generate\n * a complete change log for display purposes.\n *\n * @param oldTodos - Previous todo list state\n * @param newTodos - Current todo list state\n * @returns Array of todo change records\n */\nexport function calculateTodoChanges(\n oldTodos: TodoItem[],\n newTodos: TodoItem[],\n): TodoChange[] {\n const changes: TodoChange[] = []\n const timestamp = Date.now()\n\n // Create maps for efficient lookup\n const oldTodoMap = new Map(oldTodos.map(todo => [todo.id, todo]))\n const newTodoMap = new Map(newTodos.map(todo => [todo.id, todo]))\n\n // Check for additions and updates\n for (const newTodo of newTodos) {\n const oldTodo = oldTodoMap.get(newTodo.id)\n\n if (!oldTodo) {\n // New todo added\n changes.push({\n type: 'added',\n todoId: newTodo.id,\n newValue: newTodo,\n timestamp,\n })\n } else {\n // Check for status change\n if (oldTodo.status !== newTodo.status) {\n changes.push({\n type: 'status_changed',\n todoId: newTodo.id,\n oldValue: oldTodo,\n newValue: newTodo,\n timestamp,\n })\n } else if (JSON.stringify(oldTodo) !== JSON.stringify(newTodo)) {\n // Other fields changed\n changes.push({\n type: 'updated',\n todoId: newTodo.id,\n oldValue: oldTodo,\n newValue: newTodo,\n timestamp,\n })\n }\n }\n }\n\n // Check for removals\n for (const oldTodo of oldTodos) {\n if (!newTodoMap.has(oldTodo.id)) {\n changes.push({\n type: 'removed',\n todoId: oldTodo.id,\n oldValue: oldTodo,\n timestamp,\n })\n }\n }\n\n return changes\n}\n\n// ============================================================================\n// Helper Functions\n// ============================================================================\n\n/**\n * Finalizes a partial interaction round by computing aggregate metrics\n * and ensuring all required fields are present.\n *\n * @param partialRound - Partial round object to finalize\n * @returns Complete interaction round\n */\nfunction finalizeRound(\n partialRound: Partial<InteractionRound>,\n): InteractionRound {\n // Compute total cost from all messages\n let totalCost = 0\n // Note: agentThinking.message is now a string, cost is tracked separately\n // Cost is primarily tracked in agentResponse\n if (partialRound.agentResponse) {\n totalCost += partialRound.agentResponse.costUSD\n }\n\n // Compute duration if not already set\n const startTime = partialRound.startTime || Date.now()\n const endTime = partialRound.endTime || Date.now()\n const durationMs = partialRound.durationMs || endTime - startTime\n\n return {\n uuid: partialRound.uuid!,\n userMessage: partialRound.userMessage!,\n agentThinking: partialRound.agentThinking,\n toolExecutions: partialRound.toolExecutions || [],\n subagentExecutions: partialRound.subagentExecutions || [],\n agentResponse: partialRound.agentResponse,\n todoSnapshot: partialRound.todoSnapshot,\n startTime,\n endTime,\n durationMs,\n totalCostUSD: totalCost,\n }\n}\n\n/**\n * Maps subagent event types to execution status values.\n *\n * @param eventType - Subagent event type\n * @returns Corresponding execution status\n */\nfunction mapEventTypeToStatus(\n eventType: string,\n): 'initializing' | 'running' | 'completed' | 'error' {\n switch (eventType) {\n case 'start':\n return 'initializing'\n case 'progress':\n return 'running'\n case 'complete':\n return 'completed'\n case 'error':\n return 'error'\n default:\n return 'running'\n }\n}\n\n/**\n * Extracts thinking state from the first assistant message in a round\n * that doesn't contain tool uses.\n *\n * @param messages - Array of messages to search\n * @param roundStartIndex - Index where the current round starts\n * @returns Thinking state or undefined\n */\nexport function extractThinkingState(\n messages: Message[],\n roundStartIndex: number,\n): ThinkingState | undefined {\n for (let i = roundStartIndex; i < messages.length; i++) {\n const message = messages[i]\n\n if (message.type === 'assistant') {\n const assistantMsg = message as AssistantMessage\n const hasToolUse = assistantMsg.message.content.some(\n (block: any) => block.type === 'tool_use',\n )\n\n if (!hasToolUse) {\n // Extract text content from assistant message\n const textContent = assistantMsg.message.content\n .filter((block: any) => block.type === 'text')\n .map((block: any) => block.text)\n .join('\\n')\n\n return {\n message: textContent,\n startTime: Date.now(), // Ideally from message metadata\n durationMs: assistantMsg.durationMs,\n }\n }\n } else if (message.type === 'user') {\n // Stop searching when we hit the next user message\n break\n }\n }\n\n return undefined\n}\n\n/**\n * Gets the current in-progress tool or subagent from a round.\n * Useful for rendering live progress indicators.\n *\n * @param round - Interaction round to check\n * @returns Timeline item that's currently in progress, or null\n */\nexport function getCurrentActivity(\n round: InteractionRound,\n): TimelineItem | null {\n // Check tools\n for (const tool of round.toolExecutions) {\n if (tool.status === 'running' || tool.status === 'pending') {\n return { type: 'tool', data: tool, time: tool.startTime }\n }\n }\n\n // Check subagents\n for (const subagent of round.subagentExecutions) {\n if (subagent.status === 'running' || subagent.status === 'initializing') {\n return {\n type: 'subagent',\n data: subagent,\n time: subagent.metrics.startTime,\n }\n }\n }\n\n return null\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * Round Converter Utility\n *\n * This module provides functions to convert flat message arrays into\n * hierarchical InteractionRound structures for improved UI rendering.\n *\n * Key responsibilities:\n * - Group messages into user-assistant interaction rounds\n * - Extract tool executions from assistant messages\n * - Extract subagent executions from TaskTool invocations\n * - Merge timeline items (tools + subagents) by timestamp\n * - Track todo changes across rounds\n *\n * @module utils/roundConverter\n */\n\nimport type { UUID } from 'crypto'\nimport type {\n Message,\n UserMessage,\n AssistantMessage,\n ProgressMessage,\n} from '../types/conversation'\nimport type {\n InteractionRound,\n ToolExecution,\n SubagentExecution,\n ThinkingState,\n TodoChange,\n TodoSnapshot,\n TimelineItem,\n} from '../types/interactionRound'\nimport type { SubagentState } from '../types/subagent'\nimport type { TodoItem } from '@utils/todoStorage'\n\n/**\n * Converts a flat array of messages into structured InteractionRound objects.\n *\n * Algorithm:\n * 1. Iterate through messages sequentially\n * 2. Each UserMessage starts a new round\n * 3. Following AssistantMessages and ProgressMessages belong to that round\n * 4. Extract tool executions, subagent executions, and thinking states\n * 5. Compute round-level metrics (duration, cost, etc.)\n *\n * @param messages - Array of conversation messages to convert\n * @param currentTodos - Optional: Current todo list for the latest round\n * @param subagents - Optional: Array of subagent states to include in rounds\n * @returns Array of structured interaction rounds\n */\nexport function convertMessagesToRounds(\n messages: Message[],\n currentTodos?: TodoItem[],\n subagents?: SubagentState[],\n): InteractionRound[] {\n const rounds: InteractionRound[] = []\n let currentRound: Partial<InteractionRound> | null = null\n let previousTodos: TodoItem[] = []\n\n for (let i = 0; i < messages.length; i++) {\n const message = messages[i]\n\n if (message.type === 'user') {\n // Save the previous round if it exists\n if (currentRound && currentRound.userMessage) {\n rounds.push(finalizeRound(currentRound))\n }\n\n // Start a new round\n currentRound = {\n uuid: message.uuid,\n userMessage: message,\n toolExecutions: [],\n subagentExecutions: [],\n startTime: Date.now(), // We'll update this from actual message timestamps if available\n }\n } else if (message.type === 'assistant' && currentRound) {\n // Process assistant message\n const assistantMsg = message as AssistantMessage\n\n // Check if this message contains tool_use blocks\n const hasToolUse = assistantMsg.message.content.some(\n (block: any) => block.type === 'tool_use',\n )\n\n // Check if this message has text content\n const hasText = assistantMsg.message.content.some(\n (block: any) => block.type === 'text' && block.text.trim().length > 0,\n )\n\n if (hasToolUse) {\n // This message contains tool executions\n // Extract tool executions from this message\n const toolExecutions = extractToolExecutions(assistantMsg)\n currentRound.toolExecutions = [\n ...(currentRound.toolExecutions || []),\n ...toolExecutions,\n ]\n\n // If this message also has text content, it might be thinking or commentary\n if (hasText && !currentRound.agentThinking) {\n const textContent = assistantMsg.message.content\n .filter((block: any) => block.type === 'text')\n .map((block: any) => block.text)\n .join('\\n')\n\n if (textContent.trim()) {\n currentRound.agentThinking = {\n message: textContent,\n startTime: Date.now(),\n durationMs: assistantMsg.durationMs,\n }\n }\n }\n } else if (hasText) {\n // This is a text-only assistant message\n const textContent = assistantMsg.message.content\n .filter((block: any) => block.type === 'text')\n .map((block: any) => block.text)\n .join('\\n')\n\n // Strategy: Look ahead to determine the role of this message\n // - If next message is user \u2192 this is final response (round complete)\n // - If next message is assistant with tools \u2192 this is thinking\n // - If this is the last message \u2192 this is final response (potentially in-progress)\n // - If we already have tools \u2192 this is final response after tools\n\n const nextMessage = messages[i + 1]\n const nextIsUser = nextMessage && nextMessage.type === 'user'\n const nextIsAssistantWithTools =\n nextMessage &&\n nextMessage.type === 'assistant' &&\n (nextMessage as AssistantMessage).message.content.some(\n (block: any) => block.type === 'tool_use',\n )\n const isLastMessage = i === messages.length - 1\n const hasToolsAlready =\n currentRound.toolExecutions && currentRound.toolExecutions.length > 0\n\n if (hasToolsAlready || nextIsUser || isLastMessage) {\n // This is the final response\n currentRound.agentResponse = assistantMsg\n currentRound.endTime = Date.now()\n currentRound.durationMs = assistantMsg.durationMs\n currentRound.totalCostUSD =\n (currentRound.totalCostUSD || 0) + assistantMsg.costUSD\n } else if (nextIsAssistantWithTools && !currentRound.agentThinking) {\n // This is thinking (agent reasoning before using tools)\n currentRound.agentThinking = {\n message: textContent,\n startTime: Date.now(),\n durationMs: assistantMsg.durationMs,\n }\n } else {\n // Default: treat as final response\n currentRound.agentResponse = assistantMsg\n currentRound.endTime = Date.now()\n currentRound.durationMs = assistantMsg.durationMs\n currentRound.totalCostUSD =\n (currentRound.totalCostUSD || 0) + assistantMsg.costUSD\n }\n }\n } else if (message.type === 'progress' && currentRound) {\n // Process progress message (tool results)\n // Subagent info is extracted from the subagents parameter instead\n }\n }\n\n // Finalize the last round\n if (currentRound && currentRound.userMessage) {\n // Add current todos to the last round if provided\n if (currentTodos) {\n const changes = calculateTodoChanges(previousTodos, currentTodos)\n currentRound.todoSnapshot = {\n todos: currentTodos,\n timestamp: Date.now(),\n changes,\n }\n }\n rounds.push(finalizeRound(currentRound))\n }\n\n // Assign subagents to rounds based on their start time\n // For now, assign all subagents to the last round if it exists\n // TODO: Improve this to assign subagents to the correct round based on timing\n if (rounds.length > 0 && subagents && subagents.length > 0) {\n const lastRound = rounds[rounds.length - 1]\n lastRound.subagentExecutions = subagents\n }\n\n return rounds\n}\n\n/**\n * Extracts tool execution records from an assistant message.\n *\n * Parses the message content blocks to find tool_use blocks and creates\n * ToolExecution objects with the appropriate status and metadata.\n *\n * @param message - Assistant message potentially containing tool uses\n * @returns Array of tool execution records\n */\nexport function extractToolExecutions(\n message: AssistantMessage,\n): ToolExecution[] {\n const toolExecutions: ToolExecution[] = []\n\n if (!message.message.content) {\n return toolExecutions\n }\n\n for (const block of message.message.content) {\n if (block.type === 'tool_use') {\n const toolExec: ToolExecution = {\n id: block.id,\n name: block.name,\n input: block.input,\n status: 'completed', // Assume completed if we're seeing it\n startTime: Date.now(), // Ideally extract from message metadata\n endTime: Date.now(),\n durationMs: message.durationMs,\n }\n toolExecutions.push(toolExec)\n }\n }\n\n return toolExecutions\n}\n\n/**\n * Extracts subagent execution records from TaskTool invocations.\n *\n * NOTE: Currently not used as we're getting SubagentState directly from REPL.tsx.\n * Keeping this for potential future use or reference.\n *\n * @param progressMessage - Progress message containing subagent event data\n * @returns Subagent execution record or null if not a subagent message\n */\n/*\nexport function extractSubagentExecution(\n progressMessage: ProgressMessage,\n): SubagentExecution | null {\n if (!progressMessage.subagentEvent) {\n return null\n }\n\n const event = progressMessage.subagentEvent\n const metrics = event.metrics || {\n toolUseCount: 0,\n tokenCount: 0,\n startTime: Date.now(),\n }\n\n // Build subagent execution from event data\n const subagentExec: SubagentExecution = {\n id: event.id,\n taskName: event.taskName || 'Unknown Task',\n agentType: event.agentType || 'unknown',\n status: mapEventTypeToStatus(event.eventType),\n messages: {\n toolExecutions: [],\n },\n metrics,\n }\n\n // Extract tool executions from the progress message content\n if (progressMessage.content && progressMessage.content.message) {\n const toolExecutions = extractToolExecutions(progressMessage.content)\n subagentExec.messages.toolExecutions = toolExecutions\n }\n\n // Add response message if this is a completion event\n if (event.eventType === 'complete' && progressMessage.content) {\n subagentExec.messages.response = progressMessage.content\n }\n\n // Add error if present\n if (event.eventType === 'error' && event.error) {\n subagentExec.error = event.error\n }\n\n return subagentExec\n}\n*/\n\n/**\n * Merges tool executions and subagent executions into a single timeline\n * sorted by start time.\n *\n * This enables chronological rendering where tools and subagents appear\n * in the order they were actually executed.\n *\n * @param toolExecutions - Array of tool execution records\n * @param subagentExecutions - Array of subagent state records\n * @returns Sorted array of timeline items\n */\nexport function mergeTimelineItems(\n toolExecutions: ToolExecution[],\n subagentExecutions: SubagentState[],\n): TimelineItem[] {\n const timeline: TimelineItem[] = []\n\n // Add tool executions to timeline\n for (const tool of toolExecutions) {\n timeline.push({\n type: 'tool',\n data: tool,\n time: tool.startTime,\n })\n }\n\n // Add subagent executions to timeline\n for (const subagent of subagentExecutions) {\n timeline.push({\n type: 'subagent',\n data: subagent,\n time: subagent.metrics.startTime,\n })\n }\n\n // Sort by start time\n timeline.sort((a, b) => a.time - b.time)\n\n return timeline\n}\n\n/**\n * Calculates the differences between two todo lists.\n *\n * Identifies added, removed, updated, and status-changed todos to generate\n * a complete change log for display purposes.\n *\n * @param oldTodos - Previous todo list state\n * @param newTodos - Current todo list state\n * @returns Array of todo change records\n */\nexport function calculateTodoChanges(\n oldTodos: TodoItem[],\n newTodos: TodoItem[],\n): TodoChange[] {\n const changes: TodoChange[] = []\n const timestamp = Date.now()\n\n // Create maps for efficient lookup\n const oldTodoMap = new Map(oldTodos.map(todo => [todo.id, todo]))\n const newTodoMap = new Map(newTodos.map(todo => [todo.id, todo]))\n\n // Check for additions and updates\n for (const newTodo of newTodos) {\n const oldTodo = oldTodoMap.get(newTodo.id)\n\n if (!oldTodo) {\n // New todo added\n changes.push({\n type: 'added',\n todoId: newTodo.id,\n newValue: newTodo,\n timestamp,\n })\n } else {\n // Check for status change\n if (oldTodo.status !== newTodo.status) {\n changes.push({\n type: 'status_changed',\n todoId: newTodo.id,\n oldValue: oldTodo,\n newValue: newTodo,\n timestamp,\n })\n } else if (JSON.stringify(oldTodo) !== JSON.stringify(newTodo)) {\n // Other fields changed\n changes.push({\n type: 'updated',\n todoId: newTodo.id,\n oldValue: oldTodo,\n newValue: newTodo,\n timestamp,\n })\n }\n }\n }\n\n // Check for removals\n for (const oldTodo of oldTodos) {\n if (!newTodoMap.has(oldTodo.id)) {\n changes.push({\n type: 'removed',\n todoId: oldTodo.id,\n oldValue: oldTodo,\n timestamp,\n })\n }\n }\n\n return changes\n}\n\n// ============================================================================\n// Helper Functions\n// ============================================================================\n\n/**\n * Finalizes a partial interaction round by computing aggregate metrics\n * and ensuring all required fields are present.\n *\n * @param partialRound - Partial round object to finalize\n * @returns Complete interaction round\n */\nfunction finalizeRound(\n partialRound: Partial<InteractionRound>,\n): InteractionRound {\n // Compute total cost from all messages\n let totalCost = 0\n // Note: agentThinking.message is now a string, cost is tracked separately\n // Cost is primarily tracked in agentResponse\n if (partialRound.agentResponse) {\n totalCost += partialRound.agentResponse.costUSD\n }\n\n // Compute duration if not already set\n const startTime = partialRound.startTime || Date.now()\n const endTime = partialRound.endTime || Date.now()\n const durationMs = partialRound.durationMs || endTime - startTime\n\n return {\n uuid: partialRound.uuid!,\n userMessage: partialRound.userMessage!,\n agentThinking: partialRound.agentThinking,\n toolExecutions: partialRound.toolExecutions || [],\n subagentExecutions: partialRound.subagentExecutions || [],\n agentResponse: partialRound.agentResponse,\n todoSnapshot: partialRound.todoSnapshot,\n startTime,\n endTime,\n durationMs,\n totalCostUSD: totalCost,\n }\n}\n\n/**\n * Maps subagent event types to execution status values.\n *\n * @param eventType - Subagent event type\n * @returns Corresponding execution status\n */\nfunction mapEventTypeToStatus(\n eventType: string,\n): 'initializing' | 'running' | 'completed' | 'error' {\n switch (eventType) {\n case 'start':\n return 'initializing'\n case 'progress':\n return 'running'\n case 'complete':\n return 'completed'\n case 'error':\n return 'error'\n default:\n return 'running'\n }\n}\n\n/**\n * Extracts thinking state from the first assistant message in a round\n * that doesn't contain tool uses.\n *\n * @param messages - Array of messages to search\n * @param roundStartIndex - Index where the current round starts\n * @returns Thinking state or undefined\n */\nexport function extractThinkingState(\n messages: Message[],\n roundStartIndex: number,\n): ThinkingState | undefined {\n for (let i = roundStartIndex; i < messages.length; i++) {\n const message = messages[i]\n\n if (message.type === 'assistant') {\n const assistantMsg = message as AssistantMessage\n const hasToolUse = assistantMsg.message.content.some(\n (block: any) => block.type === 'tool_use',\n )\n\n if (!hasToolUse) {\n // Extract text content from assistant message\n const textContent = assistantMsg.message.content\n .filter((block: any) => block.type === 'text')\n .map((block: any) => block.text)\n .join('\\n')\n\n return {\n message: textContent,\n startTime: Date.now(), // Ideally from message metadata\n durationMs: assistantMsg.durationMs,\n }\n }\n } else if (message.type === 'user') {\n // Stop searching when we hit the next user message\n break\n }\n }\n\n return undefined\n}\n\n/**\n * Gets the current in-progress tool or subagent from a round.\n * Useful for rendering live progress indicators.\n *\n * @param round - Interaction round to check\n * @returns Timeline item that's currently in progress, or null\n */\nexport function getCurrentActivity(\n round: InteractionRound,\n): TimelineItem | null {\n // Check tools\n for (const tool of round.toolExecutions) {\n if (tool.status === 'running' || tool.status === 'pending') {\n return { type: 'tool', data: tool, time: tool.startTime }\n }\n }\n\n // Check subagents\n for (const subagent of round.subagentExecutions) {\n if (subagent.status === 'running' || subagent.status === 'initializing') {\n return {\n type: 'subagent',\n data: subagent,\n time: subagent.metrics.startTime,\n }\n }\n }\n\n return null\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,5 +1,20 @@
1
- import { existsSync, readFileSync, writeFileSync, mkdirSync, statSync, unlinkSync, renameSync } from "node:fs";
2
- import { dirname, normalize, resolve, extname, relative, isAbsolute } from "node:path";
1
+ import {
2
+ existsSync,
3
+ readFileSync,
4
+ writeFileSync,
5
+ mkdirSync,
6
+ statSync,
7
+ unlinkSync,
8
+ renameSync
9
+ } from "node:fs";
10
+ import {
11
+ dirname,
12
+ normalize,
13
+ resolve,
14
+ extname,
15
+ relative,
16
+ isAbsolute
17
+ } from "node:path";
3
18
  import { homedir } from "node:os";
4
19
  class SecureFileService {
5
20
  static instance;
@@ -111,14 +126,16 @@ class SecureFileService {
111
126
  }
112
127
  }
113
128
  const absolutePath = resolve(normalizedPath);
114
- const isInAllowedPath = Array.from(this.allowedBasePaths).some((basePath) => {
115
- const base = resolve(basePath);
116
- const rel = relative(base, absolutePath);
117
- if (!rel || rel === "") return true;
118
- if (rel.startsWith("..")) return false;
119
- if (isAbsolute(rel)) return false;
120
- return true;
121
- });
129
+ const isInAllowedPath = Array.from(this.allowedBasePaths).some(
130
+ (basePath) => {
131
+ const base = resolve(basePath);
132
+ const rel = relative(base, absolutePath);
133
+ if (!rel || rel === "") return true;
134
+ if (rel.startsWith("..")) return false;
135
+ if (isAbsolute(rel)) return false;
136
+ return true;
137
+ }
138
+ );
122
139
  if (!isInAllowedPath) {
123
140
  return {
124
141
  isValid: false,
@@ -235,7 +252,10 @@ class SecureFileService {
235
252
  };
236
253
  }
237
254
  }
238
- const contentSize = typeof content === "string" ? Buffer.byteLength(content, options.encoding || "utf8") : content.length;
255
+ const contentSize = typeof content === "string" ? Buffer.byteLength(
256
+ content,
257
+ options.encoding || "utf8"
258
+ ) : content.length;
239
259
  const maxSize = options.maxSize || this.maxFileSize;
240
260
  if (contentSize > maxSize) {
241
261
  return {
@@ -327,7 +347,10 @@ class SecureFileService {
327
347
  if (existsSync(normalizedPath)) {
328
348
  const stats = statSync(normalizedPath);
329
349
  if (!stats.isDirectory()) {
330
- return { success: false, error: "Path already exists and is not a directory" };
350
+ return {
351
+ success: false,
352
+ error: "Path already exists and is not a directory"
353
+ };
331
354
  }
332
355
  return { success: true };
333
356
  }
@@ -467,10 +490,16 @@ class SecureFileService {
467
490
  return { isValid: false, error: "Filename is reserved" };
468
491
  }
469
492
  if (filename.startsWith(".") || filename.endsWith(".")) {
470
- return { isValid: false, error: "Filename cannot start or end with a dot" };
493
+ return {
494
+ isValid: false,
495
+ error: "Filename cannot start or end with a dot"
496
+ };
471
497
  }
472
498
  if (filename.startsWith(" ") || filename.endsWith(" ")) {
473
- return { isValid: false, error: "Filename cannot start or end with spaces" };
499
+ return {
500
+ isValid: false,
501
+ error: "Filename cannot start or end with spaces"
502
+ };
474
503
  }
475
504
  return { isValid: true };
476
505
  }