@orderful/droid 0.48.0 → 0.50.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 (76) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +2 -1
  3. package/CHANGELOG.md +24 -0
  4. package/bun.lock +137 -3
  5. package/dist/bin/droid.js +355 -90
  6. package/dist/commands/pack.d.ts +5 -0
  7. package/dist/commands/pack.d.ts.map +1 -0
  8. package/dist/index.js +11 -0
  9. package/dist/lib/pack.d.ts +31 -0
  10. package/dist/lib/pack.d.ts.map +1 -0
  11. package/dist/lib/types.d.ts +17 -0
  12. package/dist/lib/types.d.ts.map +1 -1
  13. package/dist/tools/brain/TOOL.yaml +2 -0
  14. package/dist/tools/coach/TOOL.yaml +4 -0
  15. package/dist/tools/code-review/.claude-plugin/plugin.json +3 -2
  16. package/dist/tools/code-review/TOOL.yaml +4 -1
  17. package/dist/tools/code-review/agents/codex-context-researcher.md +99 -0
  18. package/dist/tools/code-review/skills/code-review/SKILL.md +20 -1
  19. package/dist/tools/codex/.claude-plugin/plugin.json +1 -1
  20. package/dist/tools/codex/TOOL.yaml +3 -1
  21. package/dist/tools/codex/skills/codex/SKILL.md +5 -1
  22. package/dist/tools/codex/skills/codex/scripts/normalize-frontmatter.d.ts +61 -0
  23. package/dist/tools/codex/skills/codex/scripts/normalize-frontmatter.d.ts.map +1 -0
  24. package/dist/tools/codex/skills/codex/scripts/normalize-frontmatter.ts +402 -0
  25. package/dist/tools/comments/TOOL.yaml +2 -0
  26. package/dist/tools/droid/.claude-plugin/plugin.json +1 -1
  27. package/dist/tools/droid/TOOL.yaml +3 -1
  28. package/dist/tools/droid/skills/droid/SKILL.md +48 -2
  29. package/dist/tools/droid/skills/droid/references/new-tool-workflow.md +234 -0
  30. package/dist/tools/edi-schema/TOOL.yaml +2 -0
  31. package/dist/tools/excalidraw/TOOL.yaml +2 -0
  32. package/dist/tools/meeting/TOOL.yaml +2 -0
  33. package/dist/tools/pii/TOOL.yaml +2 -0
  34. package/dist/tools/plan/TOOL.yaml +4 -0
  35. package/dist/tools/project/TOOL.yaml +2 -0
  36. package/dist/tools/release/TOOL.yaml +2 -0
  37. package/dist/tools/share/TOOL.yaml +2 -0
  38. package/dist/tools/status-update/TOOL.yaml +4 -0
  39. package/dist/tools/tech-design/TOOL.yaml +2 -0
  40. package/dist/tools/wrapup/TOOL.yaml +2 -0
  41. package/package.json +3 -1
  42. package/scripts/build.ts +3 -2
  43. package/src/bin/droid.ts +9 -0
  44. package/src/commands/pack.ts +77 -0
  45. package/src/lib/pack.test.ts +85 -0
  46. package/src/lib/pack.ts +293 -0
  47. package/src/lib/types.ts +19 -0
  48. package/src/tools/brain/TOOL.yaml +2 -0
  49. package/src/tools/coach/TOOL.yaml +4 -0
  50. package/src/tools/code-review/.claude-plugin/plugin.json +3 -2
  51. package/src/tools/code-review/TOOL.yaml +4 -1
  52. package/src/tools/code-review/agents/codex-context-researcher.md +99 -0
  53. package/src/tools/code-review/skills/code-review/SKILL.md +20 -1
  54. package/src/tools/codex/.claude-plugin/plugin.json +1 -1
  55. package/src/tools/codex/TOOL.yaml +3 -1
  56. package/src/tools/codex/skills/codex/SKILL.md +5 -1
  57. package/src/tools/codex/skills/codex/scripts/normalize-frontmatter.test.ts +331 -0
  58. package/src/tools/codex/skills/codex/scripts/normalize-frontmatter.ts +402 -0
  59. package/src/tools/comments/TOOL.yaml +2 -0
  60. package/src/tools/droid/.claude-plugin/plugin.json +1 -1
  61. package/src/tools/droid/TOOL.yaml +3 -1
  62. package/src/tools/droid/skills/droid/SKILL.md +48 -2
  63. package/src/tools/droid/skills/droid/references/new-tool-workflow.md +234 -0
  64. package/src/tools/edi-schema/TOOL.yaml +2 -0
  65. package/src/tools/excalidraw/TOOL.yaml +2 -0
  66. package/src/tools/meeting/TOOL.yaml +2 -0
  67. package/src/tools/pii/TOOL.yaml +2 -0
  68. package/src/tools/plan/TOOL.yaml +4 -0
  69. package/src/tools/project/TOOL.yaml +2 -0
  70. package/src/tools/release/TOOL.yaml +2 -0
  71. package/src/tools/share/TOOL.yaml +2 -0
  72. package/src/tools/status-update/TOOL.yaml +4 -0
  73. package/src/tools/tech-design/TOOL.yaml +2 -0
  74. package/src/tools/wrapup/TOOL.yaml +2 -0
  75. package/dist/tools/codex/skills/codex/scripts/git-scripts.test.ts +0 -364
  76. package/dist/tools/pii/skills/pii/scripts/presidio.test.ts +0 -444
