@orderful/droid 0.14.0 → 0.16.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.
Files changed (204) hide show
  1. package/.claude/CLAUDE.md +31 -9
  2. package/CHANGELOG.md +35 -0
  3. package/dist/bin/droid.js +8 -8
  4. package/dist/bin/droid.js.map +1 -1
  5. package/dist/commands/config.js +1 -1
  6. package/dist/commands/config.js.map +1 -1
  7. package/dist/commands/install.js +3 -3
  8. package/dist/commands/install.js.map +1 -1
  9. package/dist/commands/setup.d.ts +1 -1
  10. package/dist/commands/setup.d.ts.map +1 -1
  11. package/dist/commands/setup.js +3 -3
  12. package/dist/commands/setup.js.map +1 -1
  13. package/dist/commands/skills.js +4 -4
  14. package/dist/commands/skills.js.map +1 -1
  15. package/dist/commands/tui/components/Badge.d.ts +13 -0
  16. package/dist/commands/tui/components/Badge.d.ts.map +1 -0
  17. package/dist/commands/tui/components/Badge.js +29 -0
  18. package/dist/commands/tui/components/Badge.js.map +1 -0
  19. package/dist/commands/tui/components/Markdown.d.ts +5 -0
  20. package/dist/commands/tui/components/Markdown.d.ts.map +1 -0
  21. package/dist/commands/tui/components/Markdown.js +42 -0
  22. package/dist/commands/tui/components/Markdown.js.map +1 -0
  23. package/dist/commands/tui/components/SettingsDetails.d.ts +5 -0
  24. package/dist/commands/tui/components/SettingsDetails.d.ts.map +1 -0
  25. package/dist/commands/tui/components/SettingsDetails.js +11 -0
  26. package/dist/commands/tui/components/SettingsDetails.js.map +1 -0
  27. package/dist/commands/tui/components/TabBar.d.ts +10 -0
  28. package/dist/commands/tui/components/TabBar.d.ts.map +1 -0
  29. package/dist/commands/tui/components/TabBar.js +7 -0
  30. package/dist/commands/tui/components/TabBar.js.map +1 -0
  31. package/dist/commands/tui/components/ToolDetails.d.ts +8 -0
  32. package/dist/commands/tui/components/ToolDetails.d.ts.map +1 -0
  33. package/dist/commands/tui/components/ToolDetails.js +35 -0
  34. package/dist/commands/tui/components/ToolDetails.js.map +1 -0
  35. package/dist/commands/tui/components/ToolItem.d.ts +9 -0
  36. package/dist/commands/tui/components/ToolItem.d.ts.map +1 -0
  37. package/dist/commands/tui/components/ToolItem.js +11 -0
  38. package/dist/commands/tui/components/ToolItem.js.map +1 -0
  39. package/dist/commands/tui/constants.d.ts +16 -0
  40. package/dist/commands/tui/constants.d.ts.map +1 -0
  41. package/dist/commands/tui/constants.js +17 -0
  42. package/dist/commands/tui/constants.js.map +1 -0
  43. package/dist/commands/tui/hooks/useAppUpdate.d.ts +13 -0
  44. package/dist/commands/tui/hooks/useAppUpdate.d.ts.map +1 -0
  45. package/dist/commands/tui/hooks/useAppUpdate.js +52 -0
  46. package/dist/commands/tui/hooks/useAppUpdate.js.map +1 -0
  47. package/dist/commands/tui/hooks/useToolUpdates.d.ts +22 -0
  48. package/dist/commands/tui/hooks/useToolUpdates.d.ts.map +1 -0
  49. package/dist/commands/tui/hooks/useToolUpdates.js +77 -0
  50. package/dist/commands/tui/hooks/useToolUpdates.js.map +1 -0
  51. package/dist/commands/tui/types.d.ts +5 -0
  52. package/dist/commands/tui/types.d.ts.map +1 -0
  53. package/dist/commands/tui/types.js +2 -0
  54. package/dist/commands/tui/types.js.map +1 -0
  55. package/dist/commands/tui/views/ReadmeViewer.d.ts +7 -0
  56. package/dist/commands/tui/views/ReadmeViewer.d.ts.map +1 -0
  57. package/dist/commands/tui/views/ReadmeViewer.js +56 -0
  58. package/dist/commands/tui/views/ReadmeViewer.js.map +1 -0
  59. package/dist/commands/tui/views/SetupScreen.d.ts +8 -0
  60. package/dist/commands/tui/views/SetupScreen.d.ts.map +1 -0
  61. package/dist/commands/tui/views/SetupScreen.js +114 -0
  62. package/dist/commands/tui/views/SetupScreen.js.map +1 -0
  63. package/dist/commands/tui/views/SkillConfigScreen.d.ts +8 -0
  64. package/dist/commands/tui/views/SkillConfigScreen.d.ts.map +1 -0
  65. package/dist/commands/tui/views/SkillConfigScreen.js +148 -0
  66. package/dist/commands/tui/views/SkillConfigScreen.js.map +1 -0
  67. package/dist/commands/tui/views/ToolExplorer.d.ts +8 -0
  68. package/dist/commands/tui/views/ToolExplorer.d.ts.map +1 -0
  69. package/dist/commands/tui/views/ToolExplorer.js +86 -0
  70. package/dist/commands/tui/views/ToolExplorer.js.map +1 -0
  71. package/dist/commands/tui/views/ToolUpdatePrompt.d.ts +10 -0
  72. package/dist/commands/tui/views/ToolUpdatePrompt.d.ts.map +1 -0
  73. package/dist/commands/tui/views/ToolUpdatePrompt.js +38 -0
  74. package/dist/commands/tui/views/ToolUpdatePrompt.js.map +1 -0
  75. package/dist/commands/tui/views/WelcomeScreen.d.ts +11 -0
  76. package/dist/commands/tui/views/WelcomeScreen.d.ts.map +1 -0
  77. package/dist/commands/tui/views/WelcomeScreen.js +46 -0
  78. package/dist/commands/tui/views/WelcomeScreen.js.map +1 -0
  79. package/dist/commands/tui.d.ts.map +1 -1
  80. package/dist/commands/tui.js +54 -756
  81. package/dist/commands/tui.js.map +1 -1
  82. package/dist/commands/uninstall.js +2 -2
  83. package/dist/commands/uninstall.js.map +1 -1
  84. package/dist/commands/update.js +1 -1
  85. package/dist/commands/update.js.map +1 -1
  86. package/dist/index.d.ts +4 -4
  87. package/dist/index.d.ts.map +1 -1
  88. package/dist/index.js +4 -4
  89. package/dist/index.js.map +1 -1
  90. package/dist/lib/agents.d.ts +7 -7
  91. package/dist/lib/agents.d.ts.map +1 -1
  92. package/dist/lib/agents.js +63 -41
  93. package/dist/lib/agents.js.map +1 -1
  94. package/dist/lib/config.d.ts +1 -1
  95. package/dist/lib/config.d.ts.map +1 -1
  96. package/dist/lib/config.js +1 -1
  97. package/dist/lib/config.js.map +1 -1
  98. package/dist/lib/platforms.d.ts +1 -1
  99. package/dist/lib/platforms.d.ts.map +1 -1
  100. package/dist/lib/platforms.js +1 -1
  101. package/dist/lib/platforms.js.map +1 -1
  102. package/dist/lib/skill-config.d.ts +1 -1
  103. package/dist/lib/skill-config.d.ts.map +1 -1
  104. package/dist/lib/skill-config.js +2 -2
  105. package/dist/lib/skill-config.js.map +1 -1
  106. package/dist/lib/skills.d.ts +2 -1
  107. package/dist/lib/skills.d.ts.map +1 -1
  108. package/dist/lib/skills.js +45 -12
  109. package/dist/lib/skills.js.map +1 -1
  110. package/dist/lib/tools.d.ts +1 -1
  111. package/dist/lib/tools.d.ts.map +1 -1
  112. package/dist/lib/tools.js +3 -3
  113. package/dist/lib/tools.js.map +1 -1
  114. package/dist/lib/types.d.ts +4 -0
  115. package/dist/lib/types.d.ts.map +1 -1
  116. package/dist/tools/README.md +79 -50
  117. package/dist/tools/brain/TOOL.yaml +1 -1
  118. package/dist/tools/brain/skills/brain/SKILL.md +1 -0
  119. package/dist/tools/brain/skills/brain-obsidian/SKILL.md +1 -0
  120. package/dist/tools/coach/TOOL.yaml +1 -1
  121. package/dist/tools/coach/skills/coach/SKILL.md +1 -0
  122. package/{src/tools/code-review/agents/edi-standards-reviewer/AGENT.md → dist/tools/code-review/agents/edi-standards-reviewer.md} +10 -0
  123. package/dist/tools/code-review/agents/{error-handling-reviewer/AGENT.md → error-handling-reviewer.md} +10 -0
  124. package/{src/tools/code-review/agents/test-coverage-analyzer/AGENT.md → dist/tools/code-review/agents/test-coverage-analyzer.md} +11 -0
  125. package/dist/tools/code-review/agents/{type-reviewer/AGENT.md → type-reviewer.md} +10 -0
  126. package/dist/tools/code-review/skills/code-review/SKILL.md +1 -0
  127. package/dist/tools/comments/TOOL.yaml +2 -2
  128. package/dist/tools/comments/skills/comments/SKILL.md +1 -0
  129. package/dist/tools/droid/skills/droid/SKILL.md +1 -0
  130. package/dist/tools/project/skills/project/SKILL.md +1 -0
  131. package/package.json +3 -3
  132. package/src/bin/droid.ts +8 -8
  133. package/src/commands/config.ts +1 -1
  134. package/src/commands/install.ts +3 -3
  135. package/src/commands/setup.test.ts +1 -1
  136. package/src/commands/setup.ts +3 -3
  137. package/src/commands/skills.ts +4 -4
  138. package/src/commands/tui/components/Badge.tsx +86 -0
  139. package/src/commands/tui/components/Markdown.tsx +48 -0
  140. package/src/commands/tui/components/SettingsDetails.tsx +70 -0
  141. package/src/commands/tui/components/TabBar.tsx +26 -0
  142. package/src/commands/tui/components/ToolDetails.tsx +117 -0
  143. package/src/commands/tui/components/ToolItem.tsx +39 -0
  144. package/src/commands/tui/constants.ts +17 -0
  145. package/src/commands/tui/hooks/useAppUpdate.ts +67 -0
  146. package/src/commands/tui/hooks/useToolUpdates.ts +110 -0
  147. package/src/commands/tui/types.ts +4 -0
  148. package/src/commands/tui/views/ReadmeViewer.tsx +93 -0
  149. package/src/commands/tui/views/SetupScreen.tsx +242 -0
  150. package/src/commands/tui/views/SkillConfigScreen.tsx +278 -0
  151. package/src/commands/tui/views/ToolExplorer.tsx +190 -0
  152. package/src/commands/tui/views/ToolUpdatePrompt.tsx +109 -0
  153. package/src/commands/tui/views/WelcomeScreen.tsx +149 -0
  154. package/src/commands/tui.tsx +65 -1588
  155. package/src/commands/uninstall.ts +2 -2
  156. package/src/commands/update.ts +1 -1
  157. package/src/index.ts +4 -4
  158. package/src/lib/agents.ts +68 -45
  159. package/src/lib/config.ts +1 -1
  160. package/src/lib/platforms.ts +1 -1
  161. package/src/lib/skill-config.ts +2 -2
  162. package/src/lib/skills.test.ts +28 -33
  163. package/src/lib/skills.ts +49 -12
  164. package/src/lib/tools.ts +3 -3
  165. package/src/lib/types.test.ts +1 -1
  166. package/src/lib/types.ts +5 -0
  167. package/src/lib/version.test.ts +1 -1
  168. package/src/tools/README.md +79 -50
  169. package/src/tools/brain/TOOL.yaml +1 -1
  170. package/src/tools/brain/skills/brain/SKILL.md +1 -0
  171. package/src/tools/brain/skills/brain-obsidian/SKILL.md +1 -0
  172. package/src/tools/coach/TOOL.yaml +1 -1
  173. package/src/tools/coach/skills/coach/SKILL.md +1 -0
  174. package/{dist/tools/code-review/agents/edi-standards-reviewer/AGENT.md → src/tools/code-review/agents/edi-standards-reviewer.md} +10 -0
  175. package/src/tools/code-review/agents/{error-handling-reviewer/AGENT.md → error-handling-reviewer.md} +10 -0
  176. package/{dist/tools/code-review/agents/test-coverage-analyzer/AGENT.md → src/tools/code-review/agents/test-coverage-analyzer.md} +11 -0
  177. package/src/tools/code-review/agents/{type-reviewer/AGENT.md → type-reviewer.md} +10 -0
  178. package/src/tools/code-review/skills/code-review/SKILL.md +1 -0
  179. package/src/tools/comments/TOOL.yaml +2 -2
  180. package/src/tools/comments/skills/comments/SKILL.md +1 -0
  181. package/src/tools/droid/skills/droid/SKILL.md +1 -0
  182. package/src/tools/project/skills/project/SKILL.md +1 -0
  183. package/dist/tools/brain/skills/brain/SKILL.yaml +0 -29
  184. package/dist/tools/brain/skills/brain-obsidian/SKILL.yaml +0 -42
  185. package/dist/tools/coach/skills/coach/SKILL.yaml +0 -25
  186. package/dist/tools/code-review/agents/edi-standards-reviewer/AGENT.yaml +0 -14
  187. package/dist/tools/code-review/agents/error-handling-reviewer/AGENT.yaml +0 -14
  188. package/dist/tools/code-review/agents/test-coverage-analyzer/AGENT.yaml +0 -14
  189. package/dist/tools/code-review/agents/type-reviewer/AGENT.yaml +0 -13
  190. package/dist/tools/code-review/skills/code-review/SKILL.yaml +0 -19
  191. package/dist/tools/comments/skills/comments/SKILL.yaml +0 -50
  192. package/dist/tools/droid/skills/droid/SKILL.yaml +0 -7
  193. package/dist/tools/project/skills/project/SKILL.yaml +0 -30
  194. package/src/tools/brain/skills/brain/SKILL.yaml +0 -29
  195. package/src/tools/brain/skills/brain-obsidian/SKILL.yaml +0 -42
  196. package/src/tools/coach/skills/coach/SKILL.yaml +0 -25
  197. package/src/tools/code-review/agents/edi-standards-reviewer/AGENT.yaml +0 -14
  198. package/src/tools/code-review/agents/error-handling-reviewer/AGENT.yaml +0 -14
  199. package/src/tools/code-review/agents/test-coverage-analyzer/AGENT.yaml +0 -14
  200. package/src/tools/code-review/agents/type-reviewer/AGENT.yaml +0 -13
  201. package/src/tools/code-review/skills/code-review/SKILL.yaml +0 -19
  202. package/src/tools/comments/skills/comments/SKILL.yaml +0 -50
  203. package/src/tools/droid/skills/droid/SKILL.yaml +0 -7
  204. package/src/tools/project/skills/project/SKILL.yaml +0 -30
