@esimplicitylabs/katalyst-xspec 0.6.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 (29) hide show
  1. package/LICENSE +7 -0
  2. package/README.md +69 -0
  3. package/bin/katalyst-xspec.cjs +54 -0
  4. package/cli/init.cjs +679 -0
  5. package/cli/stubs.cjs +365 -0
  6. package/cli/upgrade.cjs +1014 -0
  7. package/dist/chunk-ACAXOGKZ.js +1611 -0
  8. package/dist/index.d.ts +881 -0
  9. package/dist/index.js +1091 -0
  10. package/dist/steps/index.d.ts +151 -0
  11. package/dist/steps/index.js +50 -0
  12. package/package.json +80 -0
  13. package/scripts/postinstall.cjs +85 -0
  14. package/skills/katalyst-bdd-architecture/SKILL.md +517 -0
  15. package/skills/katalyst-bdd-architecture/references/adapters.md +310 -0
  16. package/skills/katalyst-bdd-architecture/references/custom-steps.md +360 -0
  17. package/skills/katalyst-bdd-architecture/references/ports.md +256 -0
  18. package/skills/katalyst-bdd-create-test/SKILL.md +366 -0
  19. package/skills/katalyst-bdd-create-test/references/api-patterns.md +371 -0
  20. package/skills/katalyst-bdd-create-test/references/hybrid-patterns.md +420 -0
  21. package/skills/katalyst-bdd-create-test/references/tui-patterns.md +458 -0
  22. package/skills/katalyst-bdd-create-test/references/ui-patterns.md +415 -0
  23. package/skills/katalyst-bdd-quickstart/SKILL.md +292 -0
  24. package/skills/katalyst-bdd-step-reference/SKILL.md +147 -0
  25. package/skills/katalyst-bdd-step-reference/references/api-steps.md +247 -0
  26. package/skills/katalyst-bdd-step-reference/references/shared-steps.md +340 -0
  27. package/skills/katalyst-bdd-step-reference/references/tui-steps.md +483 -0
  28. package/skills/katalyst-bdd-step-reference/references/ui-steps.md +521 -0
  29. package/skills/katalyst-bdd-troubleshooting/SKILL.md +449 -0