@@ -0,0 +1,402 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * codex normalize-frontmatter
4
+ *
5
+ * Normalizes YAML frontmatter format in codex markdown files to prevent
6
+ * unnecessary diffs from yaml.safe_dump() in the codex repo's fix-frontmatter
7
+ * workflow.
8
+ *
9
+ * Usage:
10
+ * droid config --get tools.codex | droid exec codex normalize-frontmatter --config -
11
+ *
12
+ * Options:
13
+ * --config <json> Config with codex_repo path (required)
14
+ * --all Process all .md files, not just uncommitted ones
15
+ *
16
+ * What it normalizes:
17
+ * - Flatten multiline scalars (>, |, >-, |-) into single-line quoted strings
18
+ * - Quote string values containing special characters (em dashes, colons, accents, @, arrows)
19
+ * - Consistent list indentation (- item, not - item under a key)
20
+ * - Unquote dates that are YYYY-MM-DD
21
+ *
22
+ * What it does NOT do:
23
+ * - Add missing required fields
24
+ * - Validate field values or enums
25
+ * - Reorder fields
26
+ *
27
+ * Output (JSON):
28
+ * { "success": true, "files_normalized": ["path/to/file.md"], "issues": [] }
29
+ */
30
+
31
+ import { execSync } from 'child_process';
32
+ import { readFileSync, writeFileSync, existsSync } from 'fs';
33
+
34
+ interface Config {
35
+ codex_repo: string;
36
+ }
37
+
38
+ interface Result {
39
+ success: boolean;
40
+ files_normalized: string[];
41
+ issues: string[];
42
+ error?: string;
43
+ }
44
+
45
+ function parseArgs(args: string[]): { config: Config | null; all: boolean } {
46
+ let config: Config | null = null;
47
+ let all = false;
48
+
49
+ for (let i = 0; i < args.length; i++) {
50
+ const arg = args[i];
51
+ if (arg === '--config' && args[i + 1]) {
52
+ const configArg = args[++i];
53
+ if (configArg === '-') {
54
+ const stdin = readFileSync(0, 'utf-8').trim();
55
+ config = JSON.parse(stdin) as Config;
56
+ } else {
57
+ config = JSON.parse(configArg) as Config;
58
+ }
59
+ } else if (arg === '--all') {
60
+ all = true;
61
+ }
62
+ }
63
+
64
+ return { config, all };
65
+ }
66
+
67
+ function expandPath(p: string): string {
68
+ if (p.startsWith('~/')) {
69
+ return p.replace('~', process.env.HOME || '');
70
+ }
71
+ return p;
72
+ }
73
+
74
+ /**
75
+ * Characters that require quoting in YAML values.
76
+ * Matches: em dash, en dash, accented chars, @, arrows, leading/trailing spaces,
77
+ * colons followed by space, # preceded by space.
78
+ */
79
+ const NEEDS_QUOTING = /[\u2013\u2014\u2018\u2019\u201c\u201d\u00e0-\u00ff\u0100-\u017f@\u2190-\u21ff\u2260]|:\s|^\s|\s$|\s#|^[{[]|[{}[\]],?$/;
80
+
81
+ /** Date pattern: YYYY-MM-DD */
82
+ const DATE_PATTERN = /^\d{4}-\d{2}-\d{2}$/;
83
+
84
+ /**
85
+ * Extract frontmatter block from markdown content.
86
+ * Returns the frontmatter lines (without delimiters) and the rest of the content.
87
+ */
88
+ export function extractFrontmatter(content: string): {
89
+ frontmatter: string[] | null;
90
+ body: string;
91
+ } {
92
+ const lines = content.split('\n');
93
+
94
+ if (lines[0] !== '---') {
95
+ return { frontmatter: null, body: content };
96
+ }
97
+
98
+ let endIndex = -1;
99
+ for (let i = 1; i < lines.length; i++) {
100
+ if (lines[i] === '---') {
101
+ endIndex = i;
102
+ break;
103
+ }
104
+ }
105
+
106
+ if (endIndex === -1) {
107
+ return { frontmatter: null, body: content };
108
+ }
109
+
110
+ return {
111
+ frontmatter: lines.slice(1, endIndex),
112
+ // Preserve everything after closing --- including the newline separator
113
+ body: '\n' + lines.slice(endIndex + 1).join('\n'),
114
+ };
115
+ }
116
+
117
+ /**
118
+ * Check if a string value needs quoting for YAML safety.
119
+ */
120
+ export function needsQuoting(value: string): boolean {
121
+ if (value === '' || value === 'true' || value === 'false' || value === 'null') return false;
122
+ if (DATE_PATTERN.test(value)) return false;
123
+ if (NEEDS_QUOTING.test(value)) return true;
124
+ // Values starting with special YAML chars
125
+ if (/^[*&!%>|'"]/.test(value)) return true;
126
+ // Values that look like numbers but shouldn't be
127
+ if (/^0\d/.test(value) && !/^0x/i.test(value)) return true;
128
+ return false;
129
+ }
130
+
131
+ /**
132
+ * Quote a YAML string value using double quotes, escaping internal quotes.
133
+ */
134
+ export function quoteValue(value: string): string {
135
+ // Already properly quoted
136
+ if ((value.startsWith('"') && value.endsWith('"')) ||
137
+ (value.startsWith("'") && value.endsWith("'"))) {
138
+ return value;
139
+ }
140
+ const escaped = value.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
141
+ return `"${escaped}"`;
142
+ }
143
+
144
+ /**
145
+ * Unquote a date value if it's YYYY-MM-DD wrapped in quotes.
146
+ */
147
+ export function unquoteDate(value: string): string {
148
+ const inner = value.replace(/^["'](.*)["']$/, '$1');
149
+ if (DATE_PATTERN.test(inner)) {
150
+ return inner;
151
+ }
152
+ return value;
153
+ }
154
+
155
+ /**
156
+ * Normalize frontmatter lines using a line-by-line state machine.
157
+ * No YAML library used — that's the whole point.
158
+ */
159
+ export function normalizeFrontmatter(lines: string[]): string[] {
160
+ const result: string[] = [];
161
+ let i = 0;
162
+
163
+ while (i < lines.length) {
164
+ const line = lines[i];
165
+
166
+ // Blank line — preserve
167
+ if (line.trim() === '') {
168
+ result.push(line);
169
+ i++;
170
+ continue;
171
+ }
172
+
173
+ // Comment line — preserve
174
+ if (line.trimStart().startsWith('#')) {
175
+ result.push(line);
176
+ i++;
177
+ continue;
178
+ }
179
+
180
+ // Key-value line: detect "key: value" or "key:" (for multiline/list)
181
+ const kvMatch = line.match(/^(\s*)([\w][\w.-]*)\s*:\s*(.*)$/);
182
+ if (!kvMatch) {
183
+ // List item at root or continuation — normalize indentation
184
+ const listMatch = line.match(/^(\s*)- (.*)$/);
185
+ if (listMatch) {
186
+ result.push(`${listMatch[1]}- ${normalizeScalarValue(listMatch[2])}`);
187
+ } else {
188
+ result.push(line);
189
+ }
190
+ i++;
191
+ continue;
192
+ }
193
+
194
+ const [, indent, key, rawValue] = kvMatch;
195
+
196
+ // Case 1: Multiline scalar indicator (>, |, >-, |-)
197
+ if (/^[>|][-+]?\s*$/.test(rawValue)) {
198
+ // Collect continuation lines
199
+ const continuationLines: string[] = [];
200
+ const baseIndentLen = indent.length + 2; // standard YAML continuation indent
201
+ i++;
202
+
203
+ while (i < lines.length) {
204
+ const nextLine = lines[i];
205
+ // Empty line within multiline block
206
+ if (nextLine.trim() === '') {
207
+ continuationLines.push('');
208
+ i++;
209
+ continue;
210
+ }
211
+ // Check if indented beyond the key (continuation)
212
+ const nextIndent = nextLine.match(/^(\s*)/)?.[1].length ?? 0;
213
+ if (nextIndent >= baseIndentLen) {
214
+ continuationLines.push(nextLine.trim());
215
+ i++;
216
+ } else {
217
+ break;
218
+ }
219
+ }
220
+
221
+ // Flatten into single-line value
222
+ const folded = rawValue.startsWith('|')
223
+ ? continuationLines.join(' ') // literal: join with space for single-line
224
+ : continuationLines.filter(l => l !== '').join(' ');
225
+
226
+ const trimmed = folded.trim();
227
+ if (needsQuoting(trimmed)) {
228
+ result.push(`${indent}${key}: ${quoteValue(trimmed)}`);
229
+ } else {
230
+ result.push(`${indent}${key}: ${trimmed}`);
231
+ }
232
+ continue;
233
+ }
234
+
235
+ // Case 2: Key with no value — next lines are a list or nested map
236
+ if (rawValue === '') {
237
+ result.push(`${indent}${key}:`);
238
+ i++;
239
+ continue;
240
+ }
241
+
242
+ // Case 3: Regular key: value
243
+ let value = rawValue.trim();
244
+
245
+ // Unquote dates
246
+ value = unquoteDate(value);
247
+
248
+ // Quote values that need it (but aren't already quoted)
249
+ if (needsQuoting(value) && !((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'")))) {
250
+ value = quoteValue(value);
251
+ }
252
+
253
+ result.push(`${indent}${key}: ${value}`);
254
+ i++;
255
+ }
256
+
257
+ return result;
258
+ }
259
+
260
+ /**
261
+ * Normalize a scalar value that appears in a list item or elsewhere.
262
+ */
263
+ function normalizeScalarValue(value: string): string {
264
+ let v = unquoteDate(value.trim());
265
+ if (needsQuoting(v) && !((v.startsWith('"') && v.endsWith('"')) || (v.startsWith("'") && v.endsWith("'")))) {
266
+ v = quoteValue(v);
267
+ }
268
+ return v;
269
+ }
270
+
271
+ /**
272
+ * Normalize the frontmatter in a markdown file.
273
+ * Returns the normalized content, or null if no changes needed.
274
+ */
275
+ export function normalizeFile(content: string): string | null {
276
+ const { frontmatter, body } = extractFrontmatter(content);
277
+
278
+ if (!frontmatter) {
279
+ return null; // No frontmatter to normalize
280
+ }
281
+
282
+ const normalized = normalizeFrontmatter(frontmatter);
283
+
284
+ // Check if anything changed
285
+ const originalBlock = frontmatter.join('\n');
286
+ const normalizedBlock = normalized.join('\n');
287
+
288
+ if (originalBlock === normalizedBlock) {
289
+ return null; // No changes needed
290
+ }
291
+
292
+ return `---\n${normalizedBlock}\n---${body}`;
293
+ }
294
+
295
+ /**
296
+ * Get list of markdown files with uncommitted changes.
297
+ */
298
+ function getChangedFiles(cwd: string): string[] {
299
+ try {
300
+ // Both staged and unstaged changes, plus untracked files
301
+ const output = execSync(
302
+ 'git diff --name-only HEAD 2>/dev/null; git diff --name-only --cached 2>/dev/null; git ls-files --others --exclude-standard 2>/dev/null',
303
+ { cwd, encoding: 'utf-8' }
304
+ );
305
+
306
+ const files = output
307
+ .split('\n')
308
+ .map(f => f.trim())
309
+ .filter(f => f.endsWith('.md'))
310
+ .filter(f => f !== '')
311
+ // Deduplicate
312
+ .filter((f, i, arr) => arr.indexOf(f) === i);
313
+
314
+ return files;
315
+ } catch {
316
+ return [];
317
+ }
318
+ }
319
+
320
+ /**
321
+ * Get all markdown files in the repo.
322
+ */
323
+ function getAllMarkdownFiles(cwd: string): string[] {
324
+ try {
325
+ const output = execSync(
326
+ 'git ls-files "*.md" 2>/dev/null',
327
+ { cwd, encoding: 'utf-8' }
328
+ );
329
+ return output.split('\n').map(f => f.trim()).filter(f => f !== '');
330
+ } catch {
331
+ return [];
332
+ }
333
+ }
334
+
335
+ // Main — only run when executed directly (not imported by tests)
336
+ export function main(): void {
337
+ const args = process.argv.slice(2);
338
+ const { config, all } = parseArgs(args);
339
+
340
+ if (!config) {
341
+ console.log(JSON.stringify({
342
+ success: false,
343
+ files_normalized: [],
344
+ issues: [],
345
+ error: 'Missing --config. Usage: droid config --get tools.codex | droid exec codex normalize-frontmatter --config -',
346
+ }));
347
+ process.exit(1);
348
+ }
349
+
350
+ if (!config.codex_repo) {
351
+ console.log(JSON.stringify({
352
+ success: false,
353
+ files_normalized: [],
354
+ issues: [],
355
+ error: 'Missing codex_repo in config',
356
+ }));
357
+ process.exit(1);
358
+ }
359
+
360
+ const cwd = expandPath(config.codex_repo);
361
+
362
+ if (!existsSync(cwd)) {
363
+ console.log(JSON.stringify({
364
+ success: false,
365
+ files_normalized: [],
366
+ issues: [],
367
+ error: `Codex repo not found at ${cwd}`,
368
+ }));
369
+ process.exit(1);
370
+ }
371
+
372
+ const files = all ? getAllMarkdownFiles(cwd) : getChangedFiles(cwd);
373
+ const result: Result = {
374
+ success: true,
375
+ files_normalized: [],
376
+ issues: [],
377
+ };
378
+
379
+ for (const file of files) {
380
+ const fullPath = `${cwd}/${file}`;
381
+ try {
382
+ if (!existsSync(fullPath)) continue;
383
+
384
+ const content = readFileSync(fullPath, 'utf-8');
385
+ const normalized = normalizeFile(content);
386
+
387
+ if (normalized !== null) {
388
+ writeFileSync(fullPath, normalized, 'utf-8');
389
+ result.files_normalized.push(file);
390
+ }
391
+ } catch (err: unknown) {
392
+ const error = err as { message?: string };
393
+ result.issues.push(`${file}: ${error.message || 'Unknown error'}`);
394
+ }
395
+ }
396
+
397
+ console.log(JSON.stringify(result, null, 2));
398
+ }
399
+
400
+ if (import.meta.main) {
401
+ main();
402
+ }
@@ -2,6 +2,8 @@ name: comments
2
2
  description: "Enable inline conversations using @droid/@user markers. Tag @droid to ask the AI, AI responds with @{your-name}. Use /comments check to address markers, /comments cleanup to remove resolved threads. Ideal for code review notes and async collaboration."
3
3
  version: 0.3.7
4
4
  status: beta
5
+ audience:
6
+ - all
5
7
 
6
8
  includes:
7
9
  skills:
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "droid",
3
- "version": "0.7.1",
3
+ "version": "0.7.2",
4
4
  "description": "Core droid meta-skill for update awareness, tool discovery, and usage help. Checks for updates, helps users find tools, and answers 'how do I...' questions about droid workflows.",
5
5
  "author": {
6
6
  "name": "Orderful",
@@ -1,7 +1,9 @@
1
1
  name: droid
2
2
  description: "Core droid meta-skill for update awareness, tool discovery, and usage help. Checks for updates, helps users find tools, and answers 'how do I...' questions about droid workflows."
3
- version: 0.7.1
3
+ version: 0.7.2
4
4
  status: beta
5
+ audience:
6
+ - engineering
5
7
 
6
8
  # System tool - always stays current regardless of auto-update settings
7
9
  system: true
@@ -1,8 +1,8 @@
1
1
  ---
2
2
  name: droid
3
- description: "Core droid meta-skill for updates, tool discovery, usage help, and skill overrides. Use when checking for droid updates, discovering available tools, answering 'how do I...' questions about droid workflows, or customizing skill behaviour. User prompts like 'any droid updates?', 'what tools do I have?', 'what tools do you have?', 'remind me what tools you have', 'hey droid what can you do', 'how do I add context?', 'remind me how to use codex', 'what's the best way to start a session?', 'customize brain search', 'create an override for brain'."
3
+ description: "Core droid meta-skill for updates, tool discovery, usage help, skill overrides, and skill review. Use when checking for droid updates, discovering available tools, answering 'how do I...' questions about droid workflows, customizing skill behaviour, or reviewing a contributed skill. User prompts like 'any droid updates?', 'what tools do I have?', 'what tools do you have?', 'remind me what tools you have', 'hey droid what can you do', 'how do I add context?', 'remind me how to use codex', 'what's the best way to start a session?', 'customize brain search', 'create an override for brain', 'review this skill', 'droid review ./my-skill'."
4
4
  alwaysApply: true
5
- allowed-tools: [Bash, Read, Write]
5
+ allowed-tools: [Bash, Read, Write, Glob, Grep]
6
6
  ---
7
7
 
8
8
  # Droid
@@ -69,6 +69,7 @@ cat ~/.droid/config.yaml
69
69
  | Review code | `/code-review` |
70
70
  | Capture a decision | `/codex decision {summary}` |
71
71
  | Update project context | `/project update` |
72
+ | Create a new droid tool | `/droid new {name}` or `/droid new {name} --from {path}` |
72
73
 
73
74
  ## Response Guidelines
74
75
 
@@ -89,6 +90,51 @@ When answering "how do I..." questions:
89
90
  2. `/codex search {topic}` - Find relevant org knowledge (if needed)
90
91
  3. Then start working with full context
91
92
 
93
+ ## New Tool
94
+
95
+ Create a new droid tool, optionally starting from an existing skill/agent file. The droid skill for contributing to droid.
96
+
97
+ **When:** User says `/droid new {tool-name}` or `/droid new {tool-name} --from {path}`
98
+
99
+ **Usage:**
100
+ ```
101
+ /droid new {tool-name} # scaffold from scratch
102
+ /droid new {tool-name} --from path/to/file # import with explicit path
103
+ /droid new {tool-name} path/to/file # same — path detected as import source
104
+ ```
105
+
106
+ Accepts `.skill`, `.zip`, `.md` files, or directories. Users can also just drop a file into the chat after running `/droid new {tool-name}` and it will be picked up as the import source.
107
+
108
+ **Flow:**
109
+
110
+ 1. **Import** (if `--from` provided) — Determine input type:
111
+ - `.skill` or `.zip` → extract to temp directory, locate SKILL.md or agent .md inside
112
+ - Directory → use directly
113
+ - Single `.md` file → determine if it's a skill (has frontmatter with `name`/`description`) or an agent
114
+ - No `--from` → scaffold blank SKILL.md from template
115
+ 2. **Transform** — Massage imported content to droid standards:
116
+ - Multiline YAML descriptions (`>`, `>-`, `|`) → rewrite as single-line quoted string
117
+ - Missing `allowed-tools` → analyse content for tool usage, add appropriate list
118
+ - Description missing "Use when" / "User prompts like" pattern → rewrite to match
119
+ - `name` not kebab-case → fix it
120
+ - Add missing frontmatter fields (`globs: []`, `alwaysApply: false`)
121
+ 3. **Security scan** — Flag issues. High-severity (prompt injection patterns, encoded content) blocks. Medium-severity (Bash without justification, external URLs) requires acknowledgement.
122
+ 4. **Collect metadata** — Always ask for `audience`. Confirm tool name and description.
123
+ 5. **Scaffold tool** — Create the full tool structure in `src/tools/{tool-name}/`:
124
+ - `TOOL.yaml` with name, description, version `0.1.0`, status `beta`, audience
125
+ - `skills/{skill-name}/SKILL.md` (+ `references/` if present)
126
+ - `agents/{agent-name}.md` if importing an agent
127
+ - Add changeset: `.changeset/{tool-name}-initial.md`
128
+ 6. **Open PR** — Create branch `contrib/{tool-name}`, commit, push, open PR.
129
+
130
+ Present a summary with droid eyes `[● ●]` showing what was imported/transformed and what needs input. See `references/new-tool-workflow.md` for detailed validation rules, security patterns, and PR template.
131
+
132
+ **Key rules:**
133
+ - Fix what can be fixed automatically (formatting, frontmatter, descriptions)
134
+ - Only block on things that need human judgement (security, audience)
135
+ - New tools start at version `0.1.0` with status `beta`
136
+ - Always create a changeset with package name `"@orderful/droid"`
137
+
92
138
  ## Skill Overrides
93
139
 
94
140
  Users can customize skill behaviour by creating override files in `~/.droid/skill_overrides/`.