@kudusov.takhir/ba-toolkit 1.2.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 (59) hide show
  1. package/CHANGELOG.md +125 -0
  2. package/COMMANDS.md +69 -0
  3. package/LICENSE +21 -0
  4. package/README.md +842 -0
  5. package/README.ru.md +846 -0
  6. package/bin/ba-toolkit.js +468 -0
  7. package/package.json +49 -0
  8. package/skills/ac/SKILL.md +88 -0
  9. package/skills/analyze/SKILL.md +126 -0
  10. package/skills/apicontract/SKILL.md +113 -0
  11. package/skills/brief/SKILL.md +120 -0
  12. package/skills/clarify/SKILL.md +96 -0
  13. package/skills/datadict/SKILL.md +98 -0
  14. package/skills/estimate/SKILL.md +124 -0
  15. package/skills/export/SKILL.md +215 -0
  16. package/skills/glossary/SKILL.md +145 -0
  17. package/skills/handoff/SKILL.md +146 -0
  18. package/skills/nfr/SKILL.md +85 -0
  19. package/skills/principles/SKILL.md +182 -0
  20. package/skills/references/closing-message.md +33 -0
  21. package/skills/references/domains/ecommerce.md +209 -0
  22. package/skills/references/domains/fintech.md +180 -0
  23. package/skills/references/domains/healthcare.md +223 -0
  24. package/skills/references/domains/igaming.md +183 -0
  25. package/skills/references/domains/logistics.md +221 -0
  26. package/skills/references/domains/on-demand.md +231 -0
  27. package/skills/references/domains/real-estate.md +241 -0
  28. package/skills/references/domains/saas.md +185 -0
  29. package/skills/references/domains/social-media.md +234 -0
  30. package/skills/references/environment.md +57 -0
  31. package/skills/references/prerequisites.md +191 -0
  32. package/skills/references/templates/README.md +35 -0
  33. package/skills/references/templates/ac-template.md +58 -0
  34. package/skills/references/templates/analyze-template.md +65 -0
  35. package/skills/references/templates/apicontract-template.md +183 -0
  36. package/skills/references/templates/brief-template.md +51 -0
  37. package/skills/references/templates/datadict-template.md +75 -0
  38. package/skills/references/templates/export-template.md +112 -0
  39. package/skills/references/templates/handoff-template.md +102 -0
  40. package/skills/references/templates/nfr-template.md +97 -0
  41. package/skills/references/templates/principles-template.md +118 -0
  42. package/skills/references/templates/research-template.md +99 -0
  43. package/skills/references/templates/risk-template.md +188 -0
  44. package/skills/references/templates/scenarios-template.md +93 -0
  45. package/skills/references/templates/sprint-template.md +158 -0
  46. package/skills/references/templates/srs-template.md +90 -0
  47. package/skills/references/templates/stories-template.md +60 -0
  48. package/skills/references/templates/trace-template.md +59 -0
  49. package/skills/references/templates/usecases-template.md +51 -0
  50. package/skills/references/templates/wireframes-template.md +96 -0
  51. package/skills/research/SKILL.md +136 -0
  52. package/skills/risk/SKILL.md +163 -0
  53. package/skills/scenarios/SKILL.md +113 -0
  54. package/skills/sprint/SKILL.md +174 -0
  55. package/skills/srs/SKILL.md +124 -0
  56. package/skills/stories/SKILL.md +85 -0
  57. package/skills/trace/SKILL.md +85 -0
  58. package/skills/usecases/SKILL.md +91 -0
  59. package/skills/wireframes/SKILL.md +107 -0
