@indiccoder/mentis-cli 1.1.2 → 1.1.4
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/ARCHITECTURE.md +267 -0
- package/CONTRIBUTING.md +209 -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/index.js +23 -0
- package/dist/repl/ReplManager.js +19 -31
- 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/ui/InputBox.js +127 -0
- package/dist/ui/UIManager.js +2 -2
- package/dist/utils/__mocks__/chalk.js +20 -0
- package/dist/utils/__tests__/ContextVisualizer.test.js +95 -0
- package/package.json +25 -2
- 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/index.ts +33 -1
- package/src/repl/ReplManager.ts +19 -33
- 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/ui/InputBox.ts +145 -0
- package/src/ui/UIManager.ts +2 -2
- package/src/utils/__mocks__/chalk.ts +19 -0
- package/src/utils/__tests__/ContextVisualizer.test.ts +118 -0
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/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 });
|
|
@@ -57,9 +57,19 @@ class CommandManager {
|
|
|
57
57
|
async discoverCommands() {
|
|
58
58
|
const discovered = [];
|
|
59
59
|
// Personal commands
|
|
60
|
-
|
|
60
|
+
try {
|
|
61
|
+
discovered.push(...await this.discoverCommandsInDirectory(this.personalCommandsDir, 'personal'));
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
console.warn(`Warning: Failed to load personal commands from ${this.personalCommandsDir}: ${error.message}`);
|
|
65
|
+
}
|
|
61
66
|
// Project commands
|
|
62
|
-
|
|
67
|
+
try {
|
|
68
|
+
discovered.push(...await this.discoverCommandsInDirectory(this.projectCommandsDir, 'project'));
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
console.warn(`Warning: Failed to load project commands from ${this.projectCommandsDir}: ${error.message}`);
|
|
72
|
+
}
|
|
63
73
|
// Store commands in map (project commands override personal)
|
|
64
74
|
for (const command of discovered) {
|
|
65
75
|
this.commands.set(command.name, command);
|
|
@@ -102,6 +112,7 @@ class CommandManager {
|
|
|
102
112
|
const frontmatter = this.extractFrontmatter(content);
|
|
103
113
|
const commandName = this.getCommandName(commandPath, type);
|
|
104
114
|
if (!commandName) {
|
|
115
|
+
console.warn(`Warning: Invalid command name in ${commandPath} (skipping)`);
|
|
105
116
|
return null;
|
|
106
117
|
}
|
|
107
118
|
// Get namespace (subdirectory)
|
|
@@ -121,7 +132,15 @@ class CommandManager {
|
|
|
121
132
|
return command;
|
|
122
133
|
}
|
|
123
134
|
catch (error) {
|
|
124
|
-
|
|
135
|
+
if (error.code === 'ENOENT') {
|
|
136
|
+
console.warn(`Warning: Command file not found: ${commandPath}`);
|
|
137
|
+
}
|
|
138
|
+
else if (error.code === 'EACCES') {
|
|
139
|
+
console.warn(`Warning: Permission denied reading command: ${commandPath}`);
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
console.error(`Error parsing command ${commandPath}: ${error.message}`);
|
|
143
|
+
}
|
|
125
144
|
return null;
|
|
126
145
|
}
|
|
127
146
|
}
|
|
@@ -217,7 +236,10 @@ class CommandManager {
|
|
|
217
236
|
const bashRegex = /!`([^`]+)`/g;
|
|
218
237
|
let bashMatch;
|
|
219
238
|
while ((bashMatch = bashRegex.exec(content)) !== null) {
|
|
220
|
-
|
|
239
|
+
const bashCommand = bashMatch[1].trim();
|
|
240
|
+
if (bashCommand) {
|
|
241
|
+
bashCommands.push(bashCommand);
|
|
242
|
+
}
|
|
221
243
|
}
|
|
222
244
|
// Remove bash command markers
|
|
223
245
|
content = content.replace(/!`[^`]+`/g, '[BASH_OUTPUT]');
|
|
@@ -225,7 +247,10 @@ class CommandManager {
|
|
|
225
247
|
const fileRegex = /@([^\s]+)/g;
|
|
226
248
|
let fileMatch;
|
|
227
249
|
while ((fileMatch = fileRegex.exec(content)) !== null) {
|
|
228
|
-
|
|
250
|
+
const fileRef = fileMatch[1].trim();
|
|
251
|
+
if (fileRef && !fileReferences.includes(fileRef)) {
|
|
252
|
+
fileReferences.push(fileRef);
|
|
253
|
+
}
|
|
229
254
|
}
|
|
230
255
|
return { content, bashCommands, fileReferences };
|
|
231
256
|
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Tests for CommandManager
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const CommandManager_1 = require("../CommandManager");
|
|
7
|
+
describe('CommandManager', () => {
|
|
8
|
+
let manager;
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
manager = new CommandManager_1.CommandManager();
|
|
11
|
+
});
|
|
12
|
+
describe('getAllCommands', () => {
|
|
13
|
+
it('should return empty array initially', () => {
|
|
14
|
+
const commands = manager.getAllCommands();
|
|
15
|
+
expect(commands).toEqual([]);
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
describe('getCommandsContext', () => {
|
|
19
|
+
it('should return empty string when no commands', () => {
|
|
20
|
+
const context = manager.getCommandsContext();
|
|
21
|
+
expect(context).toBe('');
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
describe('parseCommand', () => {
|
|
25
|
+
it('should replace $ARGUMENTS placeholder', async () => {
|
|
26
|
+
const command = {
|
|
27
|
+
name: 'echo',
|
|
28
|
+
type: 'personal',
|
|
29
|
+
path: '/echo.md',
|
|
30
|
+
directory: '/commands',
|
|
31
|
+
description: 'Echo arguments',
|
|
32
|
+
frontmatter: {},
|
|
33
|
+
content: 'You said: $ARGUMENTS',
|
|
34
|
+
hasParameters: true
|
|
35
|
+
};
|
|
36
|
+
const parsed = await manager.parseCommand(command, ['hello', 'world']);
|
|
37
|
+
expect(parsed.content).toContain('hello world');
|
|
38
|
+
});
|
|
39
|
+
it('should replace $1, $2 placeholders', async () => {
|
|
40
|
+
const command = {
|
|
41
|
+
name: 'greet',
|
|
42
|
+
type: 'personal',
|
|
43
|
+
path: '/greet.md',
|
|
44
|
+
directory: '/commands',
|
|
45
|
+
description: 'Greet user',
|
|
46
|
+
frontmatter: {},
|
|
47
|
+
content: 'Hello $1, welcome to $2',
|
|
48
|
+
hasParameters: true
|
|
49
|
+
};
|
|
50
|
+
const parsed = await manager.parseCommand(command, ['Alice', 'Wonderland']);
|
|
51
|
+
expect(parsed.content).toContain('Hello Alice');
|
|
52
|
+
expect(parsed.content).toContain('welcome to Wonderland');
|
|
53
|
+
});
|
|
54
|
+
it('should extract bash commands from content', async () => {
|
|
55
|
+
const command = {
|
|
56
|
+
name: 'run-test',
|
|
57
|
+
type: 'personal',
|
|
58
|
+
path: '/run-test.md',
|
|
59
|
+
directory: '/commands',
|
|
60
|
+
description: 'Run tests',
|
|
61
|
+
frontmatter: {},
|
|
62
|
+
content: 'Run tests with !`npm test`',
|
|
63
|
+
hasParameters: false
|
|
64
|
+
};
|
|
65
|
+
const parsed = await manager.parseCommand(command, []);
|
|
66
|
+
expect(parsed.bashCommands).toHaveLength(1);
|
|
67
|
+
expect(parsed.bashCommands[0]).toBe('npm test');
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
});
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,24 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
"use strict";
|
|
3
|
+
/**
|
|
4
|
+
* Mentis CLI - An Agentic, Multi-Model CLI Coding Assistant
|
|
5
|
+
*
|
|
6
|
+
* @packageDocumentation
|
|
7
|
+
*/
|
|
3
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
9
|
const ReplManager_1 = require("./repl/ReplManager");
|
|
10
|
+
/**
|
|
11
|
+
* Parse command line arguments
|
|
12
|
+
*
|
|
13
|
+
* @returns Parsed command and options
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```bash
|
|
17
|
+
* mentis --resume
|
|
18
|
+
* mentis -p "fix the bug"
|
|
19
|
+
* mentis --yolo
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
5
22
|
function parseArgs() {
|
|
6
23
|
const args = process.argv.slice(2);
|
|
7
24
|
const options = {
|
|
@@ -59,6 +76,11 @@ Commands (in REPL):
|
|
|
59
76
|
}
|
|
60
77
|
return { command, options };
|
|
61
78
|
}
|
|
79
|
+
/**
|
|
80
|
+
* Main entry point for Mentis CLI
|
|
81
|
+
*
|
|
82
|
+
* Parses arguments and starts the REPL or update manager
|
|
83
|
+
*/
|
|
62
84
|
async function main() {
|
|
63
85
|
const { command, options } = parseArgs();
|
|
64
86
|
// Handle update command
|
|
@@ -72,6 +94,7 @@ async function main() {
|
|
|
72
94
|
const repl = new ReplManager_1.ReplManager(options);
|
|
73
95
|
await repl.start();
|
|
74
96
|
}
|
|
97
|
+
// Start the application
|
|
75
98
|
main().catch((error) => {
|
|
76
99
|
console.error('Fatal error:', error);
|
|
77
100
|
process.exit(1);
|