@mthanhlm/autodev 0.1.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 (35) hide show
  1. package/LICENSE +21 -0
  2. package/PUBLISH.md +75 -0
  3. package/README.md +53 -0
  4. package/autodev/bin/autodev-tools.cjs +346 -0
  5. package/autodev/templates/config.json +20 -0
  6. package/autodev/templates/plan.md +25 -0
  7. package/autodev/templates/project.md +21 -0
  8. package/autodev/templates/requirements.md +12 -0
  9. package/autodev/templates/roadmap.md +17 -0
  10. package/autodev/templates/state.md +10 -0
  11. package/autodev/templates/summary.md +16 -0
  12. package/autodev/templates/uat.md +15 -0
  13. package/autodev/workflows/execute-phase.md +50 -0
  14. package/autodev/workflows/help.md +57 -0
  15. package/autodev/workflows/new-project.md +62 -0
  16. package/autodev/workflows/plan-phase.md +54 -0
  17. package/autodev/workflows/progress.md +15 -0
  18. package/autodev/workflows/verify-work.md +39 -0
  19. package/bin/install.js +565 -0
  20. package/commands/autodev/execute-phase.md +26 -0
  21. package/commands/autodev/help.md +18 -0
  22. package/commands/autodev/new-project.md +28 -0
  23. package/commands/autodev/plan-phase.md +25 -0
  24. package/commands/autodev/progress.md +18 -0
  25. package/commands/autodev/verify-work.md +24 -0
  26. package/hooks/autodev-context-monitor.js +66 -0
  27. package/hooks/autodev-git-guard.js +55 -0
  28. package/hooks/autodev-phase-boundary.sh +20 -0
  29. package/hooks/autodev-prompt-guard.js +55 -0
  30. package/hooks/autodev-read-guard.js +49 -0
  31. package/hooks/autodev-session-state.sh +22 -0
  32. package/hooks/autodev-statusline.js +45 -0
  33. package/hooks/autodev-workflow-guard.js +51 -0
  34. package/package.json +38 -0
  35. package/scripts/run-tests.cjs +23 -0
