@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.
- package/dist/repl/ReplManager.js +142 -3
- package/dist/skills/LoadSkillTool.js +133 -0
- package/dist/skills/Skill.js +6 -0
- package/dist/skills/SkillCreator.js +247 -0
- package/dist/skills/SkillsManager.js +337 -0
- package/docs/SKILLS.md +319 -0
- package/examples/skills/code-reviewer/SKILL.md +88 -0
- package/examples/skills/commit-helper/SKILL.md +66 -0
- package/examples/skills/pdf-processing/SKILL.md +108 -0
- package/package.json +3 -2
- package/src/repl/ReplManager.ts +174 -3
- package/src/skills/LoadSkillTool.ts +168 -0
- package/src/skills/Skill.ts +51 -0
- package/src/skills/SkillCreator.ts +237 -0
- package/src/skills/SkillsManager.ts +354 -0
|
@@ -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
|