@indiccoder/mentis-cli 1.0.8 → 1.0.9

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.
@@ -0,0 +1,337 @@
1
+ "use strict";
2
+ /**
3
+ * SkillsManager - Core module for Agent Skills system
4
+ * Handles discovery, loading, and validation of skills
5
+ */
6
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
7
+ if (k2 === undefined) k2 = k;
8
+ var desc = Object.getOwnPropertyDescriptor(m, k);
9
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
10
+ desc = { enumerable: true, get: function() { return m[k]; } };
11
+ }
12
+ Object.defineProperty(o, k2, desc);
13
+ }) : (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ o[k2] = m[k];
16
+ }));
17
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
18
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
19
+ }) : function(o, v) {
20
+ o["default"] = v;
21
+ });
22
+ var __importStar = (this && this.__importStar) || (function () {
23
+ var ownKeys = function(o) {
24
+ ownKeys = Object.getOwnPropertyNames || function (o) {
25
+ var ar = [];
26
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
27
+ return ar;
28
+ };
29
+ return ownKeys(o);
30
+ };
31
+ return function (mod) {
32
+ if (mod && mod.__esModule) return mod;
33
+ var result = {};
34
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
35
+ __setModuleDefault(result, mod);
36
+ return result;
37
+ };
38
+ })();
39
+ var __importDefault = (this && this.__importDefault) || function (mod) {
40
+ return (mod && mod.__esModule) ? mod : { "default": mod };
41
+ };
42
+ Object.defineProperty(exports, "__esModule", { value: true });
43
+ exports.SkillsManager = void 0;
44
+ const fs = __importStar(require("fs"));
45
+ const path = __importStar(require("path"));
46
+ const os = __importStar(require("os"));
47
+ const fast_glob_1 = require("fast-glob");
48
+ const yaml_1 = __importDefault(require("yaml"));
49
+ class SkillsManager {
50
+ constructor(cwd = process.cwd()) {
51
+ this.skills = new Map();
52
+ this.personalSkillsDir = path.join(os.homedir(), '.mentis', 'skills');
53
+ this.projectSkillsDir = path.join(cwd, '.mentis', 'skills');
54
+ }
55
+ /**
56
+ * Discover all skills from configured directories
57
+ */
58
+ async discoverSkills(options = {}) {
59
+ const { includePersonal = true, includeProject = true, includePlugin = false // Plugin skills not implemented yet
60
+ } = options;
61
+ const discovered = [];
62
+ if (includePersonal) {
63
+ discovered.push(...await this.discoverSkillsInDirectory(this.personalSkillsDir, 'personal'));
64
+ }
65
+ if (includeProject) {
66
+ discovered.push(...await this.discoverSkillsInDirectory(this.projectSkillsDir, 'project'));
67
+ }
68
+ // Store skills in map for quick lookup
69
+ for (const skill of discovered) {
70
+ this.skills.set(skill.name, skill);
71
+ }
72
+ return Array.from(this.skills.values());
73
+ }
74
+ /**
75
+ * Discover skills in a specific directory
76
+ */
77
+ async discoverSkillsInDirectory(dir, type) {
78
+ if (!fs.existsSync(dir)) {
79
+ return [];
80
+ }
81
+ const skills = [];
82
+ try {
83
+ // Find all SKILL.md files in subdirectories
84
+ const skillFiles = await (0, fast_glob_1.glob)('**/SKILL.md', {
85
+ cwd: dir,
86
+ absolute: true,
87
+ onlyFiles: true
88
+ });
89
+ for (const skillFile of skillFiles) {
90
+ const skillDir = path.dirname(skillFile);
91
+ const skillName = path.basename(skillDir);
92
+ const skill = await this.loadSkillMetadata(skillFile, skillDir, type);
93
+ if (skill) {
94
+ skills.push(skill);
95
+ }
96
+ }
97
+ }
98
+ catch (error) {
99
+ console.error(`Error discovering skills in ${dir}: ${error.message}`);
100
+ }
101
+ return skills;
102
+ }
103
+ /**
104
+ * Load only the metadata (YAML frontmatter) from a SKILL.md file
105
+ * This is used for progressive disclosure - only name/description loaded at startup
106
+ */
107
+ async loadSkillMetadata(skillPath, skillDir, type) {
108
+ try {
109
+ const content = fs.readFileSync(skillPath, 'utf-8');
110
+ const frontmatter = this.extractFrontmatter(content);
111
+ if (!frontmatter) {
112
+ return null;
113
+ }
114
+ // Convert SkillFrontmatter to SkillMetadata for validation
115
+ const metadata = {
116
+ name: frontmatter.name,
117
+ description: frontmatter.description,
118
+ allowedTools: frontmatter['allowed-tools']
119
+ };
120
+ const validation = this.validateSkillMetadata(metadata);
121
+ const skill = {
122
+ name: frontmatter.name,
123
+ description: frontmatter.description,
124
+ allowedTools: frontmatter['allowed-tools'],
125
+ path: skillPath,
126
+ directory: skillDir,
127
+ type,
128
+ isValid: validation.isValid,
129
+ errors: validation.errors.length > 0 ? validation.errors : undefined
130
+ };
131
+ return skill;
132
+ }
133
+ catch (error) {
134
+ console.error(`Error loading skill metadata from ${skillPath}: ${error.message}`);
135
+ return null;
136
+ }
137
+ }
138
+ /**
139
+ * Load the full content of a skill (for progressive disclosure)
140
+ */
141
+ async loadFullSkill(name) {
142
+ const skill = this.skills.get(name);
143
+ if (!skill) {
144
+ return null;
145
+ }
146
+ try {
147
+ const content = fs.readFileSync(skill.path, 'utf-8');
148
+ skill.content = content;
149
+ return skill;
150
+ }
151
+ catch (error) {
152
+ console.error(`Error loading full skill ${name}: ${error.message}`);
153
+ return null;
154
+ }
155
+ }
156
+ /**
157
+ * Extract YAML frontmatter from markdown content
158
+ */
159
+ extractFrontmatter(content) {
160
+ const frontmatterRegex = /^---\s*\n([\s\S]*?)\n---\s*\n/;
161
+ const match = content.match(frontmatterRegex);
162
+ if (!match) {
163
+ return null;
164
+ }
165
+ try {
166
+ const parsed = yaml_1.default.parse(match[1]);
167
+ return parsed;
168
+ }
169
+ catch (error) {
170
+ return null;
171
+ }
172
+ }
173
+ /**
174
+ * Validate skill metadata according to Claude Code spec
175
+ */
176
+ validateSkillMetadata(metadata) {
177
+ const errors = [];
178
+ const warnings = [];
179
+ // Check required fields
180
+ if (!metadata.name) {
181
+ errors.push('Missing required field: name');
182
+ }
183
+ else {
184
+ // Name validation: lowercase letters, numbers, hyphens only, max 64 chars
185
+ const nameRegex = /^[a-z0-9-]+$/;
186
+ if (!nameRegex.test(metadata.name)) {
187
+ errors.push('Name must contain only lowercase letters, numbers, and hyphens');
188
+ }
189
+ if (metadata.name.length > 64) {
190
+ errors.push('Name must be 64 characters or less');
191
+ }
192
+ }
193
+ if (!metadata.description) {
194
+ errors.push('Missing required field: description');
195
+ }
196
+ else {
197
+ if (metadata.description.length > 1024) {
198
+ errors.push('Description must be 1024 characters or less');
199
+ }
200
+ // Check if description mentions when to use
201
+ if (!metadata.description.toLowerCase().includes('use when') &&
202
+ !metadata.description.toLowerCase().includes('use for')) {
203
+ warnings.push('Description should include when to use this skill (e.g., "Use when...")');
204
+ }
205
+ }
206
+ // Validate allowed-tools if present
207
+ if (metadata.allowedTools) {
208
+ const validTools = [
209
+ 'Read', 'Write', 'Edit', 'Grep', 'Glob', 'Bash', 'WebSearch',
210
+ 'GitStatus', 'GitDiff', 'GitCommit', 'GitPush', 'GitPull',
211
+ 'ListDir', 'SearchFile', 'RunShell'
212
+ ];
213
+ const invalidTools = metadata.allowedTools.filter(t => !validTools.includes(t));
214
+ if (invalidTools.length > 0) {
215
+ warnings.push(`Unknown tools in allowed-tools: ${invalidTools.join(', ')}`);
216
+ }
217
+ }
218
+ return {
219
+ isValid: errors.length === 0,
220
+ errors,
221
+ warnings
222
+ };
223
+ }
224
+ /**
225
+ * Get skill by name
226
+ */
227
+ getSkill(name) {
228
+ return this.skills.get(name);
229
+ }
230
+ /**
231
+ * Get all skills
232
+ */
233
+ getAllSkills() {
234
+ return Array.from(this.skills.values());
235
+ }
236
+ /**
237
+ * Get skills formatted for model context injection
238
+ * This provides only metadata for progressive disclosure
239
+ */
240
+ getSkillsContext() {
241
+ const skills = this.getAllSkills().filter(s => s.isValid);
242
+ if (skills.length === 0) {
243
+ return '';
244
+ }
245
+ const context = skills.map(skill => {
246
+ return `- ${skill.name}: ${skill.description}`;
247
+ }).join('\n');
248
+ return `Available Skills:\n${context}`;
249
+ }
250
+ /**
251
+ * Get skills as SkillContext array
252
+ */
253
+ getSkillsContextArray() {
254
+ return this.getAllSkills()
255
+ .filter(s => s.isValid)
256
+ .map(s => ({
257
+ name: s.name,
258
+ description: s.description
259
+ }));
260
+ }
261
+ /**
262
+ * Validate all loaded skills
263
+ */
264
+ validateAllSkills() {
265
+ const results = new Map();
266
+ for (const [name, skill] of this.skills) {
267
+ const metadata = {
268
+ name: skill.name,
269
+ description: skill.description,
270
+ allowedTools: skill.allowedTools
271
+ };
272
+ results.set(name, this.validateSkillMetadata(metadata));
273
+ }
274
+ return results;
275
+ }
276
+ /**
277
+ * Read a supporting file from a skill directory
278
+ * Used for progressive disclosure of skill resources
279
+ */
280
+ readSkillFile(skillName, fileName) {
281
+ const skill = this.skills.get(skillName);
282
+ if (!skill) {
283
+ return null;
284
+ }
285
+ const filePath = path.join(skill.directory, fileName);
286
+ if (!fs.existsSync(filePath)) {
287
+ return null;
288
+ }
289
+ try {
290
+ return fs.readFileSync(filePath, 'utf-8');
291
+ }
292
+ catch (error) {
293
+ return null;
294
+ }
295
+ }
296
+ /**
297
+ * List supporting files in a skill directory
298
+ */
299
+ listSkillFiles(skillName) {
300
+ const skill = this.skills.get(skillName);
301
+ if (!skill) {
302
+ return [];
303
+ }
304
+ if (!fs.existsSync(skill.directory)) {
305
+ return [];
306
+ }
307
+ try {
308
+ const files = fs.readdirSync(skill.directory);
309
+ // Exclude SKILL.md as it's the main file
310
+ return files.filter(f => f !== 'SKILL.md');
311
+ }
312
+ catch (error) {
313
+ return [];
314
+ }
315
+ }
316
+ /**
317
+ * Create skills directories if they don't exist
318
+ */
319
+ ensureDirectoriesExist() {
320
+ if (!fs.existsSync(this.personalSkillsDir)) {
321
+ fs.mkdirSync(this.personalSkillsDir, { recursive: true });
322
+ }
323
+ }
324
+ /**
325
+ * Get personal skills directory path
326
+ */
327
+ getPersonalSkillsDir() {
328
+ return this.personalSkillsDir;
329
+ }
330
+ /**
331
+ * Get project skills directory path
332
+ */
333
+ getProjectSkillsDir() {
334
+ return this.projectSkillsDir;
335
+ }
336
+ }
337
+ exports.SkillsManager = SkillsManager;
package/docs/SKILLS.md ADDED
@@ -0,0 +1,319 @@
1
+ # Agent Skills - User Guide
2
+
3
+ ## Overview
4
+
5
+ Agent Skills allow you to package your expertise into modular, reusable capabilities that Mentis can autonomously discover and use. Skills are organized folders containing instructions, scripts, and resources that extend Mentis's functionality.
6
+
7
+ ## How Skills Work
8
+
9
+ 1. **Discovery**: Mentis scans `~/.mentis/skills/` (personal) and `.mentis/skills/` (project) for skills
10
+ 2. **Metadata Injection**: At startup, skill names and descriptions are added to the system context
11
+ 3. **Model-Invoked**: When Mentis determines a skill is relevant to your request, it automatically loads the full skill content
12
+ 4. **Progressive Disclosure**: Supporting files are only loaded when explicitly referenced
13
+
14
+ ## Skill Structure
15
+
16
+ ```
17
+ skill-directory/
18
+ ├── SKILL.md # Required: YAML frontmatter + instructions
19
+ ├── reference.md # Optional: Additional documentation
20
+ ├── examples.md # Optional: Usage examples
21
+ ├── scripts/ # Optional: Executable utilities
22
+ │ └── helper.py
23
+ └── templates/ # Optional: File templates
24
+ └── template.txt
25
+ ```
26
+
27
+ ## SKILL.md Format
28
+
29
+ ```yaml
30
+ ---
31
+ name: your-skill-name
32
+ description: What this skill does and when to use it
33
+ allowed-tools: Read, Grep, Glob # Optional: Restrict tools
34
+ ---
35
+
36
+ # Your Skill Name
37
+
38
+ ## Instructions
39
+ Step-by-step guidance for Mentis.
40
+
41
+ ## Examples
42
+ Concrete usage examples.
43
+ ```
44
+
45
+ ### Required Fields
46
+
47
+ - **name**: Lowercase letters, numbers, hyphens only (max 64 characters)
48
+ - **description**: What the skill does + when to use it (max 1024 characters)
49
+
50
+ ### Optional Fields
51
+
52
+ - **allowed-tools**: List of tools the skill can use without permission
53
+
54
+ ## Creating Your First Skill
55
+
56
+ ### Option 1: Interactive Wizard
57
+
58
+ ```
59
+ /skills create
60
+ ```
61
+
62
+ Follow the prompts:
63
+ 1. Enter skill name (e.g., `my-custom-skill`)
64
+ 2. Choose type (personal or project)
65
+ 3. Write description (include "Use when..." for better discovery)
66
+ 4. Optionally restrict allowed tools
67
+
68
+ ### Option 2: Manual Creation
69
+
70
+ ```bash
71
+ # Create directory
72
+ mkdir -p ~/.mentis/skills/my-skill
73
+
74
+ # Create SKILL.md
75
+ cat > ~/.mentis/skills/my-skill/SKILL.md << 'EOF'
76
+ ---
77
+ name: my-skill
78
+ description: Does something useful. Use when the user mentions specific keywords.
79
+ ---
80
+
81
+ # My Skill
82
+
83
+ ## Instructions
84
+ 1. Do this first
85
+ 2. Then do that
86
+
87
+ ## Examples
88
+ Example usage...
89
+ EOF
90
+ ```
91
+
92
+ ## Skill Commands
93
+
94
+ | Command | Description |
95
+ |---------|-------------|
96
+ | `/skills` | List all available skills |
97
+ | `/skills list` | List all available skills |
98
+ | `/skills show <name>` | Display full skill content |
99
+ | `/skills create [name]` | Interactive skill creator |
100
+ | `/skills validate` | Validate all skills for errors |
101
+
102
+ ## Skill Types
103
+
104
+ ### Personal Skills
105
+
106
+ Stored in `~/.mentis/skills/` - available across all projects.
107
+
108
+ **Use for**:
109
+ - Individual workflows and preferences
110
+ - Experimental skills in development
111
+ - Personal productivity tools
112
+
113
+ ### Project Skills
114
+
115
+ Stored in `.mentis/skills/` - shared with your team via git.
116
+
117
+ **Use for**:
118
+ - Team workflows and conventions
119
+ - Project-specific expertise
120
+ - Shared utilities and scripts
121
+
122
+ ## Best Practices
123
+
124
+ ### 1. Write Clear Descriptions
125
+
126
+ ❌ **Too vague**:
127
+ ```yaml
128
+ description: Helps with data
129
+ ```
130
+
131
+ ✅ **Specific**:
132
+ ```yaml
133
+ description: Analyze Excel spreadsheets, generate pivot tables, create charts. Use when working with Excel files, spreadsheets, or .xlsx files.
134
+ ```
135
+
136
+ **Tip**: Include both what the skill does AND when to use it.
137
+
138
+ ### 2. Use Progressive Disclosure
139
+
140
+ Keep `SKILL.md` lean. Move detailed content to supporting files:
141
+
142
+ **SKILL.md**:
143
+ ```markdown
144
+ ## Quick Start
145
+ Basic instructions here.
146
+
147
+ For advanced patterns, see [reference.md](reference.md).
148
+ ```
149
+
150
+ **reference.md**:
151
+ ```markdown
152
+ # Advanced Reference
153
+ Detailed documentation...
154
+ ```
155
+
156
+ ### 3. Validate Your Skills
157
+
158
+ ```
159
+ /skills validate
160
+ ```
161
+
162
+ Check for:
163
+ - Missing required fields
164
+ - Invalid name format
165
+ - Description length
166
+ - Unknown tools in allowed-tools
167
+
168
+ ### 4. Test Skills
169
+
170
+ After creating a skill:
171
+ 1. Restart Mentis to load it
172
+ 2. Ask a question that should trigger the skill
173
+ 3. Verify Mentis loads and uses the skill
174
+
175
+ ## Example Skills
176
+
177
+ ### commit-helper
178
+
179
+ ```yaml
180
+ ---
181
+ name: commit-helper
182
+ description: Generates clear, conventional commit messages from git diffs. Use when writing commit messages or reviewing staged changes.
183
+ allowed-tools: ["GitStatus", "GitDiff", "GitCommit"]
184
+ ---
185
+ ```
186
+
187
+ ### code-reviewer
188
+
189
+ ```yaml
190
+ ---
191
+ name: code-reviewer
192
+ description: Reviews code for best practices, potential bugs, security issues. Use when reviewing code or checking PRs.
193
+ allowed-tools: ["Read", "Grep", "Glob"]
194
+ ---
195
+ ```
196
+
197
+ See `examples/skills/` for complete examples.
198
+
199
+ ## Troubleshooting
200
+
201
+ ### Skill Not Loading
202
+
203
+ **Check 1**: File location
204
+ ```bash
205
+ # Personal skills
206
+ ls ~/.mentis/skills/my-skill/SKILL.md
207
+
208
+ # Project skills
209
+ ls .mentis/skills/my-skill/SKILL.md
210
+ ```
211
+
212
+ **Check 2**: YAML syntax
213
+ ```bash
214
+ # Verify frontmatter format
215
+ cat ~/.mentis/skills/my-skill/SKILL.md | head -n 10
216
+ ```
217
+
218
+ Ensure:
219
+ - `---` on line 1
220
+ - `---` closes frontmatter
221
+ - Valid YAML (no tabs, correct indentation)
222
+
223
+ **Check 3**: Validation
224
+ ```bash
225
+ /skills validate
226
+ ```
227
+
228
+ ### Mentis Not Using Skill
229
+
230
+ **Problem**: Skill exists but Mentis doesn't invoke it.
231
+
232
+ **Solution**: Improve description to include trigger keywords.
233
+
234
+ ❌ **Vague**:
235
+ ```yaml
236
+ description: Code improvement
237
+ ```
238
+
239
+ ✅ **Clear with triggers**:
240
+ ```yaml
241
+ description: Refactor code for better performance and readability. Use when the user mentions optimization, refactoring, performance, or code cleanup.
242
+ ```
243
+
244
+ ### allowed-tools Not Working
245
+
246
+ **Check**: Tool names must match Mentis tool names exactly.
247
+
248
+ Valid tools: `Read`, `Write`, `Edit`, `Grep`, `Glob`, `ListDir`, `SearchFile`, `RunShell`, `WebSearch`, `GitStatus`, `GitDiff`, `GitCommit`, `GitPush`, `GitPull`
249
+
250
+ ## Sharing Skills
251
+
252
+ ### Via Git (Project Skills)
253
+
254
+ ```bash
255
+ # Add to repository
256
+ git add .mentis/skills/
257
+ git commit -m "Add team skill for X"
258
+ git push
259
+
260
+ # Team members get it automatically
261
+ git pull
262
+ # Skill is now available
263
+ ```
264
+
265
+ ### Via Manual Copy
266
+
267
+ ```bash
268
+ # Export
269
+ tar czf my-skill.tar.gz -C ~/.mentis/skills my-skill
270
+
271
+ # Import on another machine
272
+ tar xzf my-skill.tar.gz -C ~/.mentis/skills
273
+ ```
274
+
275
+ ## Advanced Topics
276
+
277
+ ### Code Execution in Skills
278
+
279
+ Skills can include executable scripts:
280
+
281
+ ```markdown
282
+ ## Running the Script
283
+
284
+ ```bash
285
+ python scripts/analyze.py data.csv
286
+ ```
287
+ ```
288
+
289
+ Mentis will execute the script and return results.
290
+
291
+ ### Tool Permissions
292
+
293
+ Use `allowed-tools` to:
294
+ - Restrict read-only skills from making changes
295
+ - Limit scope of specialized skills
296
+ - Add security for sensitive operations
297
+
298
+ ```yaml
299
+ ---
300
+ allowed-tools: ["Read", "Grep", "Glob"]
301
+ ---
302
+ ```
303
+
304
+ When this skill is active, only these tools can be used without explicit permission.
305
+
306
+ ### Dynamic Tool Loading
307
+
308
+ The model can invoke tools to load skills:
309
+
310
+ ```
311
+ load_skill({ name: "pdf-processing" })
312
+ ```
313
+
314
+ This loads the full SKILL.md content into context.
315
+
316
+ ## Resources
317
+
318
+ - [Claude Code Agent Skills](https://www.anthropic.com/engineering/equipping-agents-for-the-real-world-with-agent-skills) - Official announcement
319
+ - [examples/skills/](../examples/skills/) - Example skills directory