package/bin/install.js ADDED
@@ -0,0 +1,565 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const os = require('os');
6
+ const readline = require('readline');
7
+
8
+ const green = '\x1b[32m';
9
+ const yellow = '\x1b[33m';
10
+ const cyan = '\x1b[36m';
11
+ const reset = '\x1b[0m';
12
+
13
+ const MANAGED_PREFIX = 'autodev-';
14
+ const HOOK_FILES = [
15
+ 'autodev-context-monitor.js',
16
+ 'autodev-git-guard.js',
17
+ 'autodev-phase-boundary.sh',
18
+ 'autodev-prompt-guard.js',
19
+ 'autodev-read-guard.js',
20
+ 'autodev-session-state.sh',
21
+ 'autodev-statusline.js',
22
+ 'autodev-workflow-guard.js'
23
+ ];
24
+
25
+ function stripJsonComments(input) {
26
+ let output = '';
27
+ let inString = false;
28
+ let stringChar = '';
29
+ let escaped = false;
30
+ let inLineComment = false;
31
+ let inBlockComment = false;
32
+
33
+ for (let index = 0; index < input.length; index += 1) {
34
+ const char = input[index];
35
+ const next = input[index + 1];
36
+
37
+ if (inLineComment) {
38
+ if (char === '\n') {
39
+ inLineComment = false;
40
+ output += char;
41
+ }
42
+ continue;
43
+ }
44
+
45
+ if (inBlockComment) {
46
+ if (char === '*' && next === '/') {
47
+ inBlockComment = false;
48
+ index += 1;
49
+ }
50
+ continue;
51
+ }
52
+
53
+ if (inString) {
54
+ output += char;
55
+ if (escaped) {
56
+ escaped = false;
57
+ } else if (char === '\\') {
58
+ escaped = true;
59
+ } else if (char === stringChar) {
60
+ inString = false;
61
+ }
62
+ continue;
63
+ }
64
+
65
+ if ((char === '"' || char === "'") && !inString) {
66
+ inString = true;
67
+ stringChar = char;
68
+ output += char;
69
+ continue;
70
+ }
71
+
72
+ if (char === '/' && next === '/') {
73
+ inLineComment = true;
74
+ index += 1;
75
+ continue;
76
+ }
77
+
78
+ if (char === '/' && next === '*') {
79
+ inBlockComment = true;
80
+ index += 1;
81
+ continue;
82
+ }
83
+
84
+ output += char;
85
+ }
86
+
87
+ return output;
88
+ }
89
+
90
+ function readSettings(settingsPath) {
91
+ if (!fs.existsSync(settingsPath)) {
92
+ return {};
93
+ }
94
+
95
+ try {
96
+ const raw = fs.readFileSync(settingsPath, 'utf8');
97
+ try {
98
+ return JSON.parse(raw);
99
+ } catch {
100
+ return JSON.parse(stripJsonComments(raw));
101
+ }
102
+ } catch (error) {
103
+ console.warn(` ${yellow}Warning:${reset} could not parse ${settingsPath}: ${error.message}`);
104
+ return null;
105
+ }
106
+ }
107
+
108
+ function writeSettings(settingsPath, settings) {
109
+ fs.mkdirSync(path.dirname(settingsPath), { recursive: true });
110
+ fs.writeFileSync(settingsPath, `${JSON.stringify(settings, null, 2)}\n`, 'utf8');
111
+ }
112
+
113
+ function extractFrontmatterAndBody(content) {
114
+ if (!content.startsWith('---\n')) {
115
+ return { frontmatter: null, body: content };
116
+ }
117
+
118
+ const endIndex = content.indexOf('\n---\n', 4);
119
+ if (endIndex === -1) {
120
+ return { frontmatter: null, body: content };
121
+ }
122
+
123
+ return {
124
+ frontmatter: content.slice(4, endIndex),
125
+ body: content.slice(endIndex + 5).trim()
126
+ };
127
+ }
128
+
129
+ function extractFrontmatterField(frontmatter, name) {
130
+ const match = frontmatter.match(new RegExp(`^${name}:\\s*(.+)$`, 'm'));
131
+ return match ? match[1].trim() : null;
132
+ }
133
+
134
+ function yamlQuote(value) {
135
+ return JSON.stringify(String(value));
136
+ }
137
+
138
+ function convertCommandToClaudeSkill(content, skillName) {
139
+ const { frontmatter, body } = extractFrontmatterAndBody(content);
140
+ if (!frontmatter) {
141
+ return content;
142
+ }
143
+
144
+ const description = extractFrontmatterField(frontmatter, 'description') || '';
145
+ const argumentHint = extractFrontmatterField(frontmatter, 'argument-hint');
146
+ const toolsMatch = frontmatter.match(/^allowed-tools:\s*\n((?:\s+-\s+.+\n?)*)/m);
147
+ const toolsBlock = toolsMatch ? `allowed-tools:\n${toolsMatch[1].endsWith('\n') ? toolsMatch[1] : `${toolsMatch[1]}\n`}` : '';
148
+
149
+ let rebuilt = `---\nname: ${skillName}\ndescription: ${yamlQuote(description)}\n`;
150
+ if (argumentHint) {
151
+ rebuilt += `argument-hint: ${yamlQuote(argumentHint)}\n`;
152
+ }
153
+ if (toolsBlock) {
154
+ rebuilt += toolsBlock;
155
+ }
156
+ rebuilt += '---';
157
+ return `${rebuilt}\n${body}\n`;
158
+ }
159
+
160
+ function transformInstalledContent(content, pathPrefix) {
161
+ const normalized = pathPrefix.endsWith('/') ? pathPrefix : `${pathPrefix}/`;
162
+ const bare = normalized.replace(/\/$/, '');
163
+ return content
164
+ .replace(/~\/\.claude\//g, normalized)
165
+ .replace(/\$HOME\/\.claude\//g, normalized)
166
+ .replace(/~\/\.claude\b/g, bare)
167
+ .replace(/\$HOME\/\.claude\b/g, bare)
168
+ .replace(/\.\/\.claude\//g, normalized)
169
+ .replace(/\.\/\.claude\b/g, bare);
170
+ }
171
+
172
+ function copyTextTree(srcDir, destDir, transform) {
173
+ if (!fs.existsSync(srcDir)) {
174
+ return;
175
+ }
176
+
177
+ fs.mkdirSync(destDir, { recursive: true });
178
+ for (const entry of fs.readdirSync(srcDir, { withFileTypes: true })) {
179
+ const srcPath = path.join(srcDir, entry.name);
180
+ const destPath = path.join(destDir, entry.name);
181
+ if (entry.isDirectory()) {
182
+ copyTextTree(srcPath, destPath, transform);
183
+ continue;
184
+ }
185
+
186
+ let content = fs.readFileSync(srcPath, 'utf8');
187
+ if (transform) {
188
+ content = transform(content);
189
+ }
190
+ fs.writeFileSync(destPath, content, 'utf8');
191
+ }
192
+ }
193
+
194
+ function copyCommandsAsLocal(srcDir, destDir, transform) {
195
+ if (fs.existsSync(destDir)) {
196
+ fs.rmSync(destDir, { recursive: true, force: true });
197
+ }
198
+ fs.mkdirSync(destDir, { recursive: true });
199
+
200
+ for (const entry of fs.readdirSync(srcDir, { withFileTypes: true })) {
201
+ const srcPath = path.join(srcDir, entry.name);
202
+ const destPath = path.join(destDir, entry.name);
203
+ if (entry.isDirectory()) {
204
+ copyCommandsAsLocal(srcPath, destPath, transform);
205
+ continue;
206
+ }
207
+ let content = fs.readFileSync(srcPath, 'utf8');
208
+ content = transform(content);
209
+ fs.writeFileSync(destPath, content, 'utf8');
210
+ }
211
+ }
212
+
213
+ function copyCommandsAsGlobalSkills(srcDir, skillsDir, prefix, transform) {
214
+ fs.mkdirSync(skillsDir, { recursive: true });
215
+
216
+ for (const entry of fs.readdirSync(skillsDir, { withFileTypes: true })) {
217
+ if (entry.isDirectory() && entry.name.startsWith(`${prefix}-`)) {
218
+ fs.rmSync(path.join(skillsDir, entry.name), { recursive: true, force: true });
219
+ }
220
+ }
221
+
222
+ for (const entry of fs.readdirSync(srcDir, { withFileTypes: true })) {
223
+ if (!entry.isFile() || !entry.name.endsWith('.md')) {
224
+ continue;
225
+ }
226
+
227
+ const baseName = entry.name.replace(/\.md$/, '');
228
+ const skillName = `${prefix}-${baseName}`;
229
+ const skillDir = path.join(skillsDir, skillName);
230
+ fs.mkdirSync(skillDir, { recursive: true });
231
+
232
+ let content = fs.readFileSync(path.join(srcDir, entry.name), 'utf8');
233
+ content = transform(content);
234
+ content = convertCommandToClaudeSkill(content, skillName);
235
+ fs.writeFileSync(path.join(skillDir, 'SKILL.md'), content, 'utf8');
236
+ }
237
+ }
238
+
239
+ function removeManagedSettings(settings) {
240
+ if (settings.statusLine?.command && settings.statusLine.command.includes('autodev-statusline')) {
241
+ delete settings.statusLine;
242
+ }
243
+
244
+ if (!settings.hooks || typeof settings.hooks !== 'object') {
245
+ return settings;
246
+ }
247
+
248
+ for (const eventName of Object.keys(settings.hooks)) {
249
+ const entries = Array.isArray(settings.hooks[eventName]) ? settings.hooks[eventName] : [];
250
+ const filtered = entries.filter(entry => {
251
+ const hooks = Array.isArray(entry?.hooks) ? entry.hooks : [];
252
+ return !hooks.some(hook => typeof hook.command === 'string' && hook.command.includes('autodev-'));
253
+ });
254
+
255
+ if (filtered.length > 0) {
256
+ settings.hooks[eventName] = filtered;
257
+ } else {
258
+ delete settings.hooks[eventName];
259
+ }
260
+ }
261
+
262
+ if (Object.keys(settings.hooks).length === 0) {
263
+ delete settings.hooks;
264
+ }
265
+
266
+ return settings;
267
+ }
268
+
269
+ function ensureHook(settings, eventName, matcher, command, timeout) {
270
+ if (!settings.hooks) {
271
+ settings.hooks = {};
272
+ }
273
+ if (!settings.hooks[eventName]) {
274
+ settings.hooks[eventName] = [];
275
+ }
276
+
277
+ const exists = settings.hooks[eventName].some(entry =>
278
+ Array.isArray(entry.hooks) && entry.hooks.some(hook => hook.command === command)
279
+ );
280
+ if (exists) {
281
+ return;
282
+ }
283
+
284
+ const hookEntry = { type: 'command', command };
285
+ if (timeout) {
286
+ hookEntry.timeout = timeout;
287
+ }
288
+
289
+ const entry = { hooks: [hookEntry] };
290
+ if (matcher) {
291
+ entry.matcher = matcher;
292
+ }
293
+
294
+ settings.hooks[eventName].push(entry);
295
+ }
296
+
297
+ function setExecutableBits(targetDir) {
298
+ const candidates = [
299
+ path.join(targetDir, 'hooks', 'autodev-phase-boundary.sh'),
300
+ path.join(targetDir, 'hooks', 'autodev-session-state.sh'),
301
+ path.join(targetDir, 'autodev', 'bin', 'autodev-tools.cjs')
302
+ ];
303
+
304
+ for (const file of candidates) {
305
+ if (fs.existsSync(file)) {
306
+ fs.chmodSync(file, 0o755);
307
+ }
308
+ }
309
+ }
310
+
311
+ function buildGlobalCommand(targetDir, fileName, shell = 'node') {
312
+ const filePath = path.join(targetDir, 'hooks', fileName).replace(/\\/g, '/');
313
+ return `${shell} "${filePath}"`;
314
+ }
315
+
316
+ function buildLocalCommand(fileName, shell = 'node') {
317
+ return `${shell} "$CLAUDE_PROJECT_DIR"/.claude/hooks/${fileName}`;
318
+ }
319
+
320
+ function configureSettings(targetDir, isGlobal, options = {}) {
321
+ const settingsPath = path.join(targetDir, 'settings.json');
322
+ const settings = readSettings(settingsPath);
323
+ if (settings === null) {
324
+ return null;
325
+ }
326
+
327
+ removeManagedSettings(settings);
328
+
329
+ const preToolEvent = 'PreToolUse';
330
+ const postToolEvent = 'PostToolUse';
331
+ const sessionStartEvent = 'SessionStart';
332
+
333
+ const contextMonitorCommand = isGlobal
334
+ ? buildGlobalCommand(targetDir, 'autodev-context-monitor.js')
335
+ : buildLocalCommand('autodev-context-monitor.js');
336
+ const promptGuardCommand = isGlobal
337
+ ? buildGlobalCommand(targetDir, 'autodev-prompt-guard.js')
338
+ : buildLocalCommand('autodev-prompt-guard.js');
339
+ const readGuardCommand = isGlobal
340
+ ? buildGlobalCommand(targetDir, 'autodev-read-guard.js')
341
+ : buildLocalCommand('autodev-read-guard.js');
342
+ const workflowGuardCommand = isGlobal
343
+ ? buildGlobalCommand(targetDir, 'autodev-workflow-guard.js')
344
+ : buildLocalCommand('autodev-workflow-guard.js');
345
+ const gitGuardCommand = isGlobal
346
+ ? buildGlobalCommand(targetDir, 'autodev-git-guard.js')
347
+ : buildLocalCommand('autodev-git-guard.js');
348
+ const sessionStateCommand = isGlobal
349
+ ? buildGlobalCommand(targetDir, 'autodev-session-state.sh', 'bash')
350
+ : buildLocalCommand('autodev-session-state.sh', 'bash');
351
+ const phaseBoundaryCommand = isGlobal
352
+ ? buildGlobalCommand(targetDir, 'autodev-phase-boundary.sh', 'bash')
353
+ : buildLocalCommand('autodev-phase-boundary.sh', 'bash');
354
+ const statusLineCommand = isGlobal
355
+ ? buildGlobalCommand(targetDir, 'autodev-statusline.js')
356
+ : buildLocalCommand('autodev-statusline.js');
357
+
358
+ ensureHook(settings, sessionStartEvent, null, sessionStateCommand);
359
+ ensureHook(settings, preToolEvent, 'Write|Edit', promptGuardCommand, 5);
360
+ ensureHook(settings, preToolEvent, 'Write|Edit', readGuardCommand, 5);
361
+ ensureHook(settings, preToolEvent, 'Write|Edit', workflowGuardCommand, 5);
362
+ ensureHook(settings, preToolEvent, 'Bash', gitGuardCommand, 5);
363
+ ensureHook(settings, postToolEvent, 'Bash|Edit|Write|MultiEdit|Agent|Task', contextMonitorCommand, 10);
364
+ ensureHook(settings, postToolEvent, 'Write|Edit', phaseBoundaryCommand, 5);
365
+
366
+ if (options.installStatusLine !== false) {
367
+ settings.statusLine = {
368
+ type: 'command',
369
+ command: statusLineCommand
370
+ };
371
+ }
372
+
373
+ writeSettings(settingsPath, settings);
374
+ return settingsPath;
375
+ }
376
+
377
+ function removeInstalledFiles(targetDir, isGlobal) {
378
+ const supportDir = path.join(targetDir, 'autodev');
379
+ if (fs.existsSync(supportDir)) {
380
+ fs.rmSync(supportDir, { recursive: true, force: true });
381
+ }
382
+
383
+ const hooksDir = path.join(targetDir, 'hooks');
384
+ for (const hookName of HOOK_FILES) {
385
+ const hookPath = path.join(hooksDir, hookName);
386
+ if (fs.existsSync(hookPath)) {
387
+ fs.rmSync(hookPath, { force: true });
388
+ }
389
+ }
390
+
391
+ if (isGlobal) {
392
+ const skillsDir = path.join(targetDir, 'skills');
393
+ if (fs.existsSync(skillsDir)) {
394
+ for (const entry of fs.readdirSync(skillsDir, { withFileTypes: true })) {
395
+ if (entry.isDirectory() && entry.name.startsWith(MANAGED_PREFIX)) {
396
+ fs.rmSync(path.join(skillsDir, entry.name), { recursive: true, force: true });
397
+ }
398
+ }
399
+ }
400
+ } else {
401
+ const commandsDir = path.join(targetDir, 'commands', 'autodev');
402
+ if (fs.existsSync(commandsDir)) {
403
+ fs.rmSync(commandsDir, { recursive: true, force: true });
404
+ }
405
+ const skillsDir = path.join(targetDir, 'skills');
406
+ if (fs.existsSync(skillsDir)) {
407
+ for (const entry of fs.readdirSync(skillsDir, { withFileTypes: true })) {
408
+ if (entry.isDirectory() && entry.name.startsWith(MANAGED_PREFIX)) {
409
+ fs.rmSync(path.join(skillsDir, entry.name), { recursive: true, force: true });
410
+ }
411
+ }
412
+ }
413
+ }
414
+ }
415
+
416
+ function resolveTargetDir({ scope, cwd, targetDir }) {
417
+ if (targetDir) {
418
+ return path.resolve(targetDir);
419
+ }
420
+
421
+ if (scope === 'global') {
422
+ return path.resolve(process.env.CLAUDE_CONFIG_DIR || path.join(os.homedir(), '.claude'));
423
+ }
424
+
425
+ return path.join(path.resolve(cwd), '.claude');
426
+ }
427
+
428
+ function install(options = {}) {
429
+ const scope = options.scope === 'local' ? 'local' : 'global';
430
+ const cwd = options.cwd || process.cwd();
431
+ const targetDir = resolveTargetDir({ scope, cwd, targetDir: options.targetDir });
432
+ const isGlobal = scope === 'global';
433
+ const silent = Boolean(options.silent);
434
+ const srcRoot = path.resolve(__dirname, '..');
435
+ const pathPrefix = isGlobal
436
+ ? `${targetDir.replace(/\\/g, '/')}/`
437
+ : './.claude/';
438
+ const transform = content => transformInstalledContent(content, pathPrefix);
439
+
440
+ fs.mkdirSync(targetDir, { recursive: true });
441
+ removeInstalledFiles(targetDir, isGlobal);
442
+
443
+ if (isGlobal) {
444
+ copyCommandsAsGlobalSkills(
445
+ path.join(srcRoot, 'commands', 'autodev'),
446
+ path.join(targetDir, 'skills'),
447
+ 'autodev',
448
+ transform
449
+ );
450
+ } else {
451
+ copyCommandsAsLocal(
452
+ path.join(srcRoot, 'commands', 'autodev'),
453
+ path.join(targetDir, 'commands', 'autodev'),
454
+ transform
455
+ );
456
+ }
457
+
458
+ copyTextTree(path.join(srcRoot, 'autodev'), path.join(targetDir, 'autodev'), transform);
459
+ copyTextTree(path.join(srcRoot, 'hooks'), path.join(targetDir, 'hooks'), transform);
460
+ setExecutableBits(targetDir);
461
+ const settingsPath = configureSettings(targetDir, isGlobal, options);
462
+
463
+ if (!silent) {
464
+ const locationLabel = isGlobal ? 'global' : 'local';
465
+ console.log(` ${green}Installed${reset} autodev (${locationLabel}) at ${cyan}${targetDir}${reset}`);
466
+ if (settingsPath) {
467
+ console.log(` ${green}Updated${reset} ${cyan}${settingsPath}${reset}`);
468
+ }
469
+ console.log(` Run ${cyan}/autodev-help${reset} in Claude Code.`);
470
+ }
471
+
472
+ return { targetDir, settingsPath };
473
+ }
474
+
475
+ function uninstall(options = {}) {
476
+ const scope = options.scope === 'local' ? 'local' : 'global';
477
+ const cwd = options.cwd || process.cwd();
478
+ const targetDir = resolveTargetDir({ scope, cwd, targetDir: options.targetDir });
479
+ const isGlobal = scope === 'global';
480
+ const silent = Boolean(options.silent);
481
+
482
+ removeInstalledFiles(targetDir, isGlobal);
483
+
484
+ const settingsPath = path.join(targetDir, 'settings.json');
485
+ const settings = readSettings(settingsPath);
486
+ if (settings) {
487
+ removeManagedSettings(settings);
488
+ writeSettings(settingsPath, settings);
489
+ }
490
+
491
+ if (!silent) {
492
+ console.log(` ${green}Removed${reset} autodev from ${cyan}${targetDir}${reset}`);
493
+ }
494
+
495
+ return { targetDir, settingsPath };
496
+ }
497
+
498
+ function printHelp() {
499
+ console.log(`Usage: npx @mthanhlm/autodev [--global|--local] [--uninstall]
500
+
501
+ Options:
502
+ --global Install to Claude Code config directory
503
+ --local Install to the current project (.claude/)
504
+ --uninstall Remove autodev from the selected location
505
+ --help Show this help
506
+
507
+ Examples:
508
+ npx @mthanhlm/autodev@latest --global
509
+ npx @mthanhlm/autodev@latest --local
510
+ npx @mthanhlm/autodev@latest --global --uninstall
511
+ `);
512
+ }
513
+
514
+ function askScope() {
515
+ return new Promise(resolve => {
516
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
517
+ rl.question('Install autodev globally or locally? [g/l] ', answer => {
518
+ rl.close();
519
+ resolve(answer.trim().toLowerCase().startsWith('l') ? 'local' : 'global');
520
+ });
521
+ });
522
+ }
523
+
524
+ async function main() {
525
+ const args = process.argv.slice(2);
526
+ if (args.includes('--help') || args.includes('-h')) {
527
+ printHelp();
528
+ return;
529
+ }
530
+
531
+ let scope = null;
532
+ if (args.includes('--global') || args.includes('-g')) {
533
+ scope = 'global';
534
+ } else if (args.includes('--local') || args.includes('-l')) {
535
+ scope = 'local';
536
+ }
537
+
538
+ if (!scope) {
539
+ scope = await askScope();
540
+ }
541
+
542
+ if (args.includes('--uninstall') || args.includes('-u')) {
543
+ uninstall({ scope });
544
+ } else {
545
+ install({ scope });
546
+ }
547
+ }
548
+
549
+ if (require.main === module) {
550
+ main().catch(error => {
551
+ console.error(error);
552
+ process.exit(1);
553
+ });
554
+ }
555
+
556
+ module.exports = {
557
+ configureSettings,
558
+ convertCommandToClaudeSkill,
559
+ install,
560
+ readSettings,
561
+ removeManagedSettings,
562
+ stripJsonComments,
563
+ transformInstalledContent,
564
+ uninstall
565
+ };
@@ -0,0 +1,26 @@
1
+ ---
2
+ name: autodev:execute-phase
3
+ description: Execute a phase plan sequentially without git writes
4
+ argument-hint: "[phase-number]"
5
+ allowed-tools:
6
+ - Read
7
+ - Write
8
+ - Edit
9
+ - Bash
10
+ - Grep
11
+ - Glob
12
+ - TodoWrite
13
+ - AskUserQuestion
14
+ ---
15
+ <objective>
16
+ Execute one phase plan from `.autodev/phases/` and record the result in a summary.
17
+ </objective>
18
+
19
+ <execution_context>
20
+ @~/.claude/autodev/workflows/execute-phase.md
21
+ @~/.claude/autodev/templates/summary.md
22
+ </execution_context>
23
+
24
+ <process>
25
+ Execute the workflow in @~/.claude/autodev/workflows/execute-phase.md end-to-end.
26
+ </process>
@@ -0,0 +1,18 @@
1
+ ---
2
+ name: autodev:help
3
+ description: Show the compact autodev command reference
4
+ allowed-tools:
5
+ - Read
6
+ - Bash
7
+ ---
8
+ <objective>
9
+ Show the autodev command reference and the default workflow.
10
+ </objective>
11
+
12
+ <execution_context>
13
+ @~/.claude/autodev/workflows/help.md
14
+ </execution_context>
15
+
16
+ <process>
17
+ Output the reference from @~/.claude/autodev/workflows/help.md directly.
18
+ </process>
@@ -0,0 +1,28 @@
1
+ ---
2
+ name: autodev:new-project
3
+ description: Initialize a new autodev project with minimal ceremony
4
+ argument-hint: "[idea or goals]"
5
+ allowed-tools:
6
+ - Read
7
+ - Write
8
+ - Bash
9
+ - AskUserQuestion
10
+ - Grep
11
+ - Glob
12
+ ---
13
+ <objective>
14
+ Create the `.autodev/` project state for a new or existing codebase without over-planning.
15
+ </objective>
16
+
17
+ <execution_context>
18
+ @~/.claude/autodev/workflows/new-project.md
19
+ @~/.claude/autodev/templates/config.json
20
+ @~/.claude/autodev/templates/project.md
21
+ @~/.claude/autodev/templates/requirements.md
22
+ @~/.claude/autodev/templates/roadmap.md
23
+ @~/.claude/autodev/templates/state.md
24
+ </execution_context>
25
+
26
+ <process>
27
+ Execute the workflow in @~/.claude/autodev/workflows/new-project.md end-to-end.
28
+ </process>
@@ -0,0 +1,25 @@
1
+ ---
2
+ name: autodev:plan-phase
3
+ description: Create a practical plan for one roadmap phase
4
+ argument-hint: "[phase-number]"
5
+ allowed-tools:
6
+ - Read
7
+ - Write
8
+ - Bash
9
+ - AskUserQuestion
10
+ - Grep
11
+ - Glob
12
+ - WebFetch
13
+ ---
14
+ <objective>
15
+ Create or update a single executable phase plan under `.autodev/phases/`.
16
+ </objective>
17
+
18
+ <execution_context>
19
+ @~/.claude/autodev/workflows/plan-phase.md
20
+ @~/.claude/autodev/templates/plan.md
21
+ </execution_context>
22
+
23
+ <process>
24
+ Execute the workflow in @~/.claude/autodev/workflows/plan-phase.md end-to-end.
25
+ </process>
@@ -0,0 +1,18 @@
1
+ ---
2
+ name: autodev:progress
3
+ description: Show the current autodev status for the project
4
+ allowed-tools:
5
+ - Read
6
+ - Bash
7
+ ---
8
+ <objective>
9
+ Render the current `.autodev/` progress and next recommended command.
10
+ </objective>
11
+
12
+ <execution_context>
13
+ @~/.claude/autodev/workflows/progress.md
14
+ </execution_context>
15
+
16
+ <process>
17
+ Execute the workflow in @~/.claude/autodev/workflows/progress.md end-to-end.
18
+ </process>
@@ -0,0 +1,24 @@
1
+ ---
2
+ name: autodev:verify-work
3
+ description: Run lightweight user acceptance testing for a phase
4
+ argument-hint: "[phase-number]"
5
+ allowed-tools:
6
+ - Read
7
+ - Write
8
+ - Bash
9
+ - AskUserQuestion
10
+ - Grep
11
+ - Glob
12
+ ---
13
+ <objective>
14
+ Record manual verification for one phase and keep the next step clear.
15
+ </objective>
16
+
17
+ <execution_context>
18
+ @~/.claude/autodev/workflows/verify-work.md
19
+ @~/.claude/autodev/templates/uat.md
20
+ </execution_context>
21
+
22
+ <process>
23
+ Execute the workflow in @~/.claude/autodev/workflows/verify-work.md end-to-end.
24
+ </process>