@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.
- package/CHANGELOG.md +125 -0
- package/COMMANDS.md +69 -0
- package/LICENSE +21 -0
- package/README.md +842 -0
- package/README.ru.md +846 -0
- package/bin/ba-toolkit.js +468 -0
- package/package.json +49 -0
- package/skills/ac/SKILL.md +88 -0
- package/skills/analyze/SKILL.md +126 -0
- package/skills/apicontract/SKILL.md +113 -0
- package/skills/brief/SKILL.md +120 -0
- package/skills/clarify/SKILL.md +96 -0
- package/skills/datadict/SKILL.md +98 -0
- package/skills/estimate/SKILL.md +124 -0
- package/skills/export/SKILL.md +215 -0
- package/skills/glossary/SKILL.md +145 -0
- package/skills/handoff/SKILL.md +146 -0
- package/skills/nfr/SKILL.md +85 -0
- package/skills/principles/SKILL.md +182 -0
- package/skills/references/closing-message.md +33 -0
- package/skills/references/domains/ecommerce.md +209 -0
- package/skills/references/domains/fintech.md +180 -0
- package/skills/references/domains/healthcare.md +223 -0
- package/skills/references/domains/igaming.md +183 -0
- package/skills/references/domains/logistics.md +221 -0
- package/skills/references/domains/on-demand.md +231 -0
- package/skills/references/domains/real-estate.md +241 -0
- package/skills/references/domains/saas.md +185 -0
- package/skills/references/domains/social-media.md +234 -0
- package/skills/references/environment.md +57 -0
- package/skills/references/prerequisites.md +191 -0
- package/skills/references/templates/README.md +35 -0
- package/skills/references/templates/ac-template.md +58 -0
- package/skills/references/templates/analyze-template.md +65 -0
- package/skills/references/templates/apicontract-template.md +183 -0
- package/skills/references/templates/brief-template.md +51 -0
- package/skills/references/templates/datadict-template.md +75 -0
- package/skills/references/templates/export-template.md +112 -0
- package/skills/references/templates/handoff-template.md +102 -0
- package/skills/references/templates/nfr-template.md +97 -0
- package/skills/references/templates/principles-template.md +118 -0
- package/skills/references/templates/research-template.md +99 -0
- package/skills/references/templates/risk-template.md +188 -0
- package/skills/references/templates/scenarios-template.md +93 -0
- package/skills/references/templates/sprint-template.md +158 -0
- package/skills/references/templates/srs-template.md +90 -0
- package/skills/references/templates/stories-template.md +60 -0
- package/skills/references/templates/trace-template.md +59 -0
- package/skills/references/templates/usecases-template.md +51 -0
- package/skills/references/templates/wireframes-template.md +96 -0
- package/skills/research/SKILL.md +136 -0
- package/skills/risk/SKILL.md +163 -0
- package/skills/scenarios/SKILL.md +113 -0
- package/skills/sprint/SKILL.md +174 -0
- package/skills/srs/SKILL.md +124 -0
- package/skills/stories/SKILL.md +85 -0
- package/skills/trace/SKILL.md +85 -0
- package/skills/usecases/SKILL.md +91 -0
- 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.
|