package/cli/stubs.cjs ADDED
@@ -0,0 +1,365 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /**
5
+ * Step Stub Generator
6
+ *
7
+ * Parses bddgen output to identify missing step definitions and generates
8
+ * stub implementations in a single file: features/steps/generated-stubs.ts
9
+ *
10
+ * Usage:
11
+ * npx katalyst-xspec stubs
12
+ * npx katalyst-xspec stubs --output custom-stubs.ts
13
+ * npx katalyst-xspec stubs --dry-run
14
+ */
15
+
16
+ const { execSync } = require('child_process');
17
+ const fs = require('fs');
18
+ const path = require('path');
19
+
20
+ function parseArgs(args) {
21
+ const options = {
22
+ output: 'features/steps/generated-stubs.ts',
23
+ dryRun: false,
24
+ help: false,
25
+ verbose: false,
26
+ };
27
+
28
+ for (let i = 0; i < args.length; i++) {
29
+ const arg = args[i];
30
+ if (arg === '--output' || arg === '-o') {
31
+ options.output = args[++i];
32
+ } else if (arg === '--dry-run') {
33
+ options.dryRun = true;
34
+ } else if (arg === '--verbose' || arg === '-v') {
35
+ options.verbose = true;
36
+ } else if (arg === '--help' || arg === '-h') {
37
+ options.help = true;
38
+ }
39
+ }
40
+
41
+ return options;
42
+ }
43
+
44
+ function showHelp() {
45
+ console.log(`
46
+ Usage: npx katalyst-xspec stubs [options]
47
+
48
+ Generate step definition stubs for missing steps detected by bddgen.
49
+
50
+ Options:
51
+ -o, --output <file> Output file (default: features/steps/generated-stubs.ts)
52
+ --dry-run Show what would be generated without writing
53
+ -v, --verbose Show detailed output
54
+ -h, --help Show this help message
55
+
56
+ Examples:
57
+ npx katalyst-xspec stubs # Generate stubs
58
+ npx katalyst-xspec stubs --dry-run # Preview what would be generated
59
+ npx katalyst-xspec stubs -o my-stubs.ts # Custom output file
60
+ `);
61
+ }
62
+
63
+ /**
64
+ * Run bddgen and capture output including missing steps
65
+ */
66
+ function runBddgenAndCapture() {
67
+ try {
68
+ // Run bddgen and capture both stdout and stderr
69
+ const result = execSync('npx bddgen 2>&1', {
70
+ encoding: 'utf8',
71
+ maxBuffer: 10 * 1024 * 1024, // 10MB buffer
72
+ });
73
+ return { output: result, success: true };
74
+ } catch (error) {
75
+ // bddgen exits with non-zero when steps are missing, but we still get output
76
+ return { output: error.stdout || error.message, success: false };
77
+ }
78
+ }
79
+
80
+ /**
81
+ * Parse bddgen output to extract missing step definitions
82
+ *
83
+ * bddgen outputs missing steps in format:
84
+ * Given('the ClawMarket API is available', async ({}) => {
85
+ * When('I do something with {string}', async ({}, arg0: string) => {
86
+ */
87
+ function parseMissingSteps(output) {
88
+ const steps = [];
89
+ const seen = new Set(); // Deduplicate
90
+
91
+ // Pattern 1: Full step definition format from bddgen
92
+ // Given('pattern', async ({}, ...) => {
93
+ const fullPattern = /(Given|When|Then)\s*\(\s*['"`]([^'"`]+)['"`]\s*,\s*async\s*\(\s*\{[^}]*\}(?:\s*,\s*[^)]+)?\s*\)\s*=>\s*\{/g;
94
+
95
+ let match;
96
+ while ((match = fullPattern.exec(output)) !== null) {
97
+ const keyword = match[1];
98
+ const pattern = match[2];
99
+ const key = `${keyword}:${pattern}`;
100
+
101
+ if (!seen.has(key)) {
102
+ seen.add(key);
103
+ steps.push({ keyword, pattern });
104
+ }
105
+ }
106
+
107
+ // Pattern 2: Simpler format (just in case)
108
+ // Given('pattern'
109
+ const simplePattern = /(Given|When|Then)\s*\(\s*['"`]([^'"`]+)['"`]/g;
110
+
111
+ while ((match = simplePattern.exec(output)) !== null) {
112
+ const keyword = match[1];
113
+ const pattern = match[2];
114
+ const key = `${keyword}:${pattern}`;
115
+
116
+ if (!seen.has(key)) {
117
+ seen.add(key);
118
+ steps.push({ keyword, pattern });
119
+ }
120
+ }
121
+
122
+ return steps;
123
+ }
124
+
125
+ /**
126
+ * Extract Cucumber expression parameters from a step pattern
127
+ * @param {string} pattern - e.g., "I click the {string} button"
128
+ * @returns {Array<{name: string, type: string}>}
129
+ */
130
+ function extractParameters(pattern) {
131
+ const params = [];
132
+ const paramRegex = /\{([^}]+)\}/g;
133
+ let match;
134
+ let index = 0;
135
+
136
+ while ((match = paramRegex.exec(pattern)) !== null) {
137
+ const type = match[1];
138
+ let tsType = 'string';
139
+ let name = `arg${index}`;
140
+
141
+ switch (type) {
142
+ case 'int':
143
+ tsType = 'number';
144
+ name = `num${index}`;
145
+ break;
146
+ case 'float':
147
+ tsType = 'number';
148
+ name = `float${index}`;
149
+ break;
150
+ case 'string':
151
+ tsType = 'string';
152
+ name = `str${index}`;
153
+ break;
154
+ case 'word':
155
+ tsType = 'string';
156
+ name = `word${index}`;
157
+ break;
158
+ default:
159
+ // Custom type or any
160
+ tsType = 'string';
161
+ name = `${type}${index}`;
162
+ }
163
+
164
+ params.push({ name, type: tsType, cucumberType: type });
165
+ index++;
166
+ }
167
+
168
+ return params;
169
+ }
170
+
171
+ /**
172
+ * Generate TypeScript stub for a single step
173
+ */
174
+ function generateStepStub(step) {
175
+ const params = extractParameters(step.pattern);
176
+
177
+ // Build parameter list for the async function
178
+ let paramList = '{ world }';
179
+ if (params.length > 0) {
180
+ const paramDefs = params.map(p => `${p.name}: ${p.type}`).join(', ');
181
+ paramList = `{ world }, ${paramDefs}`;
182
+ }
183
+
184
+ // Build the step definition
185
+ const stub = `
186
+ // TODO: Implement this step
187
+ // Pattern: ${step.pattern}
188
+ ${step.keyword}('${step.pattern.replace(/'/g, "\\'")}', async (${paramList}) => {
189
+ throw new Error('Step not implemented: ${step.pattern.replace(/'/g, "\\'")}');
190
+ });`;
191
+
192
+ return stub;
193
+ }
194
+
195
+ /**
196
+ * Generate the complete stubs file
197
+ */
198
+ function generateStubsFile(steps) {
199
+ // Group steps by keyword for organization
200
+ const givenSteps = steps.filter(s => s.keyword === 'Given');
201
+ const whenSteps = steps.filter(s => s.keyword === 'When');
202
+ const thenSteps = steps.filter(s => s.keyword === 'Then');
203
+
204
+ let content = `/**
205
+ * Generated Step Stubs
206
+ *
207
+ * This file was auto-generated by katalyst-xspec stubs.
208
+ * It contains stub implementations for all missing step definitions.
209
+ *
210
+ * Instructions:
211
+ * 1. Review each stub and implement the actual logic
212
+ * 2. Move implemented steps to appropriate step files (e.g., my-steps.ts)
213
+ * 3. Delete this file or the implemented stubs
214
+ * 4. Add import to steps.ts: import './generated-stubs.js';
215
+ *
216
+ * Generated: ${new Date().toISOString()}
217
+ * Total stubs: ${steps.length}
218
+ */
219
+
220
+ import { createBdd } from 'playwright-bdd';
221
+ import { test } from './fixtures.js';
222
+
223
+ const { Given, When, Then } = createBdd(test);
224
+ `;
225
+
226
+ // Add Given steps
227
+ if (givenSteps.length > 0) {
228
+ content += `
229
+ // ============================================================================
230
+ // GIVEN STEPS (${givenSteps.length})
231
+ // ============================================================================
232
+ `;
233
+ for (const step of givenSteps) {
234
+ content += generateStepStub(step);
235
+ }
236
+ }
237
+
238
+ // Add When steps
239
+ if (whenSteps.length > 0) {
240
+ content += `
241
+
242
+ // ============================================================================
243
+ // WHEN STEPS (${whenSteps.length})
244
+ // ============================================================================
245
+ `;
246
+ for (const step of whenSteps) {
247
+ content += generateStepStub(step);
248
+ }
249
+ }
250
+
251
+ // Add Then steps
252
+ if (thenSteps.length > 0) {
253
+ content += `
254
+
255
+ // ============================================================================
256
+ // THEN STEPS (${thenSteps.length})
257
+ // ============================================================================
258
+ `;
259
+ for (const step of thenSteps) {
260
+ content += generateStepStub(step);
261
+ }
262
+ }
263
+
264
+ content += '\n';
265
+
266
+ return content;
267
+ }
268
+
269
+ /**
270
+ * Main function
271
+ */
272
+ async function main() {
273
+ const options = parseArgs(process.argv.slice(2));
274
+
275
+ if (options.help) {
276
+ showHelp();
277
+ process.exit(0);
278
+ }
279
+
280
+ console.log('Step Stub Generator');
281
+ console.log('═'.repeat(50));
282
+ console.log('');
283
+
284
+ // Run bddgen to get missing steps
285
+ console.log('Running bddgen to detect missing steps...');
286
+ const { output, success } = runBddgenAndCapture();
287
+
288
+ if (options.verbose) {
289
+ console.log('');
290
+ console.log('bddgen output:');
291
+ console.log('─'.repeat(50));
292
+ console.log(output);
293
+ console.log('─'.repeat(50));
294
+ }
295
+
296
+ // Parse missing steps
297
+ const steps = parseMissingSteps(output);
298
+
299
+ if (steps.length === 0) {
300
+ console.log('');
301
+ if (success) {
302
+ console.log('No missing steps detected. All steps are implemented!');
303
+ } else {
304
+ console.log('Could not detect missing steps from bddgen output.');
305
+ console.log('Try running with --verbose to see the full output.');
306
+ }
307
+ process.exit(0);
308
+ }
309
+
310
+ console.log(`Found ${steps.length} missing step(s)`);
311
+ console.log('');
312
+
313
+ // Group by keyword for summary
314
+ const givenCount = steps.filter(s => s.keyword === 'Given').length;
315
+ const whenCount = steps.filter(s => s.keyword === 'When').length;
316
+ const thenCount = steps.filter(s => s.keyword === 'Then').length;
317
+
318
+ console.log('Summary:');
319
+ console.log(` Given: ${givenCount}`);
320
+ console.log(` When: ${whenCount}`);
321
+ console.log(` Then: ${thenCount}`);
322
+ console.log('');
323
+
324
+ // Generate stub file content
325
+ const stubContent = generateStubsFile(steps);
326
+
327
+ if (options.dryRun) {
328
+ console.log('Generated content (dry run):');
329
+ console.log('─'.repeat(50));
330
+ console.log(stubContent);
331
+ console.log('─'.repeat(50));
332
+ console.log('');
333
+ console.log('Run without --dry-run to write to file.');
334
+ process.exit(0);
335
+ }
336
+
337
+ // Write the file
338
+ const outputPath = path.resolve(process.cwd(), options.output);
339
+ const outputDir = path.dirname(outputPath);
340
+
341
+ // Ensure directory exists
342
+ if (!fs.existsSync(outputDir)) {
343
+ fs.mkdirSync(outputDir, { recursive: true });
344
+ }
345
+
346
+ fs.writeFileSync(outputPath, stubContent);
347
+
348
+ console.log(`Generated stubs written to: ${options.output}`);
349
+ console.log('');
350
+ console.log('Next steps:');
351
+ console.log(' 1. Add to steps.ts: import \'./generated-stubs.js\';');
352
+ console.log(' 2. Implement each stub (replace throw with actual logic)');
353
+ console.log(' 3. Move implemented steps to appropriate files');
354
+ console.log(' 4. Run: npm run gen');
355
+ console.log(' 5. Run: npm test');
356
+ }
357
+
358
+ if (require.main === module) {
359
+ main().catch((err) => {
360
+ console.error('Error:', err.message);
361
+ process.exit(1);
362
+ });
363
+ }
364
+
365
+ module.exports = { main };