@@ -1,6 +1,6 @@
1
1
  import chalk from 'chalk';
2
- import { uninstallSkill } from '../lib/skills.js';
3
- import { getBundledTools, isToolInstalled } from '../lib/tools.js';
2
+ import { uninstallSkill } from '../lib/skills';
3
+ import { getBundledTools, isToolInstalled } from '../lib/tools';
4
4
 
5
5
  export async function uninstallCommand(toolName: string): Promise<void> {
6
6
  const tools = getBundledTools();
@@ -1,6 +1,6 @@
1
1
  import chalk from 'chalk';
2
2
  import { execSync } from 'child_process';
3
- import { getVersion } from '../lib/version.js';
3
+ import { getVersion } from '../lib/version';
4
4
 
5
5
  interface UpdateOptions {
6
6
  tools?: boolean;
package/src/index.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  // Re-export types and utilities for potential programmatic usage
2
- export * from './lib/types.js';
3
- export * from './lib/config.js';
4
- export * from './lib/skills.js';
5
- export * from './lib/version.js';
2
+ export * from './lib/types';
3
+ export * from './lib/config';
4
+ export * from './lib/skills';
5
+ export * from './lib/version';
package/src/lib/agents.ts CHANGED
@@ -2,9 +2,10 @@ import { existsSync, readdirSync, readFileSync, writeFileSync, unlinkSync, mkdir
2
2
  import { join, dirname } from 'path';
3
3
  import { fileURLToPath } from 'url';
4
4
  import YAML from 'yaml';
5
- import { loadConfig } from './config.js';
6
- import { Platform } from './types.js';
7
- import { getAgentsPath } from './platforms.js';
5
+ import { loadConfig } from './config';
6
+ import { Platform } from './types';
7
+ import { getAgentsPath } from './platforms';
8
+ import { loadToolManifest } from './tools';
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
- * Load an agent manifest from an agent directory
37
+ * Parse YAML frontmatter from agent file content
37
38
  */
38
- export function loadAgentManifest(agentDir: string): AgentManifest | null {
39
- const manifestPath = join(agentDir, 'AGENT.yaml');
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
- const content = readFileSync(manifestPath, 'utf-8');
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 agentDirs = readdirSync(toolAgentsDir, { withFileTypes: true })
74
- .filter((dirent) => dirent.isDirectory())
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 agentDir of agentDirs) {
78
- const manifest = loadAgentManifest(join(toolAgentsDir, agentDir));
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(agentDir: string, agentName: string): { success: boolean; message: string } {
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
- if (!existsSync(manifestPath)) {
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 manifest
183
- const manifest = YAML.parse(readFileSync(manifestPath, 'utf-8')) as AgentManifest;
184
-
185
- // Load content (AGENT.md)
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 agentDir = join(BUNDLED_TOOLS_DIR, toolName, 'agents', agentName);
238
- if (existsSync(agentDir) && existsSync(join(agentDir, 'AGENT.yaml'))) {
239
- return agentDir;
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 agentDir = findAgentPath(agentName);
252
- if (!agentDir) {
274
+ const agentPath = findAgentPath(agentName);
275
+ if (!agentPath) {
253
276
  return { success: false, message: `Agent '${agentName}' not found` };
254
277
  }
255
- return installAgentFromPath(agentDir, agentName);
278
+ return installAgentFromPath(agentPath, agentName);
256
279
  }
257
280
 
258
281
  /**
package/src/lib/config.ts CHANGED
@@ -2,7 +2,7 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
2
2
  import { homedir } from 'os';
3
3
  import { join } from 'path';
4
4
  import YAML from 'yaml';
5
- import { Platform, BuiltInOutput, type DroidConfig, type LegacyDroidConfig, type SkillOverrides, type AutoUpdateConfig } from './types.js';
5
+ import { Platform, BuiltInOutput, type DroidConfig, type LegacyDroidConfig, type SkillOverrides, type AutoUpdateConfig } from './types';
6
6
 
7
7
  const CONFIG_DIR = join(homedir(), '.droid');
8
8
  const CONFIG_FILE = join(CONFIG_DIR, 'config.yaml');
@@ -1,6 +1,6 @@
1
1
  import { join } from 'path';
2
2
  import { homedir } from 'os';
3
- import { Platform } from './types.js';
3
+ import { Platform } from './types';
4
4
 
5
5
  /**
6
6
  * Platform-specific paths configuration
@@ -1,7 +1,7 @@
1
1
  import chalk from 'chalk';
2
2
  import inquirer from 'inquirer';
3
- import { saveSkillOverrides, loadConfig, loadSkillOverrides } from './config.js';
4
- import { ConfigOptionType, type SkillOverrides, type ConfigOption } from './types.js';
3
+ import { saveSkillOverrides, loadConfig, loadSkillOverrides } from './config';
4
+ import { ConfigOptionType, type SkillOverrides, type ConfigOption } from './types';
5
5
 
6
6
  /**
7
7
  * Prompt user to configure a skill after install
@@ -4,14 +4,15 @@ import { join } from 'path';
4
4
  import { tmpdir } from 'os';
5
5
  import { homedir } from 'os';
6
6
  import YAML from 'yaml';
7
- import { Platform, SkillStatus } from './types.js';
7
+ import { Platform, SkillStatus } from './types';
8
8
  import {
9
9
  getSkillsInstallPath,
10
10
  getCommandsInstallPath,
11
11
  getPlatformConfigPath,
12
12
  getSkillStatusDisplay,
13
13
  getBundledSkillsDir,
14
- } from './skills.js';
14
+ loadSkillManifest,
15
+ } from './skills';
15
16
 
16
17
  /**
17
18
  * Parse YAML frontmatter from markdown content
@@ -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 with valid frontmatter', () => {
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 { skillName, skillDir } of skillPaths) {
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 SKILL.yaml manifest', () => {
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
- const yamlPath = join(skillDir, 'SKILL.yaml');
208
- expect(existsSync(yamlPath)).toBe(true);
209
-
210
- const content = readFileSync(yamlPath, 'utf-8');
211
- const manifest = YAML.parse(content);
212
-
213
- expect(manifest.name).toBe(skillName);
214
- expect(typeof manifest.description).toBe('string');
215
- expect(typeof manifest.version).toBe('string');
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('SKILL.md frontmatter should match SKILL.yaml', () => {
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 mdPath = join(skillDir, 'SKILL.md');
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(frontmatter?.name).toBe(manifest.name);
234
- expect(frontmatter?.description).toBe(manifest.description);
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
@@ -2,10 +2,11 @@ import { existsSync, readdirSync, readFileSync, mkdirSync, writeFileSync, rmSync
2
2
  import { join, dirname } from 'path';
3
3
  import { fileURLToPath } from 'url';
4
4
  import YAML from 'yaml';
5
- import { loadConfig, saveConfig } from './config.js';
6
- import { Platform, SkillStatus, type SkillManifest, type InstalledSkill, getPlatformTools, setPlatformTools } from './types.js';
7
- import { getInstalledAgentsDir, installAgentFromPath, uninstallAgent, isAgentInstalled } from './agents.js';
8
- import { getSkillsPath, getCommandsPath, getConfigPath } from './platforms.js';
5
+ import { loadConfig, saveConfig } from './config';
6
+ import { Platform, SkillStatus, type SkillManifest, type InstalledSkill, getPlatformTools, setPlatformTools } from './types';
7
+ import { getInstalledAgentsDir, installAgentFromPath, uninstallAgent, isAgentInstalled } from './agents';
8
+ import { getSkillsPath, getCommandsPath, getConfigPath } from './platforms';
9
+ import { loadToolManifest } from './tools';
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 manifestPath = join(skillDir, 'SKILL.yaml');
113
+ const skillMdPath = join(skillDir, 'SKILL.md');
92
114
 
93
- if (!existsSync(manifestPath)) {
115
+ // Read SKILL.md and parse frontmatter
116
+ if (!existsSync(skillMdPath)) {
94
117
  return null;
95
118
  }
96
119
 
97
- try {
98
- const content = readFileSync(manifestPath, 'utf-8');
99
- return YAML.parse(content) as SkillManifest;
100
- } catch {
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
- if (existsSync(skillDir) && existsSync(join(skillDir, 'SKILL.yaml'))) {
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 (the actual skill file for Claude Code / OpenCode)
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/tools.ts CHANGED
@@ -2,9 +2,9 @@ import { existsSync, readdirSync, readFileSync } from 'fs';
2
2
  import { join, dirname } from 'path';
3
3
  import { fileURLToPath } from 'url';
4
4
  import YAML from 'yaml';
5
- import { loadConfig } from './config.js';
6
- import { type ToolManifest, type ToolIncludes, getPlatformTools } from './types.js';
7
- import { compareSemver } from './version.js';
5
+ import { loadConfig } from './config';
6
+ import { type ToolManifest, type ToolIncludes, getPlatformTools } from './types';
7
+ import { compareSemver } from './version';
8
8
 
9
9
  const __dirname = dirname(fileURLToPath(import.meta.url));
10
10
  const BUNDLED_TOOLS_DIR = join(__dirname, '../tools');
@@ -6,7 +6,7 @@ import {
6
6
  SkillStatus,
7
7
  ConfigOptionType,
8
8
  getAITag,
9
- } from './types.js';
9
+ } from './types';
10
10
 
11
11
  describe('Platform enum', () => {
12
12
  it('should have correct values', () => {
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 {
@@ -1,5 +1,5 @@
1
1
  import { describe, it, expect } from 'bun:test';
2
- import { getVersion, compareSemver } from './version.js';
2
+ import { getVersion, compareSemver } from './version';
3
3
 
4
4
  describe('getVersion', () => {
5
5
  it('should return a version string', () => {
@@ -1,55 +1,63 @@
1
- # Contributing Skills
1
+ # Contributing Tools
2
2
 
3
- Skills are reusable AI capabilities that can be installed into Claude Code or OpenCode.
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
- skills/
11
- └── my-skill/
12
- ├── SKILL.yaml # Required: Manifest with metadata
13
- ├── SKILL.md # Required: Instructions for the AI
14
- └── commands/ # Optional: Slash commands
15
- └── my-command.md
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
- ## SKILL.yaml (Manifest)
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-skill # Must match directory name
22
- description: Short description # Shown in TUI and listings
23
- version: 1.0.0 # Semver
24
- status: beta # alpha | beta | stable (optional)
25
- dependencies: [] # Other skills required (optional)
26
- provides_output: false # Can this skill be an output target?
27
-
28
- # Configuration schema (optional)
29
- config_schema:
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 # string | boolean
44
+ type: string
32
45
  description: What this option does
33
- default: "default value" # Optional default
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
- ## SKILL.md (AI Instructions)
49
+ ## Skills
43
50
 
44
- The SKILL.md file contains instructions for the AI. It must have YAML frontmatter:
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: Short description (must match SKILL.yaml)
56
+ description: "What this skill does"
50
57
  globs:
51
- - "**/*" # File patterns this skill applies to
52
- alwaysApply: false # Always include in context?
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
- ## Commands (Optional)
68
+ Skills can have a `references/` subdirectory for additional context files.
61
69
 
62
- Commands are slash commands the user can invoke. Each is a markdown file:
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
- The command file is just markdown instructions. The filename becomes the command name (`/do-thing`).
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 Skill
101
+ ## Testing Your Tool
72
102
 
73
- 1. Run `npm run build` to compile
74
- 2. Run `droid` to open the TUI
75
- 3. Navigate to Skills tab
76
- 4. Install your skill
77
- 5. Test in Claude Code with `/your-command` or by referencing the skill
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
- - [ ] `SKILL.yaml` has all required fields (name, description, version)
82
- - [ ] `SKILL.md` has valid YAML frontmatter
83
- - [ ] Name in frontmatter matches directory name
84
- - [ ] Description matches between SKILL.yaml and SKILL.md
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`