@indiccoder/mentis-cli 1.1.3 → 1.1.5

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 (82) hide show
  1. package/.claude/settings.local.json +8 -0
  2. package/.mentis/session.json +15 -0
  3. package/.mentis/sessions/1769189035730.json +23 -0
  4. package/.mentis/sessions/1769189569160.json +23 -0
  5. package/.mentis/sessions/1769767538672.json +23 -0
  6. package/.mentis/sessions/1769767785155.json +23 -0
  7. package/.mentis/sessions/1769768745802.json +23 -0
  8. package/.mentis/sessions/1769769600884.json +31 -0
  9. package/.mentis/sessions/1769770030160.json +31 -0
  10. package/.mentis/sessions/1769770606004.json +78 -0
  11. package/.mentis/sessions/1769771084515.json +141 -0
  12. package/.mentis/sessions/1769881926630.json +57 -0
  13. package/ARCHITECTURE.md +267 -0
  14. package/CONTRIBUTING.md +209 -0
  15. package/README.md +17 -0
  16. package/dist/checkpoint/CheckpointManager.js +92 -0
  17. package/dist/commands/Command.js +15 -1
  18. package/dist/commands/CommandManager.js +30 -5
  19. package/dist/commands/__tests__/CommandManager.test.js +70 -0
  20. package/dist/debug_google.js +61 -0
  21. package/dist/debug_lite.js +49 -0
  22. package/dist/debug_lite_headers.js +57 -0
  23. package/dist/debug_search.js +16 -0
  24. package/dist/index.js +33 -0
  25. package/dist/mcp/JsonRpcClient.js +16 -0
  26. package/dist/mcp/McpConfig.js +132 -0
  27. package/dist/mcp/McpManager.js +189 -0
  28. package/dist/repl/PersistentShell.js +20 -1
  29. package/dist/repl/ReplManager.js +410 -138
  30. package/dist/skills/Skill.js +17 -2
  31. package/dist/skills/SkillsManager.js +28 -3
  32. package/dist/skills/__tests__/SkillsManager.test.js +62 -0
  33. package/dist/tools/AskQuestionTool.js +172 -0
  34. package/dist/tools/EditFileTool.js +141 -0
  35. package/dist/tools/FileTools.js +7 -1
  36. package/dist/tools/PlanModeTool.js +53 -0
  37. package/dist/tools/WebSearchTool.js +190 -27
  38. package/dist/ui/DiffViewer.js +110 -0
  39. package/dist/ui/InputBox.js +16 -2
  40. package/dist/ui/MultiFileSelector.js +123 -0
  41. package/dist/ui/PlanModeUI.js +105 -0
  42. package/dist/ui/ToolExecutor.js +154 -0
  43. package/dist/ui/UIManager.js +12 -2
  44. package/dist/utils/__mocks__/chalk.js +20 -0
  45. package/dist/utils/__tests__/ContextVisualizer.test.js +95 -0
  46. package/docs/MCP_INTEGRATION.md +290 -0
  47. package/google_dump.html +18 -0
  48. package/lite_dump.html +176 -0
  49. package/lite_headers_dump.html +176 -0
  50. package/package.json +34 -2
  51. package/scripts/test_exa_mcp.ts +90 -0
  52. package/src/checkpoint/CheckpointManager.ts +102 -0
  53. package/src/commands/Command.ts +64 -13
  54. package/src/commands/CommandManager.ts +26 -5
  55. package/src/commands/__tests__/CommandManager.test.ts +83 -0
  56. package/src/debug_google.ts +30 -0
  57. package/src/debug_lite.ts +18 -0
  58. package/src/debug_lite_headers.ts +25 -0
  59. package/src/debug_search.ts +18 -0
  60. package/src/index.ts +45 -1
  61. package/src/mcp/JsonRpcClient.ts +19 -0
  62. package/src/mcp/McpConfig.ts +153 -0
  63. package/src/mcp/McpManager.ts +224 -0
  64. package/src/repl/PersistentShell.ts +24 -1
  65. package/src/repl/ReplManager.ts +1521 -1204
  66. package/src/skills/Skill.ts +91 -11
  67. package/src/skills/SkillsManager.ts +25 -3
  68. package/src/skills/__tests__/SkillsManager.test.ts +73 -0
  69. package/src/tools/AskQuestionTool.ts +197 -0
  70. package/src/tools/EditFileTool.ts +172 -0
  71. package/src/tools/FileTools.ts +3 -0
  72. package/src/tools/PlanModeTool.ts +50 -0
  73. package/src/tools/WebSearchTool.ts +235 -63
  74. package/src/ui/DiffViewer.ts +117 -0
  75. package/src/ui/InputBox.ts +17 -2
  76. package/src/ui/MultiFileSelector.ts +135 -0
  77. package/src/ui/PlanModeUI.ts +121 -0
  78. package/src/ui/ToolExecutor.ts +182 -0
  79. package/src/ui/UIManager.ts +15 -2
  80. package/src/utils/__mocks__/chalk.ts +19 -0
  81. package/src/utils/__tests__/ContextVisualizer.test.ts +118 -0
  82. package/console.log(tick) +0 -0
