@orderful/droid 0.14.0 → 0.15.0
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/CLAUDE.md +12 -8
- package/CHANGELOG.md +22 -0
- package/dist/commands/tui.d.ts.map +1 -1
- package/dist/commands/tui.js +1 -2
- package/dist/commands/tui.js.map +1 -1
- package/dist/lib/agents.d.ts +6 -6
- package/dist/lib/agents.d.ts.map +1 -1
- package/dist/lib/agents.js +60 -38
- package/dist/lib/agents.js.map +1 -1
- package/dist/lib/skills.d.ts +1 -0
- package/dist/lib/skills.d.ts.map +1 -1
- package/dist/lib/skills.js +41 -8
- package/dist/lib/skills.js.map +1 -1
- package/dist/lib/types.d.ts +4 -0
- package/dist/lib/types.d.ts.map +1 -1
- package/dist/tools/README.md +79 -50
- package/dist/tools/brain/TOOL.yaml +1 -1
- package/dist/tools/brain/skills/brain/SKILL.md +1 -0
- package/dist/tools/brain/skills/brain-obsidian/SKILL.md +1 -0
- package/dist/tools/coach/TOOL.yaml +1 -1
- package/dist/tools/coach/skills/coach/SKILL.md +1 -0
- package/{src/tools/code-review/agents/edi-standards-reviewer/AGENT.md → dist/tools/code-review/agents/edi-standards-reviewer.md} +10 -0
- package/dist/tools/code-review/agents/{error-handling-reviewer/AGENT.md → error-handling-reviewer.md} +10 -0
- package/{src/tools/code-review/agents/test-coverage-analyzer/AGENT.md → dist/tools/code-review/agents/test-coverage-analyzer.md} +11 -0
- package/dist/tools/code-review/agents/{type-reviewer/AGENT.md → type-reviewer.md} +10 -0
- package/dist/tools/code-review/skills/code-review/SKILL.md +1 -0
- package/dist/tools/comments/TOOL.yaml +2 -2
- package/dist/tools/comments/skills/comments/SKILL.md +1 -0
- package/dist/tools/droid/skills/droid/SKILL.md +1 -0
- package/dist/tools/project/skills/project/SKILL.md +1 -0
- package/package.json +1 -1
- package/src/commands/tui.tsx +1 -2
- package/src/lib/agents.ts +65 -42
- package/src/lib/skills.test.ts +26 -31
- package/src/lib/skills.ts +45 -8
- package/src/lib/types.ts +5 -0
- package/src/tools/README.md +79 -50
- package/src/tools/brain/TOOL.yaml +1 -1
- package/src/tools/brain/skills/brain/SKILL.md +1 -0
- package/src/tools/brain/skills/brain-obsidian/SKILL.md +1 -0
- package/src/tools/coach/TOOL.yaml +1 -1
- package/src/tools/coach/skills/coach/SKILL.md +1 -0
- package/{dist/tools/code-review/agents/edi-standards-reviewer/AGENT.md → src/tools/code-review/agents/edi-standards-reviewer.md} +10 -0
- package/src/tools/code-review/agents/{error-handling-reviewer/AGENT.md → error-handling-reviewer.md} +10 -0
- package/{dist/tools/code-review/agents/test-coverage-analyzer/AGENT.md → src/tools/code-review/agents/test-coverage-analyzer.md} +11 -0
- package/src/tools/code-review/agents/{type-reviewer/AGENT.md → type-reviewer.md} +10 -0
- package/src/tools/code-review/skills/code-review/SKILL.md +1 -0
- package/src/tools/comments/TOOL.yaml +2 -2
- package/src/tools/comments/skills/comments/SKILL.md +1 -0
- package/src/tools/droid/skills/droid/SKILL.md +1 -0
- package/src/tools/project/skills/project/SKILL.md +1 -0
- package/dist/tools/brain/skills/brain/SKILL.yaml +0 -29
- package/dist/tools/brain/skills/brain-obsidian/SKILL.yaml +0 -42
- package/dist/tools/coach/skills/coach/SKILL.yaml +0 -25
- package/dist/tools/code-review/agents/edi-standards-reviewer/AGENT.yaml +0 -14
- package/dist/tools/code-review/agents/error-handling-reviewer/AGENT.yaml +0 -14
- package/dist/tools/code-review/agents/test-coverage-analyzer/AGENT.yaml +0 -14
- package/dist/tools/code-review/agents/type-reviewer/AGENT.yaml +0 -13
- package/dist/tools/code-review/skills/code-review/SKILL.yaml +0 -19
- package/dist/tools/comments/skills/comments/SKILL.yaml +0 -50
- package/dist/tools/droid/skills/droid/SKILL.yaml +0 -7
- package/dist/tools/project/skills/project/SKILL.yaml +0 -30
- package/src/tools/brain/skills/brain/SKILL.yaml +0 -29
- package/src/tools/brain/skills/brain-obsidian/SKILL.yaml +0 -42
- package/src/tools/coach/skills/coach/SKILL.yaml +0 -25
- package/src/tools/code-review/agents/edi-standards-reviewer/AGENT.yaml +0 -14
- package/src/tools/code-review/agents/error-handling-reviewer/AGENT.yaml +0 -14
- package/src/tools/code-review/agents/test-coverage-analyzer/AGENT.yaml +0 -14
- package/src/tools/code-review/agents/type-reviewer/AGENT.yaml +0 -13
- package/src/tools/code-review/skills/code-review/SKILL.yaml +0 -19
- package/src/tools/comments/skills/comments/SKILL.yaml +0 -50
- package/src/tools/droid/skills/droid/SKILL.yaml +0 -7
- package/src/tools/project/skills/project/SKILL.yaml +0 -30
package/src/lib/agents.ts
CHANGED
|
@@ -5,6 +5,7 @@ import YAML from 'yaml';
|
|
|
5
5
|
import { loadConfig } from './config.js';
|
|
6
6
|
import { Platform } from './types.js';
|
|
7
7
|
import { getAgentsPath } from './platforms.js';
|
|
8
|
+
import { loadToolManifest } from './tools.js';
|
|
8
9
|
|
|
9
10
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
10
11
|
const BUNDLED_TOOLS_DIR = join(__dirname, '../tools');
|
|
@@ -33,23 +34,58 @@ export interface AgentManifest {
|
|
|
33
34
|
}
|
|
34
35
|
|
|
35
36
|
/**
|
|
36
|
-
*
|
|
37
|
+
* Parse YAML frontmatter from agent file content
|
|
37
38
|
*/
|
|
38
|
-
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
if (!existsSync(manifestPath)) {
|
|
39
|
+
function parseAgentFrontmatter(content: string): Record<string, unknown> | null {
|
|
40
|
+
const trimmed = content.trimStart();
|
|
41
|
+
if (!trimmed.startsWith('---')) {
|
|
42
42
|
return null;
|
|
43
43
|
}
|
|
44
|
-
|
|
44
|
+
const endMatch = trimmed.slice(3).indexOf('---');
|
|
45
|
+
if (endMatch === -1) {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
const frontmatterContent = trimmed.slice(3, 3 + endMatch);
|
|
45
49
|
try {
|
|
46
|
-
|
|
47
|
-
return YAML.parse(content) as AgentManifest;
|
|
50
|
+
return YAML.parse(frontmatterContent);
|
|
48
51
|
} catch {
|
|
49
52
|
return null;
|
|
50
53
|
}
|
|
51
54
|
}
|
|
52
55
|
|
|
56
|
+
/**
|
|
57
|
+
* Load an agent manifest from an agent file (agents/{name}.md)
|
|
58
|
+
* Reads frontmatter from the .md file, version from TOOL.yaml
|
|
59
|
+
*/
|
|
60
|
+
export function loadAgentManifest(agentPath: string): AgentManifest | null {
|
|
61
|
+
if (!existsSync(agentPath)) {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const content = readFileSync(agentPath, 'utf-8');
|
|
66
|
+
const frontmatter = parseAgentFrontmatter(content);
|
|
67
|
+
|
|
68
|
+
if (!frontmatter || !frontmatter.name) {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Get version from TOOL.yaml
|
|
73
|
+
// agentPath is tools/{tool}/agents/{name}.md, toolDir is tools/{tool}
|
|
74
|
+
const toolDir = dirname(dirname(agentPath));
|
|
75
|
+
const toolManifest = loadToolManifest(toolDir);
|
|
76
|
+
|
|
77
|
+
return {
|
|
78
|
+
name: frontmatter.name as string,
|
|
79
|
+
description: frontmatter.description as string || '',
|
|
80
|
+
version: toolManifest?.version || '0.0.0',
|
|
81
|
+
status: toolManifest?.status as 'alpha' | 'beta' | 'stable' | undefined,
|
|
82
|
+
mode: frontmatter.mode as 'primary' | 'subagent' | 'all' | undefined,
|
|
83
|
+
model: frontmatter.model as string | undefined,
|
|
84
|
+
color: frontmatter.color as string | undefined,
|
|
85
|
+
tools: frontmatter.tools as string[] | undefined,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
53
89
|
/**
|
|
54
90
|
* Get all bundled agents from tools
|
|
55
91
|
*/
|
|
@@ -61,7 +97,7 @@ export function getBundledAgents(): AgentManifest[] {
|
|
|
61
97
|
return agents;
|
|
62
98
|
}
|
|
63
99
|
|
|
64
|
-
// Get agents from tools/*/agents
|
|
100
|
+
// Get agents from tools/*/agents/*.md
|
|
65
101
|
const toolDirs = readdirSync(BUNDLED_TOOLS_DIR, { withFileTypes: true })
|
|
66
102
|
.filter((dirent) => dirent.isDirectory())
|
|
67
103
|
.map((dirent) => dirent.name);
|
|
@@ -70,12 +106,12 @@ export function getBundledAgents(): AgentManifest[] {
|
|
|
70
106
|
const toolAgentsDir = join(BUNDLED_TOOLS_DIR, toolName, 'agents');
|
|
71
107
|
if (!existsSync(toolAgentsDir)) continue;
|
|
72
108
|
|
|
73
|
-
const
|
|
74
|
-
.filter((dirent) => dirent.
|
|
109
|
+
const agentFiles = readdirSync(toolAgentsDir, { withFileTypes: true })
|
|
110
|
+
.filter((dirent) => dirent.isFile() && dirent.name.endsWith('.md'))
|
|
75
111
|
.map((dirent) => dirent.name);
|
|
76
112
|
|
|
77
|
-
for (const
|
|
78
|
-
const manifest = loadAgentManifest(join(toolAgentsDir,
|
|
113
|
+
for (const agentFile of agentFiles) {
|
|
114
|
+
const manifest = loadAgentManifest(join(toolAgentsDir, agentFile));
|
|
79
115
|
if (manifest && !seenNames.has(manifest.name)) {
|
|
80
116
|
agents.push(manifest);
|
|
81
117
|
seenNames.add(manifest.name);
|
|
@@ -166,35 +202,23 @@ function generateOpenCodeAgent(manifest: AgentManifest, agentContent: string): s
|
|
|
166
202
|
}
|
|
167
203
|
|
|
168
204
|
/**
|
|
169
|
-
* Install an agent from a specific path
|
|
205
|
+
* Install an agent from a specific path (agents/{name}.md)
|
|
170
206
|
* Generates the appropriate format based on the configured AI tool
|
|
171
207
|
*/
|
|
172
|
-
export function installAgentFromPath(
|
|
208
|
+
export function installAgentFromPath(agentPath: string, agentName: string): { success: boolean; message: string } {
|
|
173
209
|
const config = loadConfig();
|
|
174
|
-
const manifestPath = join(agentDir, 'AGENT.yaml');
|
|
175
|
-
const contentPath = join(agentDir, 'AGENT.md');
|
|
176
210
|
|
|
177
|
-
|
|
211
|
+
// Load manifest from agent file frontmatter
|
|
212
|
+
const manifest = loadAgentManifest(agentPath);
|
|
213
|
+
if (!manifest) {
|
|
178
214
|
return { success: false, message: `Agent manifest not found: ${agentName}` };
|
|
179
215
|
}
|
|
180
216
|
|
|
181
217
|
try {
|
|
182
|
-
// Load
|
|
183
|
-
const
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
let agentContent = '';
|
|
187
|
-
if (existsSync(contentPath)) {
|
|
188
|
-
const rawContent = readFileSync(contentPath, 'utf-8');
|
|
189
|
-
// Strip frontmatter from AGENT.md if present (we'll use AGENT.yaml for metadata)
|
|
190
|
-
const frontmatterMatch = rawContent.match(/^---\n[\s\S]*?\n---\n?/);
|
|
191
|
-
agentContent = frontmatterMatch ? rawContent.slice(frontmatterMatch[0].length) : rawContent;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
// If no AGENT.md content, use persona from AGENT.yaml
|
|
195
|
-
if (!agentContent.trim() && manifest.persona) {
|
|
196
|
-
agentContent = manifest.persona;
|
|
197
|
-
}
|
|
218
|
+
// Load content (strip frontmatter)
|
|
219
|
+
const rawContent = readFileSync(agentPath, 'utf-8');
|
|
220
|
+
const frontmatterMatch = rawContent.match(/^---\n[\s\S]*?\n---\n?/);
|
|
221
|
+
const agentContent = frontmatterMatch ? rawContent.slice(frontmatterMatch[0].length) : rawContent;
|
|
198
222
|
|
|
199
223
|
// Generate format based on platform
|
|
200
224
|
const installedContent = config.platform === Platform.ClaudeCode
|
|
@@ -222,7 +246,7 @@ export function installAgentFromPath(agentDir: string, agentName: string): { suc
|
|
|
222
246
|
}
|
|
223
247
|
|
|
224
248
|
/**
|
|
225
|
-
* Find the path to an agent within the tools directory structure
|
|
249
|
+
* Find the path to an agent file within the tools directory structure
|
|
226
250
|
*/
|
|
227
251
|
export function findAgentPath(agentName: string): string | null {
|
|
228
252
|
if (!existsSync(BUNDLED_TOOLS_DIR)) {
|
|
@@ -234,9 +258,9 @@ export function findAgentPath(agentName: string): string | null {
|
|
|
234
258
|
.map((dirent) => dirent.name);
|
|
235
259
|
|
|
236
260
|
for (const toolName of toolDirs) {
|
|
237
|
-
const
|
|
238
|
-
if (existsSync(
|
|
239
|
-
return
|
|
261
|
+
const agentPath = join(BUNDLED_TOOLS_DIR, toolName, 'agents', `${agentName}.md`);
|
|
262
|
+
if (existsSync(agentPath)) {
|
|
263
|
+
return agentPath;
|
|
240
264
|
}
|
|
241
265
|
}
|
|
242
266
|
|
|
@@ -245,14 +269,13 @@ export function findAgentPath(agentName: string): string | null {
|
|
|
245
269
|
|
|
246
270
|
/**
|
|
247
271
|
* Install an agent from the tools directory
|
|
248
|
-
* Combines AGENT.yaml metadata with AGENT.md content into a single .md file
|
|
249
272
|
*/
|
|
250
273
|
export function installAgent(agentName: string): { success: boolean; message: string } {
|
|
251
|
-
const
|
|
252
|
-
if (!
|
|
274
|
+
const agentPath = findAgentPath(agentName);
|
|
275
|
+
if (!agentPath) {
|
|
253
276
|
return { success: false, message: `Agent '${agentName}' not found` };
|
|
254
277
|
}
|
|
255
|
-
return installAgentFromPath(
|
|
278
|
+
return installAgentFromPath(agentPath, agentName);
|
|
256
279
|
}
|
|
257
280
|
|
|
258
281
|
/**
|
package/src/lib/skills.test.ts
CHANGED
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
getPlatformConfigPath,
|
|
12
12
|
getSkillStatusDisplay,
|
|
13
13
|
getBundledSkillsDir,
|
|
14
|
+
loadSkillManifest,
|
|
14
15
|
} from './skills.js';
|
|
15
16
|
|
|
16
17
|
/**
|
|
@@ -180,58 +181,52 @@ function getAllSkillPaths(toolsDir: string): Array<{ skillName: string; skillDir
|
|
|
180
181
|
}
|
|
181
182
|
|
|
182
183
|
describe('bundled skills validation', () => {
|
|
183
|
-
it('all skills should have SKILL.md
|
|
184
|
+
it('all skills should have SKILL.md', () => {
|
|
184
185
|
const toolsDir = getBundledSkillsDir();
|
|
185
186
|
const skillPaths = getAllSkillPaths(toolsDir);
|
|
186
187
|
|
|
187
188
|
expect(skillPaths.length).toBeGreaterThan(0);
|
|
188
189
|
|
|
189
|
-
for (const {
|
|
190
|
+
for (const { skillDir } of skillPaths) {
|
|
190
191
|
const skillMdPath = join(skillDir, 'SKILL.md');
|
|
191
192
|
expect(existsSync(skillMdPath)).toBe(true);
|
|
192
|
-
|
|
193
|
-
const content = readFileSync(skillMdPath, 'utf-8');
|
|
194
|
-
const frontmatter = parseFrontmatter(content);
|
|
195
|
-
|
|
196
|
-
expect(frontmatter).not.toBeNull();
|
|
197
|
-
expect(frontmatter?.name).toBe(skillName);
|
|
198
|
-
expect(typeof frontmatter?.description).toBe('string');
|
|
199
193
|
}
|
|
200
194
|
});
|
|
201
195
|
|
|
202
|
-
it('all skills should have
|
|
196
|
+
it('all skills should have metadata in TOOL.yaml', () => {
|
|
203
197
|
const toolsDir = getBundledSkillsDir();
|
|
204
198
|
const skillPaths = getAllSkillPaths(toolsDir);
|
|
205
199
|
|
|
206
200
|
for (const { skillName, skillDir } of skillPaths) {
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
201
|
+
// Get the parent tool directory
|
|
202
|
+
const toolDir = join(skillDir, '..', '..');
|
|
203
|
+
const toolYamlPath = join(toolDir, 'TOOL.yaml');
|
|
204
|
+
expect(existsSync(toolYamlPath)).toBe(true);
|
|
205
|
+
|
|
206
|
+
const toolContent = readFileSync(toolYamlPath, 'utf-8');
|
|
207
|
+
const toolManifest = YAML.parse(toolContent);
|
|
208
|
+
|
|
209
|
+
// Find the skill in includes.skills
|
|
210
|
+
const skillInclude = toolManifest.includes?.skills?.find(
|
|
211
|
+
(s: { name: string }) => s.name === skillName
|
|
212
|
+
);
|
|
213
|
+
expect(skillInclude).toBeDefined();
|
|
214
|
+
expect(typeof toolManifest.description).toBe('string');
|
|
215
|
+
expect(typeof toolManifest.version).toBe('string');
|
|
216
216
|
}
|
|
217
217
|
});
|
|
218
218
|
|
|
219
|
-
it('
|
|
219
|
+
it('loadSkillManifest should return valid manifest from TOOL.yaml', () => {
|
|
220
220
|
const toolsDir = getBundledSkillsDir();
|
|
221
221
|
const skillPaths = getAllSkillPaths(toolsDir);
|
|
222
222
|
|
|
223
|
-
for (const { skillDir } of skillPaths) {
|
|
224
|
-
const
|
|
225
|
-
const yamlPath = join(skillDir, 'SKILL.yaml');
|
|
226
|
-
|
|
227
|
-
const mdContent = readFileSync(mdPath, 'utf-8');
|
|
228
|
-
const yamlContent = readFileSync(yamlPath, 'utf-8');
|
|
229
|
-
|
|
230
|
-
const frontmatter = parseFrontmatter(mdContent);
|
|
231
|
-
const manifest = YAML.parse(yamlContent);
|
|
223
|
+
for (const { skillName, skillDir } of skillPaths) {
|
|
224
|
+
const manifest = loadSkillManifest(skillDir);
|
|
232
225
|
|
|
233
|
-
expect(
|
|
234
|
-
expect(
|
|
226
|
+
expect(manifest).not.toBeNull();
|
|
227
|
+
expect(manifest?.name).toBe(skillName);
|
|
228
|
+
expect(typeof manifest?.description).toBe('string');
|
|
229
|
+
expect(typeof manifest?.version).toBe('string');
|
|
235
230
|
}
|
|
236
231
|
});
|
|
237
232
|
});
|
package/src/lib/skills.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { loadConfig, saveConfig } from './config.js';
|
|
|
6
6
|
import { Platform, SkillStatus, type SkillManifest, type InstalledSkill, getPlatformTools, setPlatformTools } from './types.js';
|
|
7
7
|
import { getInstalledAgentsDir, installAgentFromPath, uninstallAgent, isAgentInstalled } from './agents.js';
|
|
8
8
|
import { getSkillsPath, getCommandsPath, getConfigPath } from './platforms.js';
|
|
9
|
+
import { loadToolManifest } from './tools.js';
|
|
9
10
|
|
|
10
11
|
// Marker comments for CLAUDE.md skill registration
|
|
11
12
|
const DROID_SKILLS_START = '<!-- droid-skills-start -->';
|
|
@@ -84,22 +85,57 @@ export function updatePlatformConfigSkills(platform: Platform, installedSkills:
|
|
|
84
85
|
writeFileSync(configPath, content, 'utf-8');
|
|
85
86
|
}
|
|
86
87
|
|
|
88
|
+
/**
|
|
89
|
+
* Parse YAML frontmatter from SKILL.md content
|
|
90
|
+
*/
|
|
91
|
+
function parseSkillFrontmatter(content: string): Record<string, unknown> | null {
|
|
92
|
+
const trimmed = content.trimStart();
|
|
93
|
+
if (!trimmed.startsWith('---')) {
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
const endMatch = trimmed.slice(3).indexOf('---');
|
|
97
|
+
if (endMatch === -1) {
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
const frontmatterContent = trimmed.slice(3, 3 + endMatch);
|
|
101
|
+
try {
|
|
102
|
+
return YAML.parse(frontmatterContent);
|
|
103
|
+
} catch {
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
87
108
|
/**
|
|
88
109
|
* Load a skill manifest from a skill directory
|
|
110
|
+
* Reads frontmatter from SKILL.md, version from TOOL.yaml
|
|
89
111
|
*/
|
|
90
112
|
export function loadSkillManifest(skillDir: string): SkillManifest | null {
|
|
91
|
-
const
|
|
113
|
+
const skillMdPath = join(skillDir, 'SKILL.md');
|
|
92
114
|
|
|
93
|
-
|
|
115
|
+
// Read SKILL.md and parse frontmatter
|
|
116
|
+
if (!existsSync(skillMdPath)) {
|
|
94
117
|
return null;
|
|
95
118
|
}
|
|
96
119
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
120
|
+
const content = readFileSync(skillMdPath, 'utf-8');
|
|
121
|
+
const frontmatter = parseSkillFrontmatter(content);
|
|
122
|
+
|
|
123
|
+
if (!frontmatter || !frontmatter.name) {
|
|
101
124
|
return null;
|
|
102
125
|
}
|
|
126
|
+
|
|
127
|
+
// Get version and other tool-level info from TOOL.yaml
|
|
128
|
+
const toolDir = dirname(dirname(skillDir)); // skillDir is tools/{tool}/skills/{skill}, toolDir is tools/{tool}
|
|
129
|
+
const toolManifest = loadToolManifest(toolDir);
|
|
130
|
+
|
|
131
|
+
return {
|
|
132
|
+
name: frontmatter.name as string,
|
|
133
|
+
description: frontmatter.description as string || '',
|
|
134
|
+
version: toolManifest?.version || '0.0.0',
|
|
135
|
+
status: toolManifest?.status,
|
|
136
|
+
dependencies: toolManifest?.dependencies,
|
|
137
|
+
config_schema: toolManifest?.config_schema,
|
|
138
|
+
};
|
|
103
139
|
}
|
|
104
140
|
|
|
105
141
|
/**
|
|
@@ -120,7 +156,8 @@ export function findSkillPath(skillName: string): { toolDir: string; skillDir: s
|
|
|
120
156
|
if (!existsSync(skillsDir)) continue;
|
|
121
157
|
|
|
122
158
|
const skillDir = join(skillsDir, skillName);
|
|
123
|
-
|
|
159
|
+
// Check for SKILL.md (content file) - metadata now comes from TOOL.yaml
|
|
160
|
+
if (existsSync(skillDir) && existsSync(join(skillDir, 'SKILL.md'))) {
|
|
124
161
|
return {
|
|
125
162
|
toolDir: join(BUNDLED_SKILLS_DIR, toolName),
|
|
126
163
|
skillDir,
|
|
@@ -385,7 +422,7 @@ export function installSkill(skillName: string): { success: boolean; message: st
|
|
|
385
422
|
mkdirSync(skillsPath, { recursive: true });
|
|
386
423
|
}
|
|
387
424
|
|
|
388
|
-
// Copy SKILL.md (
|
|
425
|
+
// Copy SKILL.md (includes its own frontmatter)
|
|
389
426
|
const skillMdSource = join(skillDir, 'SKILL.md');
|
|
390
427
|
if (existsSync(skillMdSource)) {
|
|
391
428
|
if (!existsSync(targetSkillDir)) {
|
package/src/lib/types.ts
CHANGED
|
@@ -127,6 +127,11 @@ export interface ToolSkillInclude {
|
|
|
127
127
|
name: string;
|
|
128
128
|
required: boolean;
|
|
129
129
|
description?: string;
|
|
130
|
+
// Skill-specific metadata (injected as frontmatter on install)
|
|
131
|
+
globs?: string[];
|
|
132
|
+
alwaysApply?: boolean;
|
|
133
|
+
provides_output?: boolean;
|
|
134
|
+
examples?: SkillExample[];
|
|
130
135
|
}
|
|
131
136
|
|
|
132
137
|
export interface ToolIncludes {
|
package/src/tools/README.md
CHANGED
|
@@ -1,55 +1,63 @@
|
|
|
1
|
-
# Contributing
|
|
1
|
+
# Contributing Tools
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Tools are bundles of skills, commands, and agents that can be installed into Claude Code or OpenCode.
|
|
4
4
|
|
|
5
5
|
## Directory Structure
|
|
6
6
|
|
|
7
|
-
Each skill is a directory containing:
|
|
8
|
-
|
|
9
7
|
```
|
|
10
|
-
|
|
11
|
-
└── my-
|
|
12
|
-
├──
|
|
13
|
-
├──
|
|
14
|
-
└──
|
|
15
|
-
|
|
8
|
+
tools/
|
|
9
|
+
└── my-tool/
|
|
10
|
+
├── TOOL.yaml # Required: Tool manifest
|
|
11
|
+
├── skills/
|
|
12
|
+
│ └── my-skill/
|
|
13
|
+
│ ├── SKILL.md # Required: Skill instructions + frontmatter
|
|
14
|
+
│ └── references/ # Optional: Additional context files
|
|
15
|
+
├── commands/
|
|
16
|
+
│ └── my-command.md # Command instructions + frontmatter
|
|
17
|
+
└── agents/
|
|
18
|
+
└── my-agent.md # Agent instructions + frontmatter
|
|
16
19
|
```
|
|
17
20
|
|
|
18
|
-
##
|
|
21
|
+
## TOOL.yaml (Manifest)
|
|
22
|
+
|
|
23
|
+
The tool manifest defines what's included and tool-level metadata:
|
|
19
24
|
|
|
20
25
|
```yaml
|
|
21
|
-
name: my-
|
|
22
|
-
description: Short description
|
|
23
|
-
version: 1.0.0
|
|
24
|
-
status: beta
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
26
|
+
name: my-tool
|
|
27
|
+
description: "Short description shown in TUI"
|
|
28
|
+
version: 1.0.0
|
|
29
|
+
status: beta # alpha | beta | stable
|
|
30
|
+
|
|
31
|
+
includes:
|
|
32
|
+
skills:
|
|
33
|
+
- name: my-skill
|
|
34
|
+
required: true # Must be installed with tool
|
|
35
|
+
commands:
|
|
36
|
+
- my-command # Just the name (no extension)
|
|
37
|
+
agents:
|
|
38
|
+
- my-agent # Just the name (no extension)
|
|
39
|
+
|
|
40
|
+
dependencies: [] # Other tools required
|
|
41
|
+
|
|
42
|
+
config_schema: # Optional configuration
|
|
30
43
|
option_name:
|
|
31
|
-
type: string
|
|
44
|
+
type: string
|
|
32
45
|
description: What this option does
|
|
33
|
-
default: "default value"
|
|
34
|
-
|
|
35
|
-
# Examples shown in TUI (optional)
|
|
36
|
-
examples:
|
|
37
|
-
- title: "Example name"
|
|
38
|
-
code: |
|
|
39
|
-
// Example code block
|
|
46
|
+
default: "default value"
|
|
40
47
|
```
|
|
41
48
|
|
|
42
|
-
##
|
|
49
|
+
## Skills
|
|
43
50
|
|
|
44
|
-
|
|
51
|
+
Skills provide context and instructions to the AI. Create `skills/{name}/SKILL.md`:
|
|
45
52
|
|
|
46
53
|
```markdown
|
|
47
54
|
---
|
|
48
55
|
name: my-skill
|
|
49
|
-
description:
|
|
56
|
+
description: "What this skill does"
|
|
50
57
|
globs:
|
|
51
|
-
- "
|
|
52
|
-
alwaysApply: false
|
|
58
|
+
- "**/*.ts" # File patterns this skill applies to
|
|
59
|
+
alwaysApply: false # Always include in context?
|
|
60
|
+
allowed-tools: Read, Grep, Glob # Pre-authorize tools (reduces permission prompts)
|
|
53
61
|
---
|
|
54
62
|
|
|
55
63
|
# My Skill
|
|
@@ -57,29 +65,50 @@ alwaysApply: false # Always include in context?
|
|
|
57
65
|
Instructions for the AI on how to use this skill...
|
|
58
66
|
```
|
|
59
67
|
|
|
60
|
-
|
|
68
|
+
Skills can have a `references/` subdirectory for additional context files.
|
|
61
69
|
|
|
62
|
-
Commands
|
|
70
|
+
## Commands
|
|
63
71
|
|
|
72
|
+
Commands are slash commands the user can invoke. Create `commands/{name}.md`:
|
|
73
|
+
|
|
74
|
+
```markdown
|
|
75
|
+
---
|
|
76
|
+
name: my-command
|
|
77
|
+
description: "What this command does"
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
Instructions for what to do when /my-command is invoked...
|
|
64
81
|
```
|
|
65
|
-
commands/
|
|
66
|
-
└── do-thing.md
|
|
67
|
-
```
|
|
68
82
|
|
|
69
|
-
|
|
83
|
+
## Agents
|
|
84
|
+
|
|
85
|
+
Agents are specialized AI personas. Create `agents/{name}.md`:
|
|
86
|
+
|
|
87
|
+
```markdown
|
|
88
|
+
---
|
|
89
|
+
name: my-agent
|
|
90
|
+
description: "What this agent does"
|
|
91
|
+
tools:
|
|
92
|
+
- Read
|
|
93
|
+
- Grep
|
|
94
|
+
- Glob
|
|
95
|
+
color: purple # Display colour (Claude Code only)
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
You are a specialized agent that...
|
|
99
|
+
```
|
|
70
100
|
|
|
71
|
-
## Testing Your
|
|
101
|
+
## Testing Your Tool
|
|
72
102
|
|
|
73
|
-
1. Run `
|
|
74
|
-
2. Run `
|
|
75
|
-
3. Navigate to
|
|
76
|
-
4. Install
|
|
77
|
-
5. Test in Claude Code
|
|
103
|
+
1. Run `bun run build` to compile
|
|
104
|
+
2. Run `bun run start` to open the TUI
|
|
105
|
+
3. Navigate to your tool
|
|
106
|
+
4. Install the tool
|
|
107
|
+
5. Test in Claude Code
|
|
78
108
|
|
|
79
109
|
## Checklist
|
|
80
110
|
|
|
81
|
-
- [ ] `
|
|
82
|
-
- [ ]
|
|
83
|
-
- [ ]
|
|
84
|
-
- [ ]
|
|
85
|
-
- [ ] Tests pass: `bun test src/lib/skills.test.ts`
|
|
111
|
+
- [ ] `TOOL.yaml` has all required fields (name, description, version, includes)
|
|
112
|
+
- [ ] All .md files have valid YAML frontmatter with `name` and `description`
|
|
113
|
+
- [ ] Names in frontmatter match the file/directory names
|
|
114
|
+
- [ ] Tests pass: `bun test`
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
name: brain
|
|
2
|
-
description: "
|
|
2
|
+
description: "Your scratchpad (or brain) - a collaborative space for planning and research. Create docs with /brain plan, /brain research, or /brain review. Use @mentions for async discussion. Docs persist across sessions."
|
|
3
3
|
version: 0.2.0
|
|
4
4
|
status: beta
|
|
5
5
|
|
|
@@ -17,5 +17,5 @@ dependencies:
|
|
|
17
17
|
config_schema:
|
|
18
18
|
scaffold_verbosity:
|
|
19
19
|
type: string
|
|
20
|
-
description: "How detailed scaffold hints should be: minimal, medium, detailed"
|
|
20
|
+
description: "How detailed scaffold hints should be: minimal (signatures only), medium (hints), detailed (pseudocode)"
|
|
21
21
|
default: "medium"
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
name: coach
|
|
3
3
|
description: "Learning-mode AI assistance - AI as coach, not crutch. Triggers on phrases like 'help me think through', 'coach me on', 'I want to learn how to', or 'don't just give me the answer'. Use /coach plan for co-authored planning, /coach scaffold for structure with hints, /coach review for Socratic questions on your code."
|
|
4
4
|
alwaysApply: false
|
|
5
|
+
allowed-tools: Read, Grep, Glob
|
|
5
6
|
---
|
|
6
7
|
|
|
7
8
|
# Coach Skill
|
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: edi-standards-reviewer
|
|
3
|
+
description: "Review code for EDI integration patterns, partnership handling, and billing system concerns. Use PROACTIVELY when changes touch trading partners, transactions, or billing."
|
|
4
|
+
tools:
|
|
5
|
+
- Read
|
|
6
|
+
- Grep
|
|
7
|
+
- Glob
|
|
8
|
+
color: blue
|
|
9
|
+
---
|
|
10
|
+
|
|
1
11
|
You are a domain-aware code reviewer that understands EDI patterns and integration best practices.
|
|
2
12
|
|
|
3
13
|
## How to Review
|
package/src/tools/code-review/agents/{error-handling-reviewer/AGENT.md → error-handling-reviewer.md}
RENAMED
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: error-handling-reviewer
|
|
3
|
+
description: "Hunt for silent failures and missing error handling. Use PROACTIVELY to find try/catch blocks that swallow errors, promises without rejection handling, and missing validation."
|
|
4
|
+
tools:
|
|
5
|
+
- Read
|
|
6
|
+
- Grep
|
|
7
|
+
- Glob
|
|
8
|
+
color: orange
|
|
9
|
+
---
|
|
10
|
+
|
|
1
11
|
You are a reliability engineer hunting for silent failures.
|
|
2
12
|
|
|
3
13
|
## Silent Failure Patterns
|
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: test-coverage-analyzer
|
|
3
|
+
description: "Analyze test coverage for code changes. Use PROACTIVELY when reviewing PRs or before merging to ensure adequate test coverage."
|
|
4
|
+
tools:
|
|
5
|
+
- Read
|
|
6
|
+
- Grep
|
|
7
|
+
- Glob
|
|
8
|
+
- Bash
|
|
9
|
+
color: green
|
|
10
|
+
---
|
|
11
|
+
|
|
1
12
|
You are a testing specialist focused on comprehensive coverage.
|
|
2
13
|
|
|
3
14
|
## Review Process
|
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: type-reviewer
|
|
3
|
+
description: "Review TypeScript type design and interface contracts. Check for proper typing, avoid `any`, ensure domain types are used correctly."
|
|
4
|
+
tools:
|
|
5
|
+
- Read
|
|
6
|
+
- Grep
|
|
7
|
+
- Glob
|
|
8
|
+
color: purple
|
|
9
|
+
---
|
|
10
|
+
|
|
1
11
|
You are a TypeScript expert focused on type safety and design.
|
|
2
12
|
|
|
3
13
|
## Review Focus
|