@@ -0,0 +1,468 @@
1
+ #!/usr/bin/env node
2
+ /*
3
+ * BA Toolkit CLI
4
+ *
5
+ * Zero runtime dependencies — only Node.js built-ins.
6
+ * Cross-platform: tested on macOS, Linux, and Windows.
7
+ */
8
+ 'use strict';
9
+
10
+ const fs = require('fs');
11
+ const path = require('path');
12
+ const os = require('os');
13
+ const readline = require('readline');
14
+
15
+ // --- Constants ---------------------------------------------------------
16
+
17
+ const PACKAGE_ROOT = path.resolve(__dirname, '..');
18
+ const SKILLS_DIR = path.join(PACKAGE_ROOT, 'skills');
19
+ const PKG = JSON.parse(fs.readFileSync(path.join(PACKAGE_ROOT, 'package.json'), 'utf8'));
20
+
21
+ const AGENTS = {
22
+ 'claude-code': {
23
+ name: 'Claude Code',
24
+ projectPath: '.claude/skills/ba-toolkit',
25
+ globalPath: path.join(os.homedir(), '.claude', 'skills', 'ba-toolkit'),
26
+ format: 'skill',
27
+ restartHint: 'Restart Claude Code to load the new skills.',
28
+ },
29
+ codex: {
30
+ name: 'OpenAI Codex CLI',
31
+ projectPath: null, // Codex uses only global
32
+ globalPath: path.join(process.env.CODEX_HOME || path.join(os.homedir(), '.codex'), 'skills', 'ba-toolkit'),
33
+ format: 'skill',
34
+ restartHint: 'Restart the Codex CLI to load the new skills.',
35
+ },
36
+ gemini: {
37
+ name: 'Google Gemini CLI',
38
+ projectPath: '.gemini/skills/ba-toolkit',
39
+ globalPath: path.join(os.homedir(), '.gemini', 'skills', 'ba-toolkit'),
40
+ format: 'skill',
41
+ restartHint: 'Reload Gemini CLI to pick up the new skills.',
42
+ },
43
+ cursor: {
44
+ name: 'Cursor',
45
+ projectPath: '.cursor/rules/ba-toolkit',
46
+ globalPath: null, // Cursor rules are project-scoped
47
+ format: 'mdc',
48
+ restartHint: 'Reload the Cursor window to apply new rules.',
49
+ },
50
+ windsurf: {
51
+ name: 'Windsurf',
52
+ projectPath: '.windsurf/rules/ba-toolkit',
53
+ globalPath: null,
54
+ format: 'mdc',
55
+ restartHint: 'Reload the Windsurf window to apply new rules.',
56
+ },
57
+ };
58
+
59
+ const DOMAINS = [
60
+ { id: 'igaming', desc: 'iGaming — slots, betting, casino, Telegram Mini Apps' },
61
+ { id: 'fintech', desc: 'Fintech — neobanks, payments, crypto, P2P lending' },
62
+ { id: 'saas', desc: 'SaaS — B2B platforms, CRM, analytics, EdTech' },
63
+ { id: 'ecommerce', desc: 'E-commerce — stores, marketplaces, D2C brands' },
64
+ { id: 'healthcare', desc: 'Healthcare — telemedicine, EHR, patient portals' },
65
+ { id: 'logistics', desc: 'Logistics — delivery, courier, WMS, fleet' },
66
+ { id: 'on-demand', desc: 'On-demand — ride-hailing, home services, marketplace' },
67
+ { id: 'social-media', desc: 'Social/Media — social networks, creator platforms' },
68
+ { id: 'real-estate', desc: 'Real Estate — property portals, CRM, rental management' },
69
+ { id: 'custom', desc: 'Custom — any other domain' },
70
+ ];
71
+
72
+ // --- Terminal helpers --------------------------------------------------
73
+
74
+ const NO_COLOR = !!process.env.NO_COLOR || !process.stdout.isTTY;
75
+ const colour = (code) => (str) => NO_COLOR ? String(str) : `\x1b[${code}m${str}\x1b[0m`;
76
+ const cyan = colour(36);
77
+ const green = colour(32);
78
+ const yellow = colour(33);
79
+ const red = colour(31);
80
+ const gray = colour(90);
81
+ const bold = colour(1);
82
+
83
+ function log(...args) { console.log(...args); }
84
+ function logError(...args) { console.error(red('error:'), ...args); }
85
+
86
+ // --- Arg parsing -------------------------------------------------------
87
+
88
+ function parseArgs(argv) {
89
+ const args = { _: [], flags: {} };
90
+ for (let i = 0; i < argv.length; i++) {
91
+ const a = argv[i];
92
+ if (a === '--') {
93
+ args._.push(...argv.slice(i + 1));
94
+ break;
95
+ }
96
+ if (a.startsWith('--')) {
97
+ const key = a.slice(2);
98
+ const next = argv[i + 1];
99
+ if (next !== undefined && !next.startsWith('-')) {
100
+ args.flags[key] = next;
101
+ i++;
102
+ } else {
103
+ args.flags[key] = true;
104
+ }
105
+ } else if (a.startsWith('-') && a.length > 1) {
106
+ const key = a.slice(1);
107
+ args.flags[key] = true;
108
+ } else {
109
+ args._.push(a);
110
+ }
111
+ }
112
+ return args;
113
+ }
114
+
115
+ // --- Prompt helper -----------------------------------------------------
116
+
117
+ function prompt(question) {
118
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
119
+ return new Promise((resolve) => {
120
+ rl.question(question, (answer) => {
121
+ rl.close();
122
+ resolve(answer.trim());
123
+ });
124
+ });
125
+ }
126
+
127
+ // --- Utilities ---------------------------------------------------------
128
+
129
+ function sanitiseSlug(input) {
130
+ return String(input || '')
131
+ .toLowerCase()
132
+ .replace(/[^a-z0-9-]/g, '-')
133
+ .replace(/-+/g, '-')
134
+ .replace(/^-+|-+$/g, '');
135
+ }
136
+
137
+ function today() {
138
+ return new Date().toISOString().slice(0, 10);
139
+ }
140
+
141
+ function copyDir(src, dest, { dryRun = false, transform = null } = {}) {
142
+ if (!fs.existsSync(src)) {
143
+ throw new Error(`Source directory not found: ${src}`);
144
+ }
145
+ const copied = [];
146
+ (function walk(s, d) {
147
+ if (!dryRun) fs.mkdirSync(d, { recursive: true });
148
+ for (const entry of fs.readdirSync(s, { withFileTypes: true })) {
149
+ const srcPath = path.join(s, entry.name);
150
+ let destPath = path.join(d, entry.name);
151
+ if (entry.isDirectory()) {
152
+ walk(srcPath, destPath);
153
+ continue;
154
+ }
155
+ if (transform) {
156
+ const result = transform(srcPath, destPath);
157
+ if (!result) continue;
158
+ destPath = result.destPath;
159
+ if (!dryRun) {
160
+ fs.mkdirSync(path.dirname(destPath), { recursive: true });
161
+ fs.writeFileSync(destPath, result.content);
162
+ }
163
+ } else {
164
+ if (!dryRun) fs.copyFileSync(srcPath, destPath);
165
+ }
166
+ copied.push(destPath);
167
+ }
168
+ })(src, dest);
169
+ return copied;
170
+ }
171
+
172
+ // Transform SKILL.md → .mdc for Cursor / Windsurf.
173
+ // Other files (references/, templates/) are copied as-is.
174
+ function skillToMdc(srcPath, destPath) {
175
+ const base = path.basename(srcPath);
176
+ if (base !== 'SKILL.md') {
177
+ return { destPath, content: fs.readFileSync(srcPath) };
178
+ }
179
+ const content = fs.readFileSync(srcPath, 'utf8');
180
+ const fmMatch = content.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n([\s\S]*)$/);
181
+ let frontmatter = '';
182
+ let body = content;
183
+ if (fmMatch) {
184
+ frontmatter = fmMatch[1];
185
+ body = fmMatch[2];
186
+ }
187
+ const nameMatch = frontmatter.match(/^name:\s*(.+)$/m);
188
+ // description is usually a multi-line block with `description: >`
189
+ const descMatch = frontmatter.match(/description:\s*>\s*\r?\n([\s\S]*?)(?:\r?\n\w|$)/);
190
+ const descInlineMatch = frontmatter.match(/^description:\s*(.+)$/m);
191
+ const ruleName = nameMatch ? nameMatch[1].trim() : path.basename(path.dirname(srcPath));
192
+ const rawDesc = descMatch ? descMatch[1] : (descInlineMatch ? descInlineMatch[1] : '');
193
+ const ruleDesc = rawDesc.replace(/\s+/g, ' ').trim();
194
+ const mdcFrontmatter = `---\ndescription: ${ruleDesc}\nalwaysApply: false\n---\n\n`;
195
+ const newDestPath = path.join(path.dirname(destPath), `${ruleName}.mdc`);
196
+ return { destPath: newDestPath, content: mdcFrontmatter + body };
197
+ }
198
+
199
+ function renderAgentsMd({ name, slug, domain }) {
200
+ return `# BA Toolkit — Project Context
201
+
202
+ > Auto-generated by \`ba-toolkit init\` on ${today()}. Updated automatically by /brief and /srs.
203
+
204
+ ## Active Project
205
+
206
+ **Project:** ${name}
207
+ **Slug:** ${slug}
208
+ **Domain:** ${domain}
209
+ **Language:** English
210
+ **Output folder:** output/${slug}/
211
+
212
+ ## Pipeline Status
213
+
214
+ | Stage | Skill | Status | File |
215
+ |-------|-------|--------|------|
216
+ | 0 | /principles | ⬜ Not started | — |
217
+ | 1 | /brief | ⬜ Not started | — |
218
+ | 2 | /srs | ⬜ Not started | — |
219
+ | 3 | /stories | ⬜ Not started | — |
220
+ | 4 | /usecases | ⬜ Not started | — |
221
+ | 5 | /ac | ⬜ Not started | — |
222
+ | 6 | /nfr | ⬜ Not started | — |
223
+ | 7 | /datadict | ⬜ Not started | — |
224
+ | 7a | /research | ⬜ Not started | — |
225
+ | 8 | /apicontract | ⬜ Not started | — |
226
+ | 9 | /wireframes | ⬜ Not started | — |
227
+ | 10 | /scenarios | ⬜ Not started | — |
228
+ | 11 | /handoff | ⬜ Not started | — |
229
+
230
+ ## Key Constraints
231
+
232
+ - Domain: ${domain}
233
+ - (Add constraints after /brief completes)
234
+
235
+ ## Key Stakeholder Roles
236
+
237
+ - (Populated after /srs completes)
238
+
239
+ ## Open Questions
240
+
241
+ - (None yet)
242
+ `;
243
+ }
244
+
245
+ // --- Commands ----------------------------------------------------------
246
+
247
+ async function cmdInit(args) {
248
+ log('');
249
+ log(' ' + cyan('BA Toolkit — New Project Setup'));
250
+ log(' ' + cyan('================================'));
251
+ log('');
252
+
253
+ let slug = args.flags.slug;
254
+ if (!slug) slug = await prompt(' Project slug (lowercase, hyphens only, e.g. dragon-fortune): ');
255
+ slug = sanitiseSlug(slug);
256
+ if (!slug) {
257
+ logError('Invalid or empty slug.');
258
+ process.exit(1);
259
+ }
260
+
261
+ let name = args.flags.name;
262
+ if (!name) name = await prompt(' Project name (human-readable, e.g. Dragon Fortune): ');
263
+ if (!name) {
264
+ logError('Project name is required.');
265
+ process.exit(1);
266
+ }
267
+
268
+ let domain = args.flags.domain;
269
+ if (!domain) {
270
+ log('');
271
+ log(' ' + yellow('Available domains:'));
272
+ for (const d of DOMAINS) {
273
+ log(` ${d.id.padEnd(14)} ${d.desc}`);
274
+ }
275
+ log('');
276
+ domain = await prompt(' Domain: ');
277
+ }
278
+ domain = String(domain || '').toLowerCase().trim();
279
+ if (!domain) domain = 'custom';
280
+
281
+ log('');
282
+ log(' ' + green('Creating project structure...'));
283
+
284
+ const outputDir = path.join('output', slug);
285
+ if (!fs.existsSync(outputDir)) {
286
+ fs.mkdirSync(outputDir, { recursive: true });
287
+ log(` created ${outputDir}`);
288
+ } else {
289
+ log(` exists ${outputDir}`);
290
+ }
291
+
292
+ const agentsPath = 'AGENTS.md';
293
+ let writeAgents = true;
294
+ if (fs.existsSync(agentsPath)) {
295
+ const answer = await prompt(' AGENTS.md already exists. Overwrite? (y/N): ');
296
+ if (answer.toLowerCase() !== 'y') {
297
+ writeAgents = false;
298
+ log(' skipped AGENTS.md');
299
+ }
300
+ }
301
+ if (writeAgents) {
302
+ fs.writeFileSync(agentsPath, renderAgentsMd({ name, slug, domain }));
303
+ log(' created AGENTS.md');
304
+ }
305
+
306
+ log('');
307
+ log(' ' + cyan(`Project '${name}' (${slug}) initialised.`));
308
+ log('');
309
+ log(' ' + yellow('Next steps:'));
310
+ log(' 1. Install skills for your agent:');
311
+ log(' ' + gray('ba-toolkit install --for claude-code'));
312
+ log(' 2. Open your AI assistant (Claude, Cursor, etc.)');
313
+ log(' 3. Optional: run /principles to define project-wide conventions');
314
+ log(' 4. Run /brief to start the pipeline');
315
+ log('');
316
+ log(' ' + gray(`Artifacts will be saved to: ${outputDir}/`));
317
+ log('');
318
+ }
319
+
320
+ async function cmdInstall(args) {
321
+ const agentId = args.flags.for;
322
+ if (!agentId || agentId === true) {
323
+ logError('--for <agent> is required.');
324
+ log('Supported agents: ' + Object.keys(AGENTS).join(', '));
325
+ process.exit(1);
326
+ }
327
+ const agent = AGENTS[agentId];
328
+ if (!agent) {
329
+ logError(`Unknown agent: ${agentId}`);
330
+ log('Supported: ' + Object.keys(AGENTS).join(', '));
331
+ process.exit(1);
332
+ }
333
+
334
+ const requestedGlobal = !!args.flags.global;
335
+ const requestedProject = !!args.flags.project;
336
+ let isGlobal = requestedGlobal;
337
+ if (!requestedGlobal && !requestedProject) {
338
+ // Default: project-level if supported, otherwise global
339
+ isGlobal = !agent.projectPath;
340
+ }
341
+ if (isGlobal && !agent.globalPath) {
342
+ logError(`${agent.name} does not support --global install.`);
343
+ process.exit(1);
344
+ }
345
+ if (!isGlobal && !agent.projectPath) {
346
+ logError(`${agent.name} does not support project-level install. Use --global.`);
347
+ process.exit(1);
348
+ }
349
+
350
+ const destDir = isGlobal ? agent.globalPath : path.resolve(process.cwd(), agent.projectPath);
351
+ const dryRun = !!args.flags['dry-run'];
352
+
353
+ log('');
354
+ log(' ' + cyan(`BA Toolkit — Install for ${agent.name}`));
355
+ log(' ' + cyan('================================'));
356
+ log('');
357
+ log(` Source: ${SKILLS_DIR}`);
358
+ log(` Destination: ${destDir}`);
359
+ log(` Scope: ${isGlobal ? 'global (user-wide)' : 'project-level'}`);
360
+ log(` Format: ${agent.format === 'mdc' ? '.mdc (converted from SKILL.md)' : 'SKILL.md (native)'}`);
361
+ if (dryRun) log(' ' + yellow('Mode: dry-run (no files will be written)'));
362
+ log('');
363
+
364
+ if (fs.existsSync(destDir) && !dryRun) {
365
+ const answer = await prompt(` ${destDir} already exists. Overwrite? (y/N): `);
366
+ if (answer.toLowerCase() !== 'y') {
367
+ log(' Cancelled.');
368
+ return;
369
+ }
370
+ }
371
+
372
+ const transform = agent.format === 'mdc' ? skillToMdc : null;
373
+ let copied;
374
+ try {
375
+ copied = copyDir(SKILLS_DIR, destDir, { dryRun, transform });
376
+ } catch (err) {
377
+ logError(err.message);
378
+ process.exit(1);
379
+ }
380
+
381
+ log(' ' + green(`${dryRun ? 'Would copy' : 'Copied'} ${copied.length} files.`));
382
+ log('');
383
+ if (!dryRun) {
384
+ log(' ' + cyan('Install complete.'));
385
+ if (agent.format === 'mdc') {
386
+ log(' ' + gray('SKILL.md files converted to .mdc rule format.'));
387
+ }
388
+ log(' ' + yellow(agent.restartHint));
389
+ }
390
+ log('');
391
+ }
392
+
393
+ function cmdHelp() {
394
+ log(`${bold('ba-toolkit')} v${PKG.version} — AI-powered Business Analyst pipeline
395
+
396
+ ${bold('USAGE')}
397
+ ba-toolkit <command> [options]
398
+
399
+ ${bold('COMMANDS')}
400
+ init Interactive project initialiser. Creates
401
+ output/{slug}/ and a starter AGENTS.md.
402
+ install --for <agent> Install skills into an agent's directory.
403
+
404
+ ${bold('INSTALL OPTIONS')}
405
+ --for <agent> One of: ${Object.keys(AGENTS).join(', ')}
406
+ --global User-wide install
407
+ --project Project-level install (default when supported)
408
+ --dry-run Preview without writing files
409
+
410
+ ${bold('INIT OPTIONS')}
411
+ --slug <slug> Skip the slug prompt
412
+ --name <name> Skip the project name prompt
413
+ --domain <domain> Skip the domain prompt
414
+
415
+ ${bold('GENERAL OPTIONS')}
416
+ --version, -v Print version and exit
417
+ --help, -h Print this help and exit
418
+
419
+ ${bold('EXAMPLES')}
420
+ ba-toolkit init
421
+ ba-toolkit init --slug dragon-fortune --name "Dragon Fortune" --domain igaming
422
+ ba-toolkit install --for claude-code
423
+ ba-toolkit install --for claude-code --global
424
+ ba-toolkit install --for cursor
425
+ ba-toolkit install --for gemini --dry-run
426
+
427
+ ${bold('LEARN MORE')}
428
+ https://github.com/TakhirKudusov/ba-toolkit
429
+ `);
430
+ }
431
+
432
+ // --- Main --------------------------------------------------------------
433
+
434
+ async function main() {
435
+ const args = parseArgs(process.argv.slice(2));
436
+
437
+ if (args.flags.version || args.flags.v) {
438
+ log(PKG.version);
439
+ return;
440
+ }
441
+
442
+ if (args.flags.help || args.flags.h || args._.length === 0) {
443
+ cmdHelp();
444
+ return;
445
+ }
446
+
447
+ const command = args._[0];
448
+ switch (command) {
449
+ case 'init':
450
+ await cmdInit(args);
451
+ break;
452
+ case 'install':
453
+ await cmdInstall(args);
454
+ break;
455
+ case 'help':
456
+ cmdHelp();
457
+ break;
458
+ default:
459
+ logError(`Unknown command: ${command}`);
460
+ log('Run ' + cyan('ba-toolkit --help') + ' for usage.');
461
+ process.exit(1);
462
+ }
463
+ }
464
+
465
+ main().catch((err) => {
466
+ logError(err && (err.stack || err.message) || String(err));
467
+ process.exit(1);
468
+ });
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "@kudusov.takhir/ba-toolkit",
3
+ "version": "1.2.0",
4
+ "description": "AI-powered Business Analyst pipeline — 21 skills from project brief to development handoff. Works with Claude Code, Codex CLI, Gemini CLI, Cursor, and Windsurf.",
5
+ "keywords": [
6
+ "business-analyst",
7
+ "requirements",
8
+ "srs",
9
+ "user-stories",
10
+ "ba-toolkit",
11
+ "claude",
12
+ "claude-code",
13
+ "codex",
14
+ "gemini",
15
+ "cursor",
16
+ "windsurf",
17
+ "ai-skills",
18
+ "agent-skills",
19
+ "cli"
20
+ ],
21
+ "homepage": "https://github.com/TakhirKudusov/ba-toolkit",
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "git+https://github.com/TakhirKudusov/ba-toolkit.git"
25
+ },
26
+ "bugs": {
27
+ "url": "https://github.com/TakhirKudusov/ba-toolkit/issues"
28
+ },
29
+ "license": "MIT",
30
+ "author": "TakhirKudusov",
31
+ "bin": {
32
+ "ba-toolkit": "bin/ba-toolkit.js"
33
+ },
34
+ "files": [
35
+ "bin/",
36
+ "skills/",
37
+ "LICENSE",
38
+ "README.md",
39
+ "README.ru.md",
40
+ "CHANGELOG.md",
41
+ "COMMANDS.md"
42
+ ],
43
+ "engines": {
44
+ "node": ">=18"
45
+ },
46
+ "scripts": {
47
+ "test": "node bin/ba-toolkit.js --help > /dev/null && echo CLI smoke test passed"
48
+ }
49
+ }
@@ -0,0 +1,88 @@
1
+ ---
2
+ name: ba-ac
3
+ description: >
4
+ Generate Acceptance Criteria in Given/When/Then (Gherkin) format for each User Story. Use on /ac command, or when the user asks for "acceptance criteria", "given when then", "gherkin scenarios", "write AC", "definition of done", "how to verify a story", "test scenarios for stories". Fifth step of the BA Toolkit pipeline.
5
+ ---
6
+
7
+ # /ac — Acceptance Criteria
8
+
9
+ Fifth step of the BA Toolkit pipeline. Generates AC in Given/When/Then (Gherkin) format.
10
+
11
+ ## Context loading
12
+
13
+ 0. If `00_principles_*.md` exists in the output directory, load it and apply its conventions (artifact language, ID format, traceability requirements, Definition of Ready, quality gate threshold).
14
+ 1. Read `01_brief_*.md`, `02_srs_*.md`, `03_stories_*.md`, `04_usecases_*.md`. If usecases missing, warn and suggest `/usecases`.
15
+ 2. Extract: slug, domain, US list, UC list, business rules, roles.
16
+ 3. If domain supported, load `references/domains/{domain}.md`, section `5. /ac`.
17
+
18
+ ## Environment
19
+
20
+ Read `references/environment.md` from the `ba-toolkit` directory to determine the output directory for the current platform. If the file is unavailable, apply the default rule: if `/mnt/user-data/outputs/` exists and is writable, save there (Claude.ai); otherwise save to the current working directory.
21
+
22
+ ## Interview
23
+
24
+ 3–7 questions per round, 2–4 rounds.
25
+
26
+ **Required topics:**
27
+ 1. Which business rules should be reflected in AC (limits, formulas, thresholds)?
28
+ 2. Are negative scenarios needed for each US?
29
+ 3. Which boundary values are critical?
30
+ 4. Which US need multiple AC (different roles, states)?
31
+ 5. Are there data precision requirements (decimal places, formats)?
32
+
33
+ Supplement with domain-specific questions from the reference.
34
+
35
+ ## Generation
36
+
37
+ **File:** `05_ac_{slug}.md`
38
+
39
+ ```markdown
40
+ # Acceptance Criteria: {Name}
41
+
42
+ ## US-{NNN}: {Short description}
43
+
44
+ ### AC-{NNN}-{NN}: {Scenario name}
45
+ **Type:** {positive | negative | boundary}
46
+ - **Given** {initial state}
47
+ - **When** {action}
48
+ - **Then** {expected result}
49
+
50
+ **Links:** US-{NNN}, UC-{NNN}
51
+ ```
52
+
53
+ **Rules:**
54
+ - Numbering relative to US: AC-001-01 (first AC for US-001).
55
+ - Every US has at least one positive AC.
56
+ - Must-priority US have at least one negative AC.
57
+ - Given = specific state. When = single action. Then = verifiable result.
58
+ - Avoid vague wording — replace "system handles correctly" with concrete behavior.
59
+
60
+ ## Back-reference update
61
+
62
+ After generation, update `03_stories_{slug}.md`: fill the "Acceptance Criteria" field in each US with links to the corresponding AC-{NNN}-{NN}.
63
+
64
+ ## Iterative refinement
65
+
66
+ - `/revise [AC-NNN-NN]` — rewrite.
67
+ - `/expand [US-NNN]` — add AC.
68
+ - `/split [AC-NNN-NN]` — split compound AC.
69
+ - `/clarify [focus]` — targeted ambiguity pass.
70
+ - `/validate` — all US have AC; links correct; Given/When/Then present; stories file updated.
71
+ - `/done` — finalize. Next step: `/nfr`.
72
+
73
+ ## Closing message
74
+
75
+ After saving the artifact, present the following summary to the user (see `references/closing-message.md` for format):
76
+
77
+ - Saved file path.
78
+ - Total number of AC generated: breakdown by type (positive / negative / boundary).
79
+ - Count of user stories covered.
80
+ - Confirmation that back-references in `03_stories_{slug}.md` were updated.
81
+
82
+ Available commands: `/clarify [focus]` · `/revise [AC-NNN-NN]` · `/expand [US-NNN]` · `/split [AC-NNN-NN]` · `/validate` · `/done`
83
+
84
+ Next step: `/nfr`
85
+
86
+ ## Style
87
+
88
+ Formal, neutral. No emoji, slang. Terms explained on first use. Generate the artifact in the language of the user's request.