@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.
- package/.claude/settings.local.json +8 -0
- package/.mentis/session.json +15 -0
- package/.mentis/sessions/1769189035730.json +23 -0
- package/.mentis/sessions/1769189569160.json +23 -0
- package/.mentis/sessions/1769767538672.json +23 -0
- package/.mentis/sessions/1769767785155.json +23 -0
- package/.mentis/sessions/1769768745802.json +23 -0
- package/.mentis/sessions/1769769600884.json +31 -0
- package/.mentis/sessions/1769770030160.json +31 -0
- package/.mentis/sessions/1769770606004.json +78 -0
- package/.mentis/sessions/1769771084515.json +141 -0
- package/.mentis/sessions/1769881926630.json +57 -0
- package/ARCHITECTURE.md +267 -0
- package/CONTRIBUTING.md +209 -0
- package/README.md +17 -0
- package/dist/checkpoint/CheckpointManager.js +92 -0
- package/dist/commands/Command.js +15 -1
- package/dist/commands/CommandManager.js +30 -5
- package/dist/commands/__tests__/CommandManager.test.js +70 -0
- package/dist/debug_google.js +61 -0
- package/dist/debug_lite.js +49 -0
- package/dist/debug_lite_headers.js +57 -0
- package/dist/debug_search.js +16 -0
- package/dist/index.js +33 -0
- package/dist/mcp/JsonRpcClient.js +16 -0
- package/dist/mcp/McpConfig.js +132 -0
- package/dist/mcp/McpManager.js +189 -0
- package/dist/repl/PersistentShell.js +20 -1
- package/dist/repl/ReplManager.js +410 -138
- package/dist/skills/Skill.js +17 -2
- package/dist/skills/SkillsManager.js +28 -3
- package/dist/skills/__tests__/SkillsManager.test.js +62 -0
- package/dist/tools/AskQuestionTool.js +172 -0
- package/dist/tools/EditFileTool.js +141 -0
- package/dist/tools/FileTools.js +7 -1
- package/dist/tools/PlanModeTool.js +53 -0
- package/dist/tools/WebSearchTool.js +190 -27
- package/dist/ui/DiffViewer.js +110 -0
- package/dist/ui/InputBox.js +16 -2
- package/dist/ui/MultiFileSelector.js +123 -0
- package/dist/ui/PlanModeUI.js +105 -0
- package/dist/ui/ToolExecutor.js +154 -0
- package/dist/ui/UIManager.js +12 -2
- package/dist/utils/__mocks__/chalk.js +20 -0
- package/dist/utils/__tests__/ContextVisualizer.test.js +95 -0
- package/docs/MCP_INTEGRATION.md +290 -0
- package/google_dump.html +18 -0
- package/lite_dump.html +176 -0
- package/lite_headers_dump.html +176 -0
- package/package.json +34 -2
- package/scripts/test_exa_mcp.ts +90 -0
- package/src/checkpoint/CheckpointManager.ts +102 -0
- package/src/commands/Command.ts +64 -13
- package/src/commands/CommandManager.ts +26 -5
- package/src/commands/__tests__/CommandManager.test.ts +83 -0
- package/src/debug_google.ts +30 -0
- package/src/debug_lite.ts +18 -0
- package/src/debug_lite_headers.ts +25 -0
- package/src/debug_search.ts +18 -0
- package/src/index.ts +45 -1
- package/src/mcp/JsonRpcClient.ts +19 -0
- package/src/mcp/McpConfig.ts +153 -0
- package/src/mcp/McpManager.ts +224 -0
- package/src/repl/PersistentShell.ts +24 -1
- package/src/repl/ReplManager.ts +1521 -1204
- package/src/skills/Skill.ts +91 -11
- package/src/skills/SkillsManager.ts +25 -3
- package/src/skills/__tests__/SkillsManager.test.ts +73 -0
- package/src/tools/AskQuestionTool.ts +197 -0
- package/src/tools/EditFileTool.ts +172 -0
- package/src/tools/FileTools.ts +3 -0
- package/src/tools/PlanModeTool.ts +50 -0
- package/src/tools/WebSearchTool.ts +235 -63
- package/src/ui/DiffViewer.ts +117 -0
- package/src/ui/InputBox.ts +17 -2
- package/src/ui/MultiFileSelector.ts +135 -0
- package/src/ui/PlanModeUI.ts +121 -0
- package/src/ui/ToolExecutor.ts +182 -0
- package/src/ui/UIManager.ts +15 -2
- package/src/utils/__mocks__/chalk.ts +19 -0
- package/src/utils/__tests__/ContextVisualizer.test.ts +118 -0
- 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
|
+
}
|
package/ARCHITECTURE.md
ADDED
|
@@ -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`)
|
package/CONTRIBUTING.md
ADDED
|
@@ -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;
|
package/dist/commands/Command.js
CHANGED
|
@@ -1,6 +1,20 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
3
|
* Custom Slash Commands System
|
|
4
|
-
*
|
|
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 });
|