aped-method 1.0.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/bin/aped-method.js +8 -0
- package/package.json +31 -0
- package/src/index.js +348 -0
- package/src/scaffold.js +21 -0
- package/src/templates/commands.js +65 -0
- package/src/templates/config.js +230 -0
- package/src/templates/data/domain-complexity.csv +15 -0
- package/src/templates/data/project-types.csv +11 -0
- package/src/templates/guardrail.js +171 -0
- package/src/templates/index.js +17 -0
- package/src/templates/references.js +549 -0
- package/src/templates/scripts.js +345 -0
- package/src/templates/skills.js +412 -0
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "aped-method",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Scaffold the APED pipeline (Analyze, PRD, Epics, Dev, Review) into any Claude Code project",
|
|
6
|
+
"bin": {
|
|
7
|
+
"aped-method": "./bin/aped-method.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"bin/",
|
|
11
|
+
"src/"
|
|
12
|
+
],
|
|
13
|
+
"keywords": [
|
|
14
|
+
"claude-code",
|
|
15
|
+
"skills",
|
|
16
|
+
"pipeline",
|
|
17
|
+
"tdd",
|
|
18
|
+
"code-review",
|
|
19
|
+
"product-development",
|
|
20
|
+
"aped"
|
|
21
|
+
],
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"repository": {
|
|
24
|
+
"type": "git",
|
|
25
|
+
"url": "https://github.com/yabafre/aped-claude.git"
|
|
26
|
+
},
|
|
27
|
+
"author": "yabafre",
|
|
28
|
+
"engines": {
|
|
29
|
+
"node": ">=18"
|
|
30
|
+
}
|
|
31
|
+
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
import { createInterface } from 'node:readline';
|
|
2
|
+
import { stdin, stdout } from 'node:process';
|
|
3
|
+
import { scaffold } from './scaffold.js';
|
|
4
|
+
|
|
5
|
+
const DEFAULTS = {
|
|
6
|
+
apedDir: '.aped',
|
|
7
|
+
outputDir: 'docs/aped',
|
|
8
|
+
commandsDir: '.claude/commands',
|
|
9
|
+
authorName: '',
|
|
10
|
+
projectName: '',
|
|
11
|
+
communicationLang: 'english',
|
|
12
|
+
documentLang: 'english',
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
// ── ANSI helpers ──
|
|
16
|
+
const ESC = '\x1b';
|
|
17
|
+
const a = {
|
|
18
|
+
reset: `${ESC}[0m`,
|
|
19
|
+
bold: `${ESC}[1m`,
|
|
20
|
+
dim: `${ESC}[2m`,
|
|
21
|
+
italic: `${ESC}[3m`,
|
|
22
|
+
underline: `${ESC}[4m`,
|
|
23
|
+
red: `${ESC}[31m`,
|
|
24
|
+
green: `${ESC}[32m`,
|
|
25
|
+
yellow: `${ESC}[33m`,
|
|
26
|
+
blue: `${ESC}[34m`,
|
|
27
|
+
magenta: `${ESC}[35m`,
|
|
28
|
+
cyan: `${ESC}[36m`,
|
|
29
|
+
white: `${ESC}[37m`,
|
|
30
|
+
// 256-color greens for richer palette
|
|
31
|
+
lime: `${ESC}[38;5;118m`, // bright lime
|
|
32
|
+
emerald: `${ESC}[38;5;42m`, // emerald
|
|
33
|
+
mint: `${ESC}[38;5;48m`, // mint
|
|
34
|
+
forest: `${ESC}[38;5;34m`, // forest
|
|
35
|
+
spring: `${ESC}[38;5;82m`, // spring green
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const bold = (s) => `${a.bold}${s}${a.reset}`;
|
|
39
|
+
const dim = (s) => `${a.dim}${s}${a.reset}`;
|
|
40
|
+
const green = (s) => `${a.green}${s}${a.reset}`;
|
|
41
|
+
const lime = (s) => `${a.lime}${s}${a.reset}`;
|
|
42
|
+
const emerald = (s) => `${a.emerald}${s}${a.reset}`;
|
|
43
|
+
const mint = (s) => `${a.mint}${s}${a.reset}`;
|
|
44
|
+
const yellow = (s) => `${a.yellow}${s}${a.reset}`;
|
|
45
|
+
const magenta = (s) => `${a.magenta}${s}${a.reset}`;
|
|
46
|
+
const red = (s) => `${a.red}${s}${a.reset}`;
|
|
47
|
+
const cyan = (s) => `${a.cyan}${s}${a.reset}`;
|
|
48
|
+
|
|
49
|
+
// ── ASCII Art Logo ──
|
|
50
|
+
const LOGO = `
|
|
51
|
+
${a.emerald}${a.bold} █████╗ ██████╗ ███████╗██████╗
|
|
52
|
+
██╔══██╗██╔══██╗██╔════╝██╔══██╗
|
|
53
|
+
███████║██████╔╝█████╗ ██║ ██║
|
|
54
|
+
██╔══██║██╔═══╝ ██╔══╝ ██║ ██║
|
|
55
|
+
██║ ██║██║ ███████╗██████╔╝
|
|
56
|
+
╚═╝ ╚═╝╚═╝ ╚══════╝╚═════╝${a.reset}
|
|
57
|
+
${a.dim} ─────────────────────────────────${a.reset}
|
|
58
|
+
${a.lime}${a.bold} M E T H O D${a.reset}
|
|
59
|
+
${a.dim} ─────────────────────────────────${a.reset}
|
|
60
|
+
`;
|
|
61
|
+
|
|
62
|
+
const PIPELINE = ` ${a.emerald}${a.bold}A${a.reset}${a.dim}nalyze${a.reset} ${a.dim}→${a.reset} ${a.mint}${a.bold}P${a.reset}${a.dim}RD${a.reset} ${a.dim}→${a.reset} ${a.yellow}${a.bold}E${a.reset}${a.dim}pics${a.reset} ${a.dim}→${a.reset} ${a.lime}${a.bold}D${a.reset}${a.dim}ev${a.reset} ${a.dim}→${a.reset} ${a.red}${a.bold}R${a.reset}${a.dim}eview${a.reset}`;
|
|
63
|
+
|
|
64
|
+
// ── Spinner ──
|
|
65
|
+
const SPINNER_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
66
|
+
|
|
67
|
+
function createSpinner(text) {
|
|
68
|
+
let i = 0;
|
|
69
|
+
let interval;
|
|
70
|
+
return {
|
|
71
|
+
start() {
|
|
72
|
+
process.stdout.write('\x1b[?25l'); // hide cursor
|
|
73
|
+
interval = setInterval(() => {
|
|
74
|
+
const frame = SPINNER_FRAMES[i % SPINNER_FRAMES.length];
|
|
75
|
+
process.stdout.write(`\r ${a.emerald}${frame}${a.reset} ${text}`);
|
|
76
|
+
i++;
|
|
77
|
+
}, 80);
|
|
78
|
+
},
|
|
79
|
+
stop(finalText) {
|
|
80
|
+
clearInterval(interval);
|
|
81
|
+
process.stdout.write(`\r ${a.lime}${a.bold}✓${a.reset} ${finalText}\x1b[K\n`);
|
|
82
|
+
process.stdout.write('\x1b[?25h'); // show cursor
|
|
83
|
+
},
|
|
84
|
+
fail(finalText) {
|
|
85
|
+
clearInterval(interval);
|
|
86
|
+
process.stdout.write(`\r ${a.red}${a.bold}✗${a.reset} ${finalText}\x1b[K\n`);
|
|
87
|
+
process.stdout.write('\x1b[?25h');
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function sleep(ms) {
|
|
93
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// ── Section display ──
|
|
97
|
+
function sectionHeader(title) {
|
|
98
|
+
console.log('');
|
|
99
|
+
console.log(` ${a.emerald}${a.bold}┌─${a.reset} ${a.bold}${title}${a.reset}`);
|
|
100
|
+
console.log(` ${a.emerald}│${a.reset}`);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function sectionEnd() {
|
|
104
|
+
console.log(` ${a.emerald}${a.bold}└──────────────────────────────────${a.reset}`);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// ── Args ──
|
|
108
|
+
function parseArgs(argv) {
|
|
109
|
+
const args = {};
|
|
110
|
+
for (let i = 2; i < argv.length; i++) {
|
|
111
|
+
const arg = argv[i];
|
|
112
|
+
if (arg === '--yes' || arg === '-y') { args.yes = true; continue; }
|
|
113
|
+
const match = arg.match(/^--(\w[\w-]*)=(.+)$/);
|
|
114
|
+
if (match) {
|
|
115
|
+
const key = match[1].replace(/-([a-z])/g, (_, ch) => ch.toUpperCase());
|
|
116
|
+
args[key] = match[2];
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return args;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function ask(rl, question, defaultVal) {
|
|
123
|
+
return new Promise((resolve) => {
|
|
124
|
+
const suffix = defaultVal ? ` ${dim(`(${defaultVal})`)}` : '';
|
|
125
|
+
rl.question(` ${a.emerald}│${a.reset} ${question}${suffix}: `, (answer) => {
|
|
126
|
+
resolve(answer.trim() || defaultVal || '');
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
async function readStdinLines() {
|
|
132
|
+
if (stdin.isTTY) return null;
|
|
133
|
+
return new Promise((resolve) => {
|
|
134
|
+
let data = '';
|
|
135
|
+
stdin.setEncoding('utf-8');
|
|
136
|
+
stdin.on('data', (chunk) => { data += chunk; });
|
|
137
|
+
stdin.on('end', () => resolve(data.split('\n')));
|
|
138
|
+
setTimeout(() => resolve(null), 100);
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// ── Main ──
|
|
143
|
+
export async function run() {
|
|
144
|
+
const args = parseArgs(process.argv);
|
|
145
|
+
const stdinLines = await readStdinLines();
|
|
146
|
+
let lineIndex = 0;
|
|
147
|
+
|
|
148
|
+
let detectedProject = '';
|
|
149
|
+
try {
|
|
150
|
+
const { readFileSync } = await import('node:fs');
|
|
151
|
+
const pkg = JSON.parse(readFileSync('package.json', 'utf-8'));
|
|
152
|
+
detectedProject = pkg.name || '';
|
|
153
|
+
} catch {
|
|
154
|
+
detectedProject = process.cwd().split('/').pop();
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// ── Banner ──
|
|
158
|
+
console.log(LOGO);
|
|
159
|
+
console.log(PIPELINE);
|
|
160
|
+
console.log('');
|
|
161
|
+
|
|
162
|
+
if (args.yes) {
|
|
163
|
+
const config = {
|
|
164
|
+
projectName: args.project || args.projectName || detectedProject || 'my-project',
|
|
165
|
+
authorName: args.author || args.authorName || '',
|
|
166
|
+
communicationLang: args.lang || args.communicationLang || DEFAULTS.communicationLang,
|
|
167
|
+
documentLang: args.docLang || args.documentLang || DEFAULTS.documentLang,
|
|
168
|
+
apedDir: args.aped || args.apedDir || DEFAULTS.apedDir,
|
|
169
|
+
outputDir: args.output || args.outputDir || DEFAULTS.outputDir,
|
|
170
|
+
commandsDir: args.commands || args.commandsDir || DEFAULTS.commandsDir,
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
await runScaffold(config);
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const rl = createInterface({ input: stdin, output: stdout });
|
|
178
|
+
|
|
179
|
+
const prompt = stdinLines
|
|
180
|
+
? (question, def) => {
|
|
181
|
+
const val = (stdinLines[lineIndex++] || '').trim();
|
|
182
|
+
const result = val || def || '';
|
|
183
|
+
console.log(` ${a.emerald}│${a.reset} ${question}: ${result}`);
|
|
184
|
+
return Promise.resolve(result);
|
|
185
|
+
}
|
|
186
|
+
: (question, def) => ask(rl, question, def);
|
|
187
|
+
|
|
188
|
+
try {
|
|
189
|
+
sectionHeader('Configuration');
|
|
190
|
+
|
|
191
|
+
const config = {};
|
|
192
|
+
config.projectName = await prompt(`${bold('Project name')}`, detectedProject);
|
|
193
|
+
config.authorName = await prompt(`${bold('Author')}`, DEFAULTS.authorName);
|
|
194
|
+
config.communicationLang = await prompt(`${bold('Communication language')}`, DEFAULTS.communicationLang);
|
|
195
|
+
config.documentLang = await prompt(`${bold('Document language')}`, DEFAULTS.documentLang);
|
|
196
|
+
config.apedDir = await prompt(`${bold('APED dir')} ${dim('(engine)')}`, DEFAULTS.apedDir);
|
|
197
|
+
config.outputDir = await prompt(`${bold('Output dir')} ${dim('(artifacts)')}`, DEFAULTS.outputDir);
|
|
198
|
+
config.commandsDir = await prompt(`${bold('Commands dir')}`, DEFAULTS.commandsDir);
|
|
199
|
+
|
|
200
|
+
sectionEnd();
|
|
201
|
+
console.log('');
|
|
202
|
+
printConfig(config);
|
|
203
|
+
console.log('');
|
|
204
|
+
|
|
205
|
+
const confirm = await prompt(`${bold('Proceed?')}`, 'Y');
|
|
206
|
+
if (confirm.toLowerCase() === 'n') {
|
|
207
|
+
console.log(`\n ${yellow('Cancelled.')}\n`);
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
await runScaffold(config);
|
|
212
|
+
} finally {
|
|
213
|
+
rl.close();
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
async function runScaffold(config) {
|
|
218
|
+
// ── Phase 1: Validating config ──
|
|
219
|
+
const s1 = createSpinner('Validating configuration...');
|
|
220
|
+
s1.start();
|
|
221
|
+
await sleep(400);
|
|
222
|
+
s1.stop('Configuration validated');
|
|
223
|
+
|
|
224
|
+
// ── Phase 2: Creating directory structure ──
|
|
225
|
+
const s2 = createSpinner('Creating directory structure...');
|
|
226
|
+
s2.start();
|
|
227
|
+
await sleep(300);
|
|
228
|
+
s2.stop('Directory structure ready');
|
|
229
|
+
|
|
230
|
+
// ── Phase 3: Scaffolding ──
|
|
231
|
+
sectionHeader('Scaffolding Pipeline');
|
|
232
|
+
console.log(` ${a.emerald}│${a.reset}`);
|
|
233
|
+
|
|
234
|
+
const count = await scaffoldWithCheckpoints(config);
|
|
235
|
+
|
|
236
|
+
sectionEnd();
|
|
237
|
+
|
|
238
|
+
// ── Phase 4: Setting up hooks ──
|
|
239
|
+
const s3 = createSpinner('Installing guardrail hook...');
|
|
240
|
+
s3.start();
|
|
241
|
+
await sleep(350);
|
|
242
|
+
s3.stop('Guardrail hook installed');
|
|
243
|
+
|
|
244
|
+
// ── Phase 5: Final verification ──
|
|
245
|
+
const s4 = createSpinner('Verifying installation...');
|
|
246
|
+
s4.start();
|
|
247
|
+
await sleep(300);
|
|
248
|
+
s4.stop(`Installation verified — ${bold(String(count))} files`);
|
|
249
|
+
|
|
250
|
+
// ── Done ──
|
|
251
|
+
printDone(count);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
async function scaffoldWithCheckpoints(config) {
|
|
255
|
+
const { getTemplates } = await import('./templates/index.js');
|
|
256
|
+
const { mkdirSync, writeFileSync, chmodSync, existsSync } = await import('node:fs');
|
|
257
|
+
const { join, dirname } = await import('node:path');
|
|
258
|
+
|
|
259
|
+
const cwd = process.cwd();
|
|
260
|
+
const templates = getTemplates(config);
|
|
261
|
+
|
|
262
|
+
const groups = {
|
|
263
|
+
config: { label: 'Config & State', icon: '⚙', items: [] },
|
|
264
|
+
templates: { label: 'Templates', icon: '📄', items: [] },
|
|
265
|
+
commands: { label: 'Slash Commands', icon: '⚡', items: [] },
|
|
266
|
+
skills: { label: 'Skills (SKILL.md)', icon: '🧠', items: [] },
|
|
267
|
+
scripts: { label: 'Validation Scripts', icon: '🔧', items: [] },
|
|
268
|
+
references: { label: 'Reference Docs', icon: '📚', items: [] },
|
|
269
|
+
hooks: { label: 'Hooks & Settings', icon: '🛡', items: [] },
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
for (const tpl of templates) {
|
|
273
|
+
const p = tpl.path;
|
|
274
|
+
if (p.includes('/hooks/') || p.includes('settings')) groups.hooks.items.push(tpl);
|
|
275
|
+
else if (p.includes('/scripts/')) groups.scripts.items.push(tpl);
|
|
276
|
+
else if (p.includes('/references/')) groups.references.items.push(tpl);
|
|
277
|
+
else if (p.includes('SKILL.md')) groups.skills.items.push(tpl);
|
|
278
|
+
else if (p.includes('/commands/')) groups.commands.items.push(tpl);
|
|
279
|
+
else if (p.includes('/templates/')) groups.templates.items.push(tpl);
|
|
280
|
+
else groups.config.items.push(tpl);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
let count = 0;
|
|
284
|
+
|
|
285
|
+
for (const [, group] of Object.entries(groups)) {
|
|
286
|
+
if (group.items.length === 0) continue;
|
|
287
|
+
|
|
288
|
+
const sp = createSpinner(`${group.label}...`);
|
|
289
|
+
sp.start();
|
|
290
|
+
await sleep(200);
|
|
291
|
+
|
|
292
|
+
let groupCount = 0;
|
|
293
|
+
for (const tpl of group.items) {
|
|
294
|
+
const fullPath = join(cwd, tpl.path);
|
|
295
|
+
mkdirSync(dirname(fullPath), { recursive: true });
|
|
296
|
+
if (!existsSync(fullPath)) {
|
|
297
|
+
writeFileSync(fullPath, tpl.content, 'utf-8');
|
|
298
|
+
if (tpl.executable) chmodSync(fullPath, 0o755);
|
|
299
|
+
groupCount++;
|
|
300
|
+
count++;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
sp.stop(`${group.icon} ${group.label} ${dim(`(${groupCount} files)`)}`);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
return count;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
function printConfig(config) {
|
|
311
|
+
const box = (label, value, extra) => {
|
|
312
|
+
const e = extra ? ` ${dim(extra)}` : '';
|
|
313
|
+
console.log(` ${a.emerald}│${a.reset} ${dim(label.padEnd(16))}${bold(value)}${e}`);
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
console.log(` ${a.emerald}${a.bold}┌─${a.reset} ${bold('Summary')}`);
|
|
317
|
+
console.log(` ${a.emerald}│${a.reset}`);
|
|
318
|
+
box('Project', config.projectName);
|
|
319
|
+
box('Author', config.authorName || dim('(not set)'));
|
|
320
|
+
box('Communication', config.communicationLang);
|
|
321
|
+
box('Documents', config.documentLang);
|
|
322
|
+
box('APED', config.apedDir + '/', 'engine');
|
|
323
|
+
box('Output', config.outputDir + '/', 'artifacts');
|
|
324
|
+
box('Commands', config.commandsDir + '/');
|
|
325
|
+
console.log(` ${a.emerald}│${a.reset}`);
|
|
326
|
+
console.log(` ${a.emerald}${a.bold}└──────────────────────────────────${a.reset}`);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
function printDone(count) {
|
|
330
|
+
console.log('');
|
|
331
|
+
console.log(` ${a.emerald}${a.bold}╔══════════════════════════════════════╗${a.reset}`);
|
|
332
|
+
console.log(` ${a.emerald}${a.bold}║${a.reset} ${a.lime}${a.bold}✓${a.reset} ${bold(`${count} files scaffolded`)} ${a.emerald}${a.bold}║${a.reset}`);
|
|
333
|
+
console.log(` ${a.emerald}${a.bold}║${a.reset} ${dim('Pipeline ready to use')} ${a.emerald}${a.bold}║${a.reset}`);
|
|
334
|
+
console.log(` ${a.emerald}${a.bold}╚══════════════════════════════════════╝${a.reset}`);
|
|
335
|
+
console.log('');
|
|
336
|
+
|
|
337
|
+
console.log(` ${a.bold}Available commands:${a.reset}`);
|
|
338
|
+
console.log('');
|
|
339
|
+
console.log(` ${a.emerald}${a.bold}/aped-a${a.reset} ${dim('Analyze — parallel research → product brief')}`);
|
|
340
|
+
console.log(` ${a.mint}${a.bold}/aped-p${a.reset} ${dim('PRD — autonomous generation from brief')}`);
|
|
341
|
+
console.log(` ${a.yellow}${a.bold}/aped-e${a.reset} ${dim('Epics — requirements decomposition')}`);
|
|
342
|
+
console.log(` ${a.lime}${a.bold}/aped-d${a.reset} ${dim('Dev — TDD story implementation')}`);
|
|
343
|
+
console.log(` ${a.red}${a.bold}/aped-r${a.reset} ${dim('Review — adversarial code review')}`);
|
|
344
|
+
console.log(` ${a.spring}${a.bold}/aped-all${a.reset} ${dim('Full pipeline A→P→E→D→R')}`);
|
|
345
|
+
console.log('');
|
|
346
|
+
console.log(` ${dim('Guardrail hook active — pipeline coherence enforced')}`);
|
|
347
|
+
console.log('');
|
|
348
|
+
}
|
package/src/scaffold.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { mkdirSync, writeFileSync, chmodSync, existsSync } from 'node:fs';
|
|
2
|
+
import { join, dirname } from 'node:path';
|
|
3
|
+
import { getTemplates } from './templates/index.js';
|
|
4
|
+
|
|
5
|
+
export async function scaffold(config) {
|
|
6
|
+
const cwd = process.cwd();
|
|
7
|
+
const templates = getTemplates(config);
|
|
8
|
+
let count = 0;
|
|
9
|
+
|
|
10
|
+
for (const tpl of templates) {
|
|
11
|
+
const fullPath = join(cwd, tpl.path);
|
|
12
|
+
mkdirSync(dirname(fullPath), { recursive: true });
|
|
13
|
+
if (!existsSync(fullPath)) {
|
|
14
|
+
writeFileSync(fullPath, tpl.content, 'utf-8');
|
|
15
|
+
if (tpl.executable) chmodSync(fullPath, 0o755);
|
|
16
|
+
count++;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return count;
|
|
21
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
export function commands(c) {
|
|
2
|
+
const a = c.apedDir;
|
|
3
|
+
return [
|
|
4
|
+
{
|
|
5
|
+
path: `${c.commandsDir}/aped-a.md`,
|
|
6
|
+
content: `---
|
|
7
|
+
name: aped-a
|
|
8
|
+
description: 'Analyze project through parallel research. Use when user says "analyze", "research project", or "aped analyze"'
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
Read and follow the SKILL.md at $PROJECT_ROOT/${a}/aped-a/SKILL.md
|
|
12
|
+
`,
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
path: `${c.commandsDir}/aped-p.md`,
|
|
16
|
+
content: `---
|
|
17
|
+
name: aped-p
|
|
18
|
+
description: 'Generate PRD from product brief. Use when user says "create PRD", "generate PRD", or "aped prd"'
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
Read and follow the SKILL.md at $PROJECT_ROOT/${a}/aped-p/SKILL.md
|
|
22
|
+
`,
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
path: `${c.commandsDir}/aped-e.md`,
|
|
26
|
+
content: `---
|
|
27
|
+
name: aped-e
|
|
28
|
+
description: 'Create epics and stories from PRD. Use when user says "create epics", "break into stories", or "aped epics"'
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
Read and follow the SKILL.md at $PROJECT_ROOT/${a}/aped-e/SKILL.md
|
|
32
|
+
`,
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
path: `${c.commandsDir}/aped-d.md`,
|
|
36
|
+
content: `---
|
|
37
|
+
name: aped-d
|
|
38
|
+
description: 'Dev sprint - implement next story with TDD. Use when user says "start dev", "implement story", or "aped dev"'
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
Read and follow the SKILL.md at $PROJECT_ROOT/${a}/aped-d/SKILL.md
|
|
42
|
+
`,
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
path: `${c.commandsDir}/aped-r.md`,
|
|
46
|
+
content: `---
|
|
47
|
+
name: aped-r
|
|
48
|
+
description: 'Adversarial code review for completed story. Use when user says "review code", "run review", or "aped review"'
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
Read and follow the SKILL.md at $PROJECT_ROOT/${a}/aped-r/SKILL.md
|
|
52
|
+
`,
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
path: `${c.commandsDir}/aped-all.md`,
|
|
56
|
+
content: `---
|
|
57
|
+
name: aped-all
|
|
58
|
+
description: 'Run full APED pipeline (Analyze>PRD>Epics>Dev>Review). Use when user says "run full pipeline", "aped all", or "start from scratch"'
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
Read and follow the SKILL.md at $PROJECT_ROOT/${a}/aped-all/SKILL.md
|
|
62
|
+
`,
|
|
63
|
+
},
|
|
64
|
+
];
|
|
65
|
+
}
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
export function configFiles(c) {
|
|
2
|
+
const a = c.apedDir;
|
|
3
|
+
const o = c.outputDir;
|
|
4
|
+
return [
|
|
5
|
+
{
|
|
6
|
+
path: `${a}/config.yaml`,
|
|
7
|
+
content: `# APED Project Configuration
|
|
8
|
+
project_name: ${c.projectName}
|
|
9
|
+
user_name: ${c.authorName}
|
|
10
|
+
communication_language: ${c.communicationLang}
|
|
11
|
+
document_output_language: ${c.documentLang}
|
|
12
|
+
aped_path: ${a}
|
|
13
|
+
output_path: ${o}
|
|
14
|
+
`,
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
path: `${a}/state.yaml`,
|
|
18
|
+
content: `# APED Pipeline State
|
|
19
|
+
pipeline:
|
|
20
|
+
current_phase: "none"
|
|
21
|
+
phases: {}
|
|
22
|
+
|
|
23
|
+
# Sprint state (generated by aped-e, consumed by aped-d and aped-r)
|
|
24
|
+
sprint:
|
|
25
|
+
project: ""
|
|
26
|
+
stories: {}
|
|
27
|
+
`,
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
path: `${a}/templates/product-brief.md`,
|
|
31
|
+
content: `# Product Brief: {{project_name}}
|
|
32
|
+
|
|
33
|
+
## Executive Summary
|
|
34
|
+
|
|
35
|
+
<!-- 2-3 sentences: what we're building and why it matters -->
|
|
36
|
+
|
|
37
|
+
## Core Vision
|
|
38
|
+
|
|
39
|
+
### Problem Statement
|
|
40
|
+
|
|
41
|
+
<!-- What specific problem exists today? -->
|
|
42
|
+
|
|
43
|
+
### Problem Impact
|
|
44
|
+
|
|
45
|
+
<!-- Who is affected and how? Quantify where possible. -->
|
|
46
|
+
|
|
47
|
+
### Proposed Solution
|
|
48
|
+
|
|
49
|
+
<!-- High-level description of the solution approach -->
|
|
50
|
+
|
|
51
|
+
### Key Differentiators
|
|
52
|
+
|
|
53
|
+
<!-- What makes this different from existing solutions? -->
|
|
54
|
+
|
|
55
|
+
## Target Users
|
|
56
|
+
|
|
57
|
+
### Primary Users
|
|
58
|
+
|
|
59
|
+
<!-- Who are the main users? Demographics, behaviors, technical level -->
|
|
60
|
+
|
|
61
|
+
### Secondary Users
|
|
62
|
+
|
|
63
|
+
<!-- Other stakeholders who interact with the product -->
|
|
64
|
+
|
|
65
|
+
### User Journey
|
|
66
|
+
|
|
67
|
+
<!-- Key touchpoints from discovery to daily usage -->
|
|
68
|
+
|
|
69
|
+
## Success Metrics
|
|
70
|
+
|
|
71
|
+
### Business Objectives
|
|
72
|
+
|
|
73
|
+
<!-- What business outcomes define success? -->
|
|
74
|
+
|
|
75
|
+
### KPIs
|
|
76
|
+
|
|
77
|
+
<!-- Measurable indicators with targets and timeframes -->
|
|
78
|
+
|
|
79
|
+
## MVP Scope
|
|
80
|
+
|
|
81
|
+
### Core Features
|
|
82
|
+
|
|
83
|
+
<!-- Minimum feature set for first viable release -->
|
|
84
|
+
|
|
85
|
+
### Out of Scope
|
|
86
|
+
|
|
87
|
+
<!-- Explicitly excluded from MVP -->
|
|
88
|
+
|
|
89
|
+
### Success Criteria
|
|
90
|
+
|
|
91
|
+
<!-- How we know MVP succeeded -->
|
|
92
|
+
|
|
93
|
+
### Future Vision
|
|
94
|
+
|
|
95
|
+
<!-- Post-MVP direction and growth areas -->
|
|
96
|
+
`,
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
path: `${a}/templates/prd.md`,
|
|
100
|
+
content: `# Product Requirements Document — {{project_name}}
|
|
101
|
+
|
|
102
|
+
**Author:** {{user_name}}
|
|
103
|
+
**Date:** {{date}}
|
|
104
|
+
|
|
105
|
+
## Executive Summary
|
|
106
|
+
|
|
107
|
+
## Success Criteria
|
|
108
|
+
|
|
109
|
+
### User Outcomes
|
|
110
|
+
|
|
111
|
+
### Business Outcomes
|
|
112
|
+
|
|
113
|
+
### Technical Outcomes
|
|
114
|
+
|
|
115
|
+
### Measurable Outcomes
|
|
116
|
+
|
|
117
|
+
## Product Scope
|
|
118
|
+
|
|
119
|
+
### MVP (Phase 1)
|
|
120
|
+
|
|
121
|
+
### Growth (Phase 2)
|
|
122
|
+
|
|
123
|
+
### Vision (Phase 3)
|
|
124
|
+
|
|
125
|
+
## User Journeys
|
|
126
|
+
|
|
127
|
+
## Domain Requirements
|
|
128
|
+
|
|
129
|
+
<!-- Conditional: only if domain-complexity.csv flags high/medium complexity -->
|
|
130
|
+
|
|
131
|
+
## Functional Requirements
|
|
132
|
+
|
|
133
|
+
<!-- Format: FR#: [Actor] can [capability] [context/constraint] -->
|
|
134
|
+
<!-- Target: 10-80 FRs -->
|
|
135
|
+
|
|
136
|
+
## Non-Functional Requirements
|
|
137
|
+
|
|
138
|
+
### Performance
|
|
139
|
+
|
|
140
|
+
### Security
|
|
141
|
+
|
|
142
|
+
### Scalability
|
|
143
|
+
|
|
144
|
+
### Accessibility
|
|
145
|
+
|
|
146
|
+
### Integration
|
|
147
|
+
`,
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
path: `${a}/templates/epics.md`,
|
|
151
|
+
content: `# Epics & Stories — {{project_name}}
|
|
152
|
+
|
|
153
|
+
**Generated:** {{date}}
|
|
154
|
+
**Source PRD:** {{prd_path}}
|
|
155
|
+
|
|
156
|
+
## Requirements Inventory
|
|
157
|
+
|
|
158
|
+
### Functional Requirements
|
|
159
|
+
|
|
160
|
+
### Non-Functional Requirements
|
|
161
|
+
|
|
162
|
+
### Additional Requirements
|
|
163
|
+
|
|
164
|
+
## FR Coverage Map
|
|
165
|
+
|
|
166
|
+
<!-- Every FR mapped to exactly one epic. Format: FR# -> Epic N -->
|
|
167
|
+
|
|
168
|
+
## Epics
|
|
169
|
+
|
|
170
|
+
### Epic 1: {{epic_title}}
|
|
171
|
+
|
|
172
|
+
**Goal:** {{user-value-focused goal}}
|
|
173
|
+
|
|
174
|
+
#### Story 1.1: {{story_title}}
|
|
175
|
+
|
|
176
|
+
**As a** {{role}}, **I want** {{capability}}, **so that** {{benefit}}.
|
|
177
|
+
|
|
178
|
+
**Acceptance Criteria:**
|
|
179
|
+
- **Given** {{context}}, **When** {{action}}, **Then** {{outcome}}
|
|
180
|
+
|
|
181
|
+
**Tasks:**
|
|
182
|
+
- [ ] {{task description}} [AC: {{ac_ref}}]
|
|
183
|
+
|
|
184
|
+
**Dev Notes:**
|
|
185
|
+
- Architecture: {{relevant patterns}}
|
|
186
|
+
- Files: {{files to create/modify}}
|
|
187
|
+
- Testing: {{test approach}}
|
|
188
|
+
`,
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
path: `${a}/templates/story.md`,
|
|
192
|
+
content: `# Story: {{story_key}} — {{story_title}}
|
|
193
|
+
|
|
194
|
+
**Epic:** {{epic_title}}
|
|
195
|
+
**Status:** {{status}}
|
|
196
|
+
|
|
197
|
+
## User Story
|
|
198
|
+
|
|
199
|
+
**As a** {{role}}, **I want** {{capability}}, **so that** {{benefit}}.
|
|
200
|
+
|
|
201
|
+
## Acceptance Criteria
|
|
202
|
+
|
|
203
|
+
- **Given** {{context}}, **When** {{action}}, **Then** {{outcome}}
|
|
204
|
+
|
|
205
|
+
## Tasks
|
|
206
|
+
|
|
207
|
+
- [ ] {{task description}} [AC: {{ac_ref}}]
|
|
208
|
+
|
|
209
|
+
## Dev Notes
|
|
210
|
+
|
|
211
|
+
- **Architecture:** {{relevant architecture decisions and patterns}}
|
|
212
|
+
- **Files:** {{files to create or modify}}
|
|
213
|
+
- **Testing:** {{test approach and framework}}
|
|
214
|
+
- **Dependencies:** {{external libs, APIs, services}}
|
|
215
|
+
|
|
216
|
+
## Dev Agent Record
|
|
217
|
+
|
|
218
|
+
- **Model:** {{model used}}
|
|
219
|
+
- **Started:** {{timestamp}}
|
|
220
|
+
- **Completed:** {{timestamp}}
|
|
221
|
+
|
|
222
|
+
### Debug Log
|
|
223
|
+
|
|
224
|
+
### Completion Notes
|
|
225
|
+
|
|
226
|
+
### File List
|
|
227
|
+
`,
|
|
228
|
+
},
|
|
229
|
+
];
|
|
230
|
+
}
|