@@ -0,0 +1,57 @@
1
+ {
2
+ "timestamp": 1769881926630,
3
+ "name": "session-1769881926630",
4
+ "history": [
5
+ {
6
+ "role": "user",
7
+ "content": "Repository Structure:\n├── .claude/\n│ └── settings.local.json\n├── .mentis/\n│ ├── commands/\n│ │ └── ls.md\n│ ├── sessions/\n│ │ ├── 1769189035730.json\n│ │ ├── 1769189569160.json\n│ │ ├── 1769767538672.json\n│ │ ├── 1769767785155.json\n│ │ ├── 1769768745802.json\n│ │ ├── 1769769600884.json\n│ │ ├── 1769770030160.json\n│ │ ├── 1769770606004.json\n│ │ └── 1769771084515.json\n│ └── session.json\n├── docs/\n│ └── SKILLS.md\n├── examples/\n│ └── skills/\n│ ├── code-reviewer/\n│ │ └── SKILL.md\n│ ├── commit-helper/\n│ │ └── SKILL.md\n│ └── pdf-processing/\n│ └── SKILL.md\n├── scripts/\n│ ├── test_config_update.ts\n│ ├── test_features.ts\n│ ├── test_glm.ts\n│ └── test_models.ts\n├── src/\n│ ├── checkpoint/\n│ │ └── CheckpointManager.ts\n│ ├── commands/\n│ │ ├── __tests__/\n│ │ │ └── CommandManager.test.ts\n│ │ ├── Command.ts\n│ │ ├── CommandCreator.ts\n│ │ ├── CommandManager.ts\n│ │ └── SlashCommandTool.ts\n│ ├── config/\n│ │ └── ConfigManager.ts\n│ ├── context/\n│ │ ├── ContextManager.ts\n│ │ └── RepoMapper.ts\n│ ├── llm/\n│ │ ├── ModelInterface.ts\n│ │ └── OpenAIClient.ts\n│ ├── mcp/\n│ │ ├── JsonRpcClient.ts\n│ │ └── McpClient.ts\n│ ├── repl/\n│ │ ├── PersistentShell.ts\n│ │ └── ReplManager.ts\n│ ├── skills/\n│ │ ├── __tests__/\n│ │ │ └── SkillsManager.test.ts\n│ │ ├── LoadSkillTool.ts\n│ │ ├── Skill.ts\n│ │ ├── SkillCreator.ts\n│ │ └── SkillsManager.ts\n│ ├── tools/\n│ │ ├── FileTools.ts\n│ │ ├── GitTools.ts\n│ │ ├── PersistentShellTool.ts\n│ │ ├── SearchTools.ts\n│ │ ├── Tool.ts\n│ │ └── WebSearchTool.ts\n│ ├── ui/\n│ │ ├── InputBox.ts\n│ │ └── UIManager.ts\n│ ├── utils/\n│ │ ├── __mocks__/\n│ │ │ └── chalk.ts\n│ │ ├── __tests__/\n│ │ │ └── ContextVisualizer.test.ts\n│ │ ├── ContextVisualizer.ts\n│ │ ├── ConversationCompacter.ts\n│ │ ├── ProjectInitializer.ts\n│ │ └── UpdateManager.ts\n│ ├── debug_google.ts\n│ ├── debug_lite_headers.ts\n│ ├── debug_lite.ts\n│ ├── debug_search.ts\n│ └── index.ts\n├── .gitignore\n├── ARCHITECTURE.md\n├── CONTRIBUTING.md\n├── debug_fs.js\n├── google_dump.html\n├── lite_dump.html\n├── lite_headers_dump.html\n├── package-lock.json\n├── package.json\n├── README.md\n├── screenshot_1765779883482_9b30.png\n└── tsconfig.json\n\n\n\n\nUser Question: Available Custom Commands:\n/ls [directory]: List all files in current directory\n\nhi do you have mcp capability\n[SYSTEM: You are in BUILD mode. Focus on implementing working code that solves the user request efficiently.]"
8
+ },
9
+ {
10
+ "role": "assistant",
11
+ "content": "\nYes, I do have MCP (Model Context Protocol) capability! Looking at the repository structure, I can see there's a dedicated MCP implementation in the `src/mcp/` directory with:\n\n- `JsonRpcClient.ts` - Handles JSON-RPC communication\n- `McpClient.ts` - Main MCP client implementation\n\nThe MCP system allows for integrating external tools and services through a standardized protocol. Let me check what MCP functionality is currently available:\n",
12
+ "tool_calls": [
13
+ {
14
+ "function": {
15
+ "arguments": "{\"filePath\":\"src/mcp/McpClient.ts\"}",
16
+ "name": "read_file"
17
+ },
18
+ "id": "call_-7937540705308331019",
19
+ "index": 0,
20
+ "type": "function"
21
+ }
22
+ ]
23
+ },
24
+ {
25
+ "role": "tool",
26
+ "tool_call_id": "call_-7937540705308331019",
27
+ "name": "read_file",
28
+ "content": "import { JsonRpcClient } from './JsonRpcClient';\r\nimport { Tool } from '../tools/Tool';\r\n\r\nexport class McpClient {\r\n private rpc: JsonRpcClient;\r\n public serverName: string = 'unknown';\r\n\r\n constructor(command: string, args: string[]) {\r\n this.rpc = new JsonRpcClient(command, args);\r\n }\r\n\r\n async initialize() {\r\n const result = await this.rpc.sendRequest('initialize', {\r\n protocolVersion: '2024-11-05',\r\n capabilities: {},\r\n clientInfo: {\r\n name: 'mentis-cli',\r\n version: '1.0.0',\r\n },\r\n });\r\n\r\n this.serverName = result.serverInfo.name;\r\n\r\n // Notify initialized\r\n this.rpc.sendNotification('notifications/initialized');\r\n return result;\r\n }\r\n\r\n async listTools(): Promise<Tool[]> {\r\n const result = await this.rpc.sendRequest('tools/list');\r\n const mcpTools = result.tools || [];\r\n\r\n return mcpTools.map((t: any) => new McpToolAdapter(this, t));\r\n }\r\n\r\n async callTool(name: string, args: any): Promise<any> {\r\n return this.rpc.sendRequest('tools/call', {\r\n name,\r\n arguments: args,\r\n });\r\n }\r\n\r\n disconnect() {\r\n this.rpc.disconnect();\r\n }\r\n}\r\n\r\nclass McpToolAdapter implements Tool {\r\n private client: McpClient;\r\n public name: string;\r\n public description: string;\r\n public parameters: any;\r\n\r\n constructor(client: McpClient, toolDef: any) {\r\n this.client = client;\r\n this.name = toolDef.name;\r\n this.description = toolDef.description || '';\r\n this.parameters = toolDef.inputSchema || {};\r\n }\r\n\r\n async execute(args: any): Promise<string> {\r\n const result = await this.client.callTool(this.name, args);\r\n // MCP returns { content: [ { type: 'text', text: '...' } ], isError: boolean }\r\n if (result.isError) {\r\n throw new Error('MCP Tool Error');\r\n }\r\n\r\n // Extract text content\r\n if (result.content && Array.isArray(result.content)) {\r\n return result.content.map((c: any) => c.text).join('\\n');\r\n }\r\n\r\n return JSON.stringify(result);\r\n }\r\n}\r\n"
29
+ },
30
+ {
31
+ "role": "assistant",
32
+ "content": "\n",
33
+ "tool_calls": [
34
+ {
35
+ "function": {
36
+ "arguments": "{\"filePath\":\"src/mcp/JsonRpcClient.ts\"}",
37
+ "name": "read_file"
38
+ },
39
+ "id": "call_-7937555136399364450",
40
+ "index": 0,
41
+ "type": "function"
42
+ }
43
+ ]
44
+ },
45
+ {
46
+ "role": "tool",
47
+ "tool_call_id": "call_-7937555136399364450",
48
+ "name": "read_file",
49
+ "content": "import { spawn, ChildProcess } from 'child_process';\r\nimport * as readline from 'readline';\r\n\r\nexport interface JsonRpcRequest {\r\n jsonrpc: '2.0';\r\n method: string;\r\n params?: any;\r\n id?: number | string;\r\n}\r\n\r\nexport interface JsonRpcResponse {\r\n jsonrpc: '2.0';\r\n id: number | string | null;\r\n result?: any;\r\n error?: {\r\n code: number;\r\n message: string;\r\n data?: any;\r\n };\r\n}\r\n\r\nexport class JsonRpcClient {\r\n private process: ChildProcess;\r\n private sequence = 0;\r\n private pendingRequests = new Map<number | string, { resolve: Function; reject: Function }>();\r\n\r\n constructor(command: string, args: string[]) {\r\n this.process = spawn(command, args, {\r\n stdio: ['pipe', 'pipe', 'inherit'], // stdin, stdout, stderr\r\n });\r\n\r\n if (!this.process.stdout) {\r\n throw new Error('Failed to open stdout for MCP process');\r\n }\r\n\r\n const rl = readline.createInterface({ input: this.process.stdout });\r\n rl.on('line', (line) => this.handleMessage(line));\r\n\r\n this.process.on('error', (err) => console.error('MCP Process Error:', err));\r\n this.process.on('exit', (code) => console.log('MCP Process Exited:', code));\r\n }\r\n\r\n private handleMessage(line: string) {\r\n try {\r\n if (!line.trim()) return;\r\n const message = JSON.parse(line);\r\n\r\n // Handle Response\r\n if (message.id !== undefined && (message.result !== undefined || message.error !== undefined)) {\r\n const handler = this.pendingRequests.get(message.id);\r\n if (handler) {\r\n this.pendingRequests.delete(message.id);\r\n if (message.error) {\r\n handler.reject(new Error(message.error.message));\r\n } else {\r\n handler.resolve(message.result);\r\n }\r\n }\r\n } else {\r\n // Handle Notification or Request from server (not implemented deep yet)\r\n // console.log('Received notification from MCP:', message);\r\n }\r\n } catch (e) {\r\n console.error('Failed to parse MCP message:', line, e);\r\n }\r\n }\r\n\r\n public async sendRequest(method: string, params?: any): Promise<any> {\r\n const id = this.sequence++;\r\n const request: JsonRpcRequest = {\r\n jsonrpc: '2.0',\r\n method,\r\n params,\r\n id,\r\n };\r\n\r\n return new Promise((resolve, reject) => {\r\n this.pendingRequests.set(id, { resolve, reject });\r\n try {\r\n if (!this.process.stdin) throw new Error('Stdin not available');\r\n const data = JSON.stringify(request) + '\\n';\r\n this.process.stdin.write(data);\r\n } catch (e) {\r\n this.pendingRequests.delete(id);\r\n reject(e);\r\n }\r\n });\r\n }\r\n\r\n public sendNotification(method: string, params?: any) {\r\n if (!this.process.stdin) return;\r\n const notification: JsonRpcRequest = {\r\n jsonrpc: '2.0',\r\n method,\r\n params,\r\n };\r\n this.process.stdin.write(JSON.stringify(notification) + '\\n');\r\n }\r\n\r\n public disconnect() {\r\n this.process.kill();\r\n }\r\n}\r\n"
50
+ },
51
+ {
52
+ "role": "assistant",
53
+ "content": "\nYes, I have MCP (Model Context Protocol) capability! The system includes a full MCP implementation with:\n\n**MCP Components:**\n- `McpClient.ts` - Main client for communicating with MCP servers\n- `JsonRpcClient.ts` - JSON-RPC communication handler\n- Support for dynamic tool loading from MCP servers\n\n**Key Features:**\n1. **Server Initialization** - Connects to MCP servers using command-line processes\n2. **Tool Discovery** - Automatically lists available tools from connected servers\n3. **Tool Execution** - Calls MCP tools with arguments and returns results\n4. **Error Handling** - Proper error handling and process management\n\n**How it works:**\n- Spawns external MCP server processes\n- Uses JSON-RPC 2.0 protocol for communication\n- Adapts MCP tools to the internal Tool interface\n- Supports text content extraction from tool responses\n\nThe MCP system allows integrating external tools and services seamlessly into the Mentis CLI, extending functionality beyond the built-in tools.\n\nWould you like me to show you how to use it or help you set up an MCP server connection?"
54
+ }
55
+ ],
56
+ "files": []
57
+ }
@@ -0,0 +1,267 @@
1
+ # Mentis CLI Architecture
2
+
3
+ ## Overview
4
+
5
+ Mentis CLI is an agentic, multi-model CLI coding assistant built with TypeScript. It provides an interactive REPL for AI-assisted coding with support for multiple LLM providers (Gemini, Ollama, Anthropic, OpenAI-compatible).
6
+
7
+ ## Project Structure
8
+
9
+ ```
10
+ mentis-cli/
11
+ ├── src/
12
+ │ ├── index.ts # CLI entry point, argument parsing
13
+ │ ├── repl/ # REPL session management
14
+ │ │ ├── ReplManager.ts # Main REPL controller
15
+ │ │ └── HistoryManager.ts # Chat history persistence
16
+ │ ├── llm/ # LLM provider implementations
17
+ │ │ ├── ModelInterface.ts # Shared interfaces
18
+ │ │ ├── Gemini.ts # Google Gemini provider
19
+ │ │ ├── Ollama.ts # Ollama local provider
20
+ │ │ ├── Anthropic.ts # Anthropic Claude provider
21
+ │ │ └── OpenAI.ts # OpenAI-compatible provider
22
+ │ ├── tools/ # Tool implementations
23
+ │ │ ├── ToolManager.ts # Tool registry
24
+ │ │ ├── LoadSkillTool.ts # Agent Skills tool
25
+ │ │ └── ... # Individual tools
26
+ │ ├── commands/ # Custom Slash Commands
27
+ │ │ ├── Command.ts # Command interfaces
28
+ │ │ └── CommandManager.ts # Command discovery & parsing
29
+ │ ├── skills/ # Agent Skills system
30
+ │ │ ├── Skill.ts # Skill interfaces
31
+ │ │ ├── SkillsManager.ts # Skill discovery & validation
32
+ │ │ └── LoadSkillTool.ts # Tool for loading skills
33
+ │ ├── ui/ # User interface
34
+ │ │ ├── UIManager.ts # Display formatting
35
+ │ │ └── InputBox.ts # Input prompt with history
36
+ │ ├── mcp/ # Model Context Protocol
37
+ │ │ └── McpManager.ts # MCP server management
38
+ │ └── utils/ # Utilities
39
+ │ ├── ContextVisualizer.ts # Token usage display
40
+ │ ├── UpdateManager.ts # Auto-update
41
+ │ └── ...
42
+ ├── dist/ # Compiled JavaScript
43
+ ├── .mentis/ # Project-specific config
44
+ └── package.json
45
+ ```
46
+
47
+ ## Core Components
48
+
49
+ ### 1. REPL System (`src/repl/`)
50
+
51
+ The **ReplManager** is the heart of Mentis CLI. It orchestrates the entire conversation flow:
52
+
53
+ ```
54
+ User Input → Command Parser → Skill/Command Handler → LLM → Tool Execution → Response
55
+ ```
56
+
57
+ **Key responsibilities:**
58
+ - Manage chat history with auto-compact at 80% context
59
+ - Handle slash commands (`/help`, `/clear`, `/skills`, etc.)
60
+ - Inject skills and custom commands into system prompt
61
+ - Execute tool calls returned by LLM
62
+ - Save/restore session checkpoints
63
+
64
+ ### 2. LLM Abstraction (`src/llm/`)
65
+
66
+ Each LLM provider implements the `ModelInterface`:
67
+
68
+ ```typescript
69
+ interface ModelInterface {
70
+ chat(messages: ChatMessage[], tools?: ToolInfo[]): Promise<ChatResponse>;
71
+ streamChat(messages: ChatMessage[], tools?: ToolInfo[]): AsyncIterator<ChatChunk>;
72
+ }
73
+ ```
74
+
75
+ **Supported providers:**
76
+ - **Gemini** (Google) - Default, supports streaming
77
+ - **Ollama** - Local models via API
78
+ - **Anthropic** - Claude models
79
+ - **OpenAI-compatible** - Custom endpoints
80
+
81
+ ### 3. Tool System (`src/tools/`)
82
+
83
+ Tools are functions the AI can invoke. Each tool implements:
84
+
85
+ ```typescript
86
+ interface Tool {
87
+ name: string;
88
+ description: string;
89
+ inputSchema: Record<string, unknown>;
90
+ execute(params: Record<string, unknown>): Promise<ToolResult>;
91
+ }
92
+ ```
93
+
94
+ **Available tools:**
95
+ - File operations: `Read`, `Write`, `Edit`, `Glob`, `Grep`
96
+ - Execution: `Bash`
97
+ - Git: `GitStatus`, `GitDiff`, `GitCommit`
98
+ - Skills: `LoadSkillTool` (progressive disclosure)
99
+
100
+ ### 4. Agent Skills (`src/skills/`)
101
+
102
+ Agent Skills are reusable AI configurations stored as `SKILL.md` files:
103
+
104
+ ```
105
+ ~/.mentis/skills/
106
+ └── code-reviewer/
107
+ └── SKILL.md
108
+ ```
109
+
110
+ **Features:**
111
+ - Progressive disclosure (metadata loaded first, content on-demand)
112
+ - Optional tool restrictions
113
+ - Validation (name format, description quality)
114
+ - Personal vs project scopes
115
+
116
+ ### 5. Custom Commands (`src/commands/`)
117
+
118
+ Custom slash commands defined as markdown files:
119
+
120
+ ```
121
+ ~/.mentis/commands/
122
+ └── test.md
123
+ ```
124
+
125
+ **Features:**
126
+ - Parameter substitution (`$1`, `$2`, `$ARGUMENTS`)
127
+ - Bash execution (`!`cmd``)
128
+ - File references (`@file`)
129
+ - Namespace support (subdirectories)
130
+
131
+ ## Data Flow
132
+
133
+ ### Conversation Flow
134
+
135
+ ```
136
+ 1. User enters input
137
+ 2. Check for slash commands
138
+ 3. Inject skills/commands context
139
+ 4. Call LLM with tools
140
+ 5. Parse tool calls
141
+ 6. Execute tools
142
+ 7. Return results to LLM
143
+ 8. Stream response to user
144
+ 9. Update history
145
+ 10. Check context usage (compact if >80%)
146
+ ```
147
+
148
+ ### Tool Execution Flow
149
+
150
+ ```
151
+ LLM returns tool call
152
+
153
+ ToolManager finds tool
154
+
155
+ Validate parameters
156
+
157
+ Execute tool
158
+
159
+ Format result
160
+
161
+ Return to LLM for final response
162
+ ```
163
+
164
+ ### Skill Loading Flow
165
+
166
+ ```
167
+ Startup: Load all skill metadata (name, description)
168
+
169
+ User mentions skill keyword
170
+
171
+ LoadSkillTool invoked
172
+
173
+ Load full SKILL.md content
174
+
175
+ Inject into system prompt
176
+
177
+ AI executes with skill context
178
+ ```
179
+
180
+ ## Configuration
181
+
182
+ ### Environment Variables
183
+
184
+ ```bash
185
+ GEMINI_API_KEY=xxx # Gemini API key
186
+ OLLAMA_BASE_URL=http://... # Ollama endpoint
187
+ ANTHROPIC_API_KEY=xxx # Anthropic key
188
+ OPENAI_API_KEY=xxx # OpenAI-compatible key
189
+ OPENAI_BASE_URL=http://... # Custom endpoint
190
+ ```
191
+
192
+ ### Dotenv Config (`.mentis.md`)
193
+
194
+ ```yaml
195
+ model: gemini-2.5-pro
196
+ temperature: 0.7
197
+ max_tokens: 8192
198
+ ```
199
+
200
+ ### Directory Structure
201
+
202
+ ```
203
+ ~/.mentis/ # User config
204
+ ├── skills/ # Personal skills
205
+ ├── commands/ # Personal commands
206
+ └── checkpoints/ # Session saves
207
+
208
+ .mentis/ # Project config
209
+ ├── skills/ # Project skills
210
+ ├── commands/ # Project commands
211
+ └── .mentis.md # Project config file
212
+ ```
213
+
214
+ ## Testing
215
+
216
+ Jest tests are located in `src/**/__tests__/`:
217
+
218
+ ```bash
219
+ npm test # Run all tests
220
+ npm run test:watch # Watch mode
221
+ npm run test:coverage # Coverage report
222
+ ```
223
+
224
+ **Current test coverage:**
225
+ - CommandManager - Command parsing, argument substitution
226
+ - SkillsManager - Skill discovery, validation
227
+ - ContextVisualizer - Token calculation, compacting
228
+
229
+ ## Extension Points
230
+
231
+ ### Adding a New LLM Provider
232
+
233
+ 1. Implement `ModelInterface` in `src/llm/`
234
+ 2. Add to `ModelFactory.ts`
235
+ 3. Add CLI option for selection
236
+
237
+ ### Adding a New Tool
238
+
239
+ 1. Create tool class in `src/tools/`
240
+ 2. Register in `ToolManager.ts`
241
+ 3. Update documentation
242
+
243
+ ### Adding a New Slash Command
244
+
245
+ Create `.md` file in `~/.mentis/commands/`:
246
+
247
+ ```markdown
248
+ ---
249
+ description: My custom command
250
+ ---
251
+
252
+ Execute: !`echo "$1"`
253
+ ```
254
+
255
+ ## Performance Considerations
256
+
257
+ - **Streaming**: All LLM calls use streaming for faster perceived response
258
+ - **Progressive Disclosure**: Skills loaded on-demand to reduce startup time
259
+ - **Auto-Compact**: History automatically compacted at 80% context to prevent errors
260
+ - **Caching**: Discovered skills/commands cached in memory
261
+
262
+ ## Security
263
+
264
+ - API keys stored in environment variables or `.env`
265
+ - No credentials in code or config files
266
+ - Tool execution sandboxed (respects system permissions)
267
+ - Git operations require explicit confirmation (unless `--yolo`)
@@ -0,0 +1,209 @@
1
+ # Contributing to Mentis CLI
2
+
3
+ Thank you for your interest in contributing to Mentis CLI! This document provides guidelines for contributing.
4
+
5
+ ## Table of Contents
6
+
7
+ - [Development Setup](#development-setup)
8
+ - [Project Structure](#project-structure)
9
+ - [Coding Standards](#coding-standards)
10
+ - [Testing](#testing)
11
+ - [Submitting Changes](#submitting-changes)
12
+ - [Feature Ideas](#feature-ideas)
13
+
14
+ ## Development Setup
15
+
16
+ ### Prerequisites
17
+
18
+ - Node.js 18+
19
+ - npm or yarn
20
+ - Git
21
+
22
+ ### Fork and Clone
23
+
24
+ ```bash
25
+ # Fork the repository on GitHub
26
+ git clone https://github.com/YOUR_USERNAME/Mentis-CLI.git
27
+ cd Mentis-CLI
28
+ ```
29
+
30
+ ### Install Dependencies
31
+
32
+ ```bash
33
+ npm install
34
+ # or with legacy peer deps if needed
35
+ npm install --legacy-peer-deps
36
+ ```
37
+
38
+ ### Build
39
+
40
+ ```bash
41
+ npm run build
42
+ ```
43
+
44
+ ### Link for Local Testing
45
+
46
+ ```bash
47
+ npm link
48
+ # Now you can run 'mentis' anywhere
49
+ ```
50
+
51
+ ## Project Structure
52
+
53
+ Key directories:
54
+
55
+ - `src/` - TypeScript source code
56
+ - `src/llm/` - LLM provider implementations
57
+ - `src/tools/` - Tool implementations
58
+ - `src/skills/` - Agent Skills system
59
+ - `src/commands/` - Custom Commands system
60
+ - `src/ui/` - User interface components
61
+ - `src/**/__tests__/` - Jest test files
62
+
63
+ ## Coding Standards
64
+
65
+ ### TypeScript
66
+
67
+ - Use strict TypeScript settings
68
+ - Add JSDoc comments for exported functions/classes
69
+ - Define interfaces for all complex types
70
+
71
+ ### Code Style
72
+
73
+ - Use 4 spaces for indentation
74
+ - Prefer `const` over `let`
75
+ - Use arrow functions for callbacks
76
+ - Add error handling with specific error codes
77
+
78
+ ### Naming Conventions
79
+
80
+ - Classes: `PascalCase`
81
+ - Functions/variables: `camelCase`
82
+ - Interfaces: `PascalCase` with `I` prefix avoided
83
+ - Private members: `camelCase` with no prefix (TypeScript's `private` keyword)
84
+
85
+ ### Example
86
+
87
+ ```typescript
88
+ /**
89
+ * Calculate token usage from message history
90
+ */
91
+ export function calculateUsage(history: ChatMessage[]): ContextUsage {
92
+ let totalChars = 0;
93
+
94
+ for (const msg of history) {
95
+ if (msg.content) {
96
+ totalChars += msg.content.length;
97
+ }
98
+ }
99
+
100
+ return {
101
+ tokens: Math.ceil(totalChars / 4),
102
+ percentage: Math.round((totalChars / maxTokens) * 100),
103
+ maxTokens
104
+ };
105
+ }
106
+ ```
107
+
108
+ ## Testing
109
+
110
+ ### Running Tests
111
+
112
+ ```bash
113
+ npm test # Run all tests
114
+ npm run test:watch # Watch mode
115
+ npm run test:coverage # Coverage report
116
+ ```
117
+
118
+ ### Writing Tests
119
+
120
+ - Place test files in `src/**/__tests__/`
121
+ - Name test files: `*.test.ts`
122
+ - Use Jest conventions (`describe`, `it`, `expect`)
123
+
124
+ ### Example Test
125
+
126
+ ```typescript
127
+ describe('MyFunction', () => {
128
+ it('should return expected result', () => {
129
+ const result = myFunction('input');
130
+ expect(result).toBe('expected');
131
+ });
132
+ });
133
+ ```
134
+
135
+ ### Test Coverage
136
+
137
+ Aim for:
138
+ - Branches: 50%+
139
+ - Functions: 70%+
140
+ - Lines: 70%+
141
+
142
+ ## Submitting Changes
143
+
144
+ ### Commit Messages
145
+
146
+ Use conventional commit format:
147
+
148
+ ```
149
+ type(scope): description
150
+
151
+ feat(tools): add new file search tool
152
+ fix(repl): handle empty input gracefully
153
+ docs(readme): update installation instructions
154
+ test(skills): add validation tests
155
+ ```
156
+
157
+ ### Pull Request Process
158
+
159
+ 1. Create a branch from `main`
160
+ 2. Make your changes
161
+ 3. Add/update tests
162
+ 4. Run tests: `npm test`
163
+ 5. Build: `npm run build`
164
+ 6. Commit with conventional message
165
+ 7. Push to your fork
166
+ 8. Open a pull request
167
+
168
+ ### PR Checklist
169
+
170
+ - [ ] Tests pass
171
+ - [ ] New features include tests
172
+ - [ ] Documentation updated
173
+ - [ ] Commit messages follow conventions
174
+ - [ ] No console errors/warnings
175
+
176
+ ## Feature Ideas
177
+
178
+ Looking for something to work on? Here are some ideas:
179
+
180
+ ### High Priority
181
+
182
+ - [ ] Add more LLM providers (Mistral, Cohere, etc.)
183
+ - [ ] Implement MCP server protocol
184
+ - [ ] Add skill templates/generator
185
+ - [ ] Improve error recovery
186
+
187
+ ### Medium Priority
188
+
189
+ - [ ] Add conversation search
190
+ - [ ] Implement plugin system
191
+ - [ ] Add code generation templates
192
+ - [ ] Improve completions for custom commands
193
+
194
+ ### Low Priority
195
+
196
+ - [ ] Add themes for UI
197
+ - [ ] Implement voice input
198
+ - [ ] Add collaborative features
199
+ - [ ] Create web UI
200
+
201
+ ## Questions?
202
+
203
+ - Open an issue for bugs or feature requests
204
+ - Start a discussion for questions
205
+ - Check existing issues before creating new ones
206
+
207
+ ## License
208
+
209
+ By contributing, you agree that your contributions will be licensed under the ISC License.
package/README.md CHANGED
@@ -53,6 +53,23 @@ Type `/model` inside the CLI to launch the interactive configuration wizard:
53
53
 
54
54
  *Credentials are stored securely in `~/.mentisrc`.*
55
55
 
56
+ ### Web Search (Optional)
57
+
58
+ Mentis includes web search capabilities for documentation and error solving. To enable it:
59
+
60
+ 1. **Get a free Tavily API key** at https://tavily.com
61
+ 2. Add to your `.env` file in your project directory:
62
+ ```
63
+ TAVILY_API_KEY=your_key_here
64
+ ```
65
+
66
+ Alternatively, you can use **Exa** via MCP:
67
+ ```bash
68
+ export EXA_API_KEY=your_key_here
69
+ mentis
70
+ /mcp connect "Exa Search"
71
+ ```
72
+
56
73
  ## 📖 Usage
57
74
 
58
75
  ### Modes
@@ -49,5 +49,97 @@ class CheckpointManager {
49
49
  const filePath = path_1.default.join(this.checkpointDir, `${name}.json`);
50
50
  return fs_extra_1.default.existsSync(filePath);
51
51
  }
52
+ // ─── Per-Directory Local Session Methods ───────────────────────────────
53
+ getLocalSessionsDir(cwd) {
54
+ return path_1.default.join(cwd, '.mentis', 'sessions');
55
+ }
56
+ saveLocalSession(cwd, history, files) {
57
+ const timestamp = Date.now();
58
+ const checkpoint = {
59
+ timestamp,
60
+ name: `session-${timestamp}`,
61
+ history,
62
+ files
63
+ };
64
+ const sessionsDir = this.getLocalSessionsDir(cwd);
65
+ fs_extra_1.default.ensureDirSync(sessionsDir);
66
+ const filePath = path_1.default.join(sessionsDir, `${timestamp}.json`);
67
+ fs_extra_1.default.writeJsonSync(filePath, checkpoint, { spaces: 2 });
68
+ return filePath;
69
+ }
70
+ loadLocalSession(cwd, sessionId) {
71
+ const sessionsDir = this.getLocalSessionsDir(cwd);
72
+ if (!fs_extra_1.default.existsSync(sessionsDir))
73
+ return null;
74
+ let filePath;
75
+ if (sessionId) {
76
+ filePath = path_1.default.join(sessionsDir, `${sessionId}.json`);
77
+ }
78
+ else {
79
+ // Load most recent
80
+ const sessions = this.listLocalSessions(cwd);
81
+ if (sessions.length === 0)
82
+ return null;
83
+ filePath = path_1.default.join(sessionsDir, `${sessions[0].id}.json`);
84
+ }
85
+ if (fs_extra_1.default.existsSync(filePath)) {
86
+ return fs_extra_1.default.readJsonSync(filePath);
87
+ }
88
+ return null;
89
+ }
90
+ listLocalSessions(cwd) {
91
+ const sessionsDir = this.getLocalSessionsDir(cwd);
92
+ if (!fs_extra_1.default.existsSync(sessionsDir))
93
+ return [];
94
+ const files = fs_extra_1.default.readdirSync(sessionsDir);
95
+ return files
96
+ .filter(f => f.endsWith('.json'))
97
+ .map(f => {
98
+ const filePath = path_1.default.join(sessionsDir, f);
99
+ try {
100
+ const data = fs_extra_1.default.readJsonSync(filePath);
101
+ // Extract meaningful preview from LAST user message (to show current state)
102
+ const lastUserMsg = [...(data.history || [])].reverse().find(m => m.role === 'user');
103
+ let preview = 'No preview';
104
+ if (lastUserMsg?.content) {
105
+ let clean = lastUserMsg.content;
106
+ // 1. Remove Repository Structure (explicitly)
107
+ clean = clean.replace(/Repository Structure:[\s\S]*?(?=\n\n|$)/g, '');
108
+ // 2. Remove "User Question:" label (if context insertion logic used it)
109
+ clean = clean.replace(/User Question:\s*/g, '');
110
+ // 3. Remove "Available Custom Commands" block
111
+ clean = clean.replace(/Available Custom Commands:[\s\S]*?(?=\n\n|$)/g, '');
112
+ // 4. Remove "Available Skills" block (if present)
113
+ clean = clean.replace(/Available Skills \([\s\S]*?(?=\n\n|$)/g, '');
114
+ clean = clean.replace(/Available Skills:[\s\S]*?(?=\n\n|$)/g, '');
115
+ // 5. Remove [SYSTEM: ...] instructions at the end
116
+ clean = clean.replace(/\n\[SYSTEM:[\s\S]*?\]$/g, '');
117
+ // 6. Clean up code blocks references/markdown if they were just context
118
+ const text = clean.trim();
119
+ if (text) {
120
+ // Take the first 50 chars of what's left
121
+ preview = text.substring(0, 50).replace(/\n/g, ' ');
122
+ }
123
+ else {
124
+ preview = 'New Session';
125
+ }
126
+ }
127
+ return {
128
+ id: f.replace('.json', ''),
129
+ timestamp: data.timestamp,
130
+ messageCount: data.history?.length || 0,
131
+ preview: preview.length >= 50 ? preview + '...' : preview
132
+ };
133
+ }
134
+ catch {
135
+ return null;
136
+ }
137
+ })
138
+ .filter((s) => s !== null)
139
+ .sort((a, b) => b.timestamp - a.timestamp); // Most recent first
140
+ }
141
+ localSessionExists(cwd) {
142
+ return this.listLocalSessions(cwd).length > 0;
143
+ }
52
144
  }
53
145
  exports.CheckpointManager = CheckpointManager;
@@ -1,6 +1,20 @@
1
1
  "use strict";
2
2
  /**
3
3
  * Custom Slash Commands System
4
- * Users can define their own slash commands as markdown files
4
+ *
5
+ * Users can define their own slash commands as markdown files with YAML frontmatter.
6
+ * Commands support parameter substitution, bash execution, and file references.
7
+ *
8
+ * @packageDocumentation
9
+ *
10
+ * @example
11
+ * ```markdown
12
+ * ---
13
+ * description: Run tests and show coverage
14
+ * argument-hint: [test-pattern]
15
+ * ---
16
+ *
17
+ * Run tests with !`npm test $1`
18
+ * ```
5
19
  */
6
20
  Object.defineProperty(exports, "__esModule", { value: true });