@sun-asterisk/sungen 2.2.3 → 2.3.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 (78) hide show
  1. package/README.md +4 -4
  2. package/dist/cli/commands/update.d.ts +3 -0
  3. package/dist/cli/commands/update.d.ts.map +1 -0
  4. package/dist/cli/commands/update.js +21 -0
  5. package/dist/cli/commands/update.js.map +1 -0
  6. package/dist/cli/index.js +3 -1
  7. package/dist/cli/index.js.map +1 -1
  8. package/dist/generators/gherkin-parser/index.d.ts +2 -0
  9. package/dist/generators/gherkin-parser/index.d.ts.map +1 -1
  10. package/dist/generators/gherkin-parser/index.js +16 -2
  11. package/dist/generators/gherkin-parser/index.js.map +1 -1
  12. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/attribute-assertion.hbs +3 -0
  13. package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/locator-base.hbs +12 -1
  14. package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/locator.hbs +12 -1
  15. package/dist/generators/test-generator/patterns/assertion-patterns.d.ts.map +1 -1
  16. package/dist/generators/test-generator/patterns/assertion-patterns.js +12 -0
  17. package/dist/generators/test-generator/patterns/assertion-patterns.js.map +1 -1
  18. package/dist/generators/test-generator/patterns/index.d.ts +9 -0
  19. package/dist/generators/test-generator/patterns/index.d.ts.map +1 -1
  20. package/dist/generators/test-generator/patterns/index.js +32 -0
  21. package/dist/generators/test-generator/patterns/index.js.map +1 -1
  22. package/dist/generators/test-generator/patterns/table-patterns.d.ts.map +1 -1
  23. package/dist/generators/test-generator/patterns/table-patterns.js +8 -5
  24. package/dist/generators/test-generator/patterns/table-patterns.js.map +1 -1
  25. package/dist/orchestrator/ai-rules-updater.d.ts +13 -0
  26. package/dist/orchestrator/ai-rules-updater.d.ts.map +1 -0
  27. package/dist/orchestrator/ai-rules-updater.js +157 -0
  28. package/dist/orchestrator/ai-rules-updater.js.map +1 -0
  29. package/dist/orchestrator/project-initializer.d.ts.map +1 -1
  30. package/dist/orchestrator/project-initializer.js +2 -27
  31. package/dist/orchestrator/project-initializer.js.map +1 -1
  32. package/dist/orchestrator/screen-manager.d.ts +1 -0
  33. package/dist/orchestrator/screen-manager.d.ts.map +1 -1
  34. package/dist/orchestrator/screen-manager.js +70 -3
  35. package/dist/orchestrator/screen-manager.js.map +1 -1
  36. package/dist/orchestrator/templates/ai-instructions/claude-cmd-add-screen.md +18 -9
  37. package/dist/orchestrator/templates/ai-instructions/claude-cmd-make-tc.md +11 -4
  38. package/dist/orchestrator/templates/ai-instructions/claude-cmd-make-test.md +9 -11
  39. package/dist/orchestrator/templates/ai-instructions/claude-skill-gherkin-syntax.md +27 -8
  40. package/dist/orchestrator/templates/ai-instructions/claude-skill-selector-fix.md +91 -25
  41. package/dist/orchestrator/templates/ai-instructions/claude-skill-tc-generation.md +124 -71
  42. package/dist/orchestrator/templates/ai-instructions/copilot-cmd-add-screen.md +13 -5
  43. package/dist/orchestrator/templates/ai-instructions/copilot-cmd-make-tc.md +12 -4
  44. package/dist/orchestrator/templates/ai-instructions/copilot-cmd-make-test.md +9 -11
  45. package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-gherkin-syntax.md +27 -8
  46. package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-selector-fix.md +72 -31
  47. package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-tc-generation.md +124 -72
  48. package/dist/orchestrator/templates/readme.md +13 -8
  49. package/package.json +1 -1
  50. package/src/cli/commands/update.ts +18 -0
  51. package/src/cli/index.ts +3 -1
  52. package/src/generators/gherkin-parser/index.ts +19 -2
  53. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/attribute-assertion.hbs +3 -0
  54. package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator-base.hbs +12 -1
  55. package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator.hbs +12 -1
  56. package/src/generators/test-generator/patterns/assertion-patterns.ts +13 -0
  57. package/src/generators/test-generator/patterns/index.ts +41 -0
  58. package/src/generators/test-generator/patterns/table-patterns.ts +8 -5
  59. package/src/orchestrator/ai-rules-updater.ts +139 -0
  60. package/src/orchestrator/project-initializer.ts +2 -32
  61. package/src/orchestrator/screen-manager.ts +72 -3
  62. package/src/orchestrator/templates/ai-instructions/claude-cmd-add-screen.md +18 -9
  63. package/src/orchestrator/templates/ai-instructions/claude-cmd-make-tc.md +11 -4
  64. package/src/orchestrator/templates/ai-instructions/claude-cmd-make-test.md +9 -11
  65. package/src/orchestrator/templates/ai-instructions/claude-skill-gherkin-syntax.md +27 -8
  66. package/src/orchestrator/templates/ai-instructions/claude-skill-selector-fix.md +91 -25
  67. package/src/orchestrator/templates/ai-instructions/claude-skill-tc-generation.md +124 -71
  68. package/src/orchestrator/templates/ai-instructions/copilot-cmd-add-screen.md +13 -5
  69. package/src/orchestrator/templates/ai-instructions/copilot-cmd-make-tc.md +12 -4
  70. package/src/orchestrator/templates/ai-instructions/copilot-cmd-make-test.md +9 -11
  71. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-gherkin-syntax.md +27 -8
  72. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-selector-fix.md +72 -31
  73. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-tc-generation.md +124 -72
  74. package/src/orchestrator/templates/readme.md +13 -8
  75. package/docs/gherkin standards/gherkin-core-standard.md +0 -431
  76. package/docs/gherkin standards/gherkin-core-standard.vi.md +0 -399
  77. package/docs/gherkin-dictionary.md +0 -1126
  78. package/docs/makeauth.md +0 -225
@@ -72,6 +72,14 @@ export class PatternRegistry {
72
72
  // Prefer resolver (framework-agnostic) over generator (legacy)
73
73
  if (pattern.resolver) {
74
74
  const resolved = pattern.resolver(step, context);
75
+
76
+ // Auto-inject parent scoping if step has parentRef
77
+ if (step.parentRef && step.parentType) {
78
+ resolved.data.parentLocator = PatternRegistry.resolveParentLocator(
79
+ step.parentRef, step.parentType, context
80
+ );
81
+ }
82
+
75
83
  const code = context.templateEngine.renderStep(resolved.templateName, resolved.data);
76
84
  return {
77
85
  code,
@@ -86,6 +94,39 @@ export class PatternRegistry {
86
94
  return null;
87
95
  }
88
96
 
97
+ /**
98
+ * Resolve parent scoping to a Playwright locator string.
99
+ * Tries YAML lookup first, falls back to auto-infer from parentType.
100
+ *
101
+ * Parent type → Playwright role:
102
+ * table → 'table', list → 'list', section → 'region',
103
+ * dialog → 'dialog', form → 'form'
104
+ */
105
+ private static resolveParentLocator(
106
+ parentRef: string, parentType: string, context: PatternContext
107
+ ): string {
108
+ // Try resolving from selectors YAML
109
+ try {
110
+ const resolved = context.selectorResolver.resolveSelector(
111
+ parentRef, context.featureName, parentType, 0
112
+ );
113
+ return context.renderLocator(resolved);
114
+ } catch {
115
+ // Fallback: auto-infer from parentType + parentRef as accessible name
116
+ }
117
+
118
+ const roleMap: Record<string, string> = {
119
+ table: 'table',
120
+ list: 'list',
121
+ section: 'region',
122
+ dialog: 'dialog',
123
+ form: 'form',
124
+ };
125
+ const role = roleMap[parentType] || parentType;
126
+ const escapedName = parentRef.replace(/'/g, "\\'");
127
+ return `page.getByRole('${role}', { name: '${escapedName}' })`;
128
+ }
129
+
89
130
  /**
90
131
  * Check if step matches a pattern matcher
91
132
  */
@@ -6,11 +6,12 @@
6
6
  import { StepPattern } from './types';
7
7
 
8
8
  export const tablePatterns: StepPattern[] = [
9
- // "User see [Users] table has {{count}} rows" — must have "rows" AFTER {{data}}
9
+ // "User see [Users] table with {{count}} rows" (preferred)
10
+ // Also accepts: "User see [Users] table has {{count}} rows" (backward compat)
10
11
  {
11
12
  name: 'table-row-count',
12
13
  matcher: (step) => {
13
- return /\btable\s+has\b/i.test(step.text) &&
14
+ return /\btable\s+(?:has|with)\b/i.test(step.text) &&
14
15
  /\}\}\s*rows?\b/i.test(step.text) &&
15
16
  !!step.dataRef;
16
17
  },
@@ -29,7 +30,8 @@ export const tablePatterns: StepPattern[] = [
29
30
  priority: 16,
30
31
  },
31
32
 
32
- // "User see [Users] table has [Email] column"
33
+ // "User see [Users] table has [Email] column" (backward compat)
34
+ // Preferred: "User see [Email] column in [Users] table" (parent scoping)
33
35
  {
34
36
  name: 'table-column-exists',
35
37
  matcher: (step) => {
@@ -152,11 +154,12 @@ export const tablePatterns: StepPattern[] = [
152
154
  priority: 17,
153
155
  },
154
156
 
155
- // "User see [Users] table has row with {{name}}"
157
+ // "User see [Users] table row with {{name}}" (preferred)
158
+ // Also accepts: "User see [Users] table has row with {{name}}" (backward compat)
156
159
  {
157
160
  name: 'table-row-exists',
158
161
  matcher: (step) => {
159
- return /\btable\s+has\s+row\s+with\b/i.test(step.text) && !!step.dataRef;
162
+ return /\btable\s+(?:has\s+)?row\s+with\b/i.test(step.text) && !!step.dataRef;
160
163
  },
161
164
  resolver: (step, context) => {
162
165
  const resolved = context.selectorResolver.resolveSelector(
@@ -0,0 +1,139 @@
1
+ /**
2
+ * AI Rules Updater
3
+ * Updates AI rules, commands, and skills from bundled templates.
4
+ * Used by `sungen update` command.
5
+ */
6
+
7
+ import * as fs from 'fs';
8
+ import * as path from 'path';
9
+
10
+ // File mapping: [templateFile, outputPath]
11
+ // Shared with project-initializer.ts
12
+ export const AI_RULES_FILE_MAPPING: [string, string][] = [
13
+ // Config
14
+ ['claude-config.md', 'CLAUDE.md'],
15
+ ['copilot-config.md', '.github/copilot-instructions.md'],
16
+
17
+ // Commands — Claude Code
18
+ ['claude-cmd-add-screen.md', '.claude/commands/sungen/add-screen.md'],
19
+ ['claude-cmd-make-tc.md', '.claude/commands/sungen/make-tc.md'],
20
+ ['claude-cmd-make-test.md', '.claude/commands/sungen/make-test.md'],
21
+
22
+ // Commands — GitHub Copilot
23
+ ['copilot-cmd-add-screen.md', '.github/prompts/sungen-add-screen.prompt.md'],
24
+ ['copilot-cmd-make-tc.md', '.github/prompts/sungen-make-tc.prompt.md'],
25
+ ['copilot-cmd-make-test.md', '.github/prompts/sungen-make-test.prompt.md'],
26
+
27
+ // Skills — Claude Code
28
+ ['claude-skill-gherkin-syntax.md', '.claude/skills/sungen-gherkin-syntax/SKILL.md'],
29
+ ['claude-skill-selector-keys.md', '.claude/skills/sungen-selector-keys/SKILL.md'],
30
+ ['claude-skill-error-mapping.md', '.claude/skills/sungen-error-mapping/SKILL.md'],
31
+ ['claude-skill-tc-generation.md', '.claude/skills/sungen-tc-generation/SKILL.md'],
32
+ ['claude-skill-selector-fix.md', '.claude/skills/sungen-selector-fix/SKILL.md'],
33
+
34
+ // Skills — GitHub Copilot
35
+ ['github-skill-sungen-gherkin-syntax.md', '.github/skills/sungen-gherkin-syntax/SKILL.md'],
36
+ ['github-skill-sungen-selector-keys.md', '.github/skills/sungen-selector-keys/SKILL.md'],
37
+ ['github-skill-sungen-error-mapping.md', '.github/skills/sungen-error-mapping/SKILL.md'],
38
+ ['github-skill-sungen-tc-generation.md', '.github/skills/sungen-tc-generation/SKILL.md'],
39
+ ['github-skill-sungen-selector-fix.md', '.github/skills/sungen-selector-fix/SKILL.md'],
40
+ ];
41
+
42
+ export class AIRulesUpdater {
43
+ private cwd: string;
44
+ private aiTemplateDir: string;
45
+
46
+ constructor(cwd: string) {
47
+ this.cwd = cwd;
48
+ this.aiTemplateDir = path.join(__dirname, 'templates', 'ai-instructions');
49
+ }
50
+
51
+ async update(dryRun: boolean): Promise<void> {
52
+ console.log('🔄 Updating AI rules, commands, and skills...\n');
53
+
54
+ const updated: string[] = [];
55
+ const created: string[] = [];
56
+ const unchanged: string[] = [];
57
+ const missing: string[] = [];
58
+
59
+ for (const [templateFile, outputRelPath] of AI_RULES_FILE_MAPPING) {
60
+ const templatePath = path.join(this.aiTemplateDir, templateFile);
61
+ const outputPath = path.join(this.cwd, outputRelPath);
62
+
63
+ if (!fs.existsSync(templatePath)) {
64
+ missing.push(templateFile);
65
+ continue;
66
+ }
67
+
68
+ const newContent = fs.readFileSync(templatePath, 'utf-8');
69
+
70
+ if (fs.existsSync(outputPath)) {
71
+ const currentContent = fs.readFileSync(outputPath, 'utf-8');
72
+ if (currentContent === newContent) {
73
+ unchanged.push(outputRelPath);
74
+ continue;
75
+ }
76
+
77
+ if (!dryRun) {
78
+ fs.writeFileSync(outputPath, newContent, 'utf-8');
79
+ }
80
+ updated.push(outputRelPath);
81
+ } else {
82
+ if (!dryRun) {
83
+ const outputDir = path.dirname(outputPath);
84
+ if (!fs.existsSync(outputDir)) {
85
+ fs.mkdirSync(outputDir, { recursive: true });
86
+ }
87
+ fs.writeFileSync(outputPath, newContent, 'utf-8');
88
+ }
89
+ created.push(outputRelPath);
90
+ }
91
+ }
92
+
93
+ // Print results
94
+ if (dryRun) {
95
+ console.log('📋 Dry run — no files changed\n');
96
+ }
97
+
98
+ if (updated.length > 0) {
99
+ console.log(`✏️ Updated (${updated.length}):`);
100
+ for (const f of updated) {
101
+ console.log(` ${f}`);
102
+ }
103
+ console.log();
104
+ }
105
+
106
+ if (created.length > 0) {
107
+ console.log(`✨ Created (${created.length}):`);
108
+ for (const f of created) {
109
+ console.log(` ${f}`);
110
+ }
111
+ console.log();
112
+ }
113
+
114
+ if (unchanged.length > 0) {
115
+ console.log(`✅ Already up to date (${unchanged.length}):`);
116
+ for (const f of unchanged) {
117
+ console.log(` ${f}`);
118
+ }
119
+ console.log();
120
+ }
121
+
122
+ if (missing.length > 0) {
123
+ console.log(`⚠️ Template not found (${missing.length}):`);
124
+ for (const f of missing) {
125
+ console.log(` ${f}`);
126
+ }
127
+ console.log();
128
+ }
129
+
130
+ const totalChanges = updated.length + created.length;
131
+ if (totalChanges === 0) {
132
+ console.log('All AI rules are up to date. No changes needed.');
133
+ } else if (dryRun) {
134
+ console.log(`${totalChanges} file(s) would be changed. Run without --dry-run to apply.`);
135
+ } else {
136
+ console.log(`${totalChanges} file(s) updated successfully.`);
137
+ }
138
+ }
139
+ }
@@ -6,6 +6,7 @@
6
6
  import * as fs from 'fs';
7
7
  import * as path from 'path';
8
8
  import { execSync } from 'child_process';
9
+ import { AI_RULES_FILE_MAPPING } from './ai-rules-updater';
9
10
 
10
11
  export class ProjectInitializer {
11
12
  private baseCwd: string;
@@ -250,38 +251,7 @@ export class ProjectInitializer {
250
251
  private createAIRules(): void {
251
252
  const aiTemplateDir = path.join(__dirname, 'templates', 'ai-instructions');
252
253
 
253
- // File mapping: [templateFile, outputPath]
254
- const fileMapping: [string, string][] = [
255
- // Config
256
- ['claude-config.md', 'CLAUDE.md'],
257
- ['copilot-config.md', '.github/copilot-instructions.md'],
258
-
259
- // Commands — Claude Code
260
- ['claude-cmd-add-screen.md', '.claude/commands/sungen/add-screen.md'],
261
- ['claude-cmd-make-tc.md', '.claude/commands/sungen/make-tc.md'],
262
- ['claude-cmd-make-test.md', '.claude/commands/sungen/make-test.md'],
263
-
264
- // Commands — GitHub Copilot
265
- ['copilot-cmd-add-screen.md', '.github/prompts/sungen-add-screen.prompt.md'],
266
- ['copilot-cmd-make-tc.md', '.github/prompts/sungen-make-tc.prompt.md'],
267
- ['copilot-cmd-make-test.md', '.github/prompts/sungen-make-test.prompt.md'],
268
-
269
- // Skills — Claude Code
270
- ['claude-skill-gherkin-syntax.md', '.claude/skills/sungen-gherkin-syntax/SKILL.md'],
271
- ['claude-skill-selector-keys.md', '.claude/skills/sungen-selector-keys/SKILL.md'],
272
- ['claude-skill-error-mapping.md', '.claude/skills/sungen-error-mapping/SKILL.md'],
273
- ['claude-skill-tc-generation.md', '.claude/skills/sungen-tc-generation/SKILL.md'],
274
- ['claude-skill-selector-fix.md', '.claude/skills/sungen-selector-fix/SKILL.md'],
275
-
276
- // Skills — GitHub Copilot (separate copies with Copilot-friendly descriptions)
277
- ['github-skill-sungen-gherkin-syntax.md', '.github/skills/sungen-gherkin-syntax/SKILL.md'],
278
- ['github-skill-sungen-selector-keys.md', '.github/skills/sungen-selector-keys/SKILL.md'],
279
- ['github-skill-sungen-error-mapping.md', '.github/skills/sungen-error-mapping/SKILL.md'],
280
- ['github-skill-sungen-tc-generation.md', '.github/skills/sungen-tc-generation/SKILL.md'],
281
- ['github-skill-sungen-selector-fix.md', '.github/skills/sungen-selector-fix/SKILL.md'],
282
- ];
283
-
284
- for (const [templateFile, outputRelPath] of fileMapping) {
254
+ for (const [templateFile, outputRelPath] of AI_RULES_FILE_MAPPING) {
285
255
  const outputPath = path.join(this.cwd, outputRelPath);
286
256
 
287
257
  if (fs.existsSync(outputPath)) {
@@ -47,6 +47,8 @@ export class ScreenManager {
47
47
  const featuresDir = path.join(screenDir, 'features');
48
48
  const selectorsDir = path.join(screenDir, 'selectors');
49
49
  const testDataDir = path.join(screenDir, 'test-data');
50
+ const requirementsDir = path.join(screenDir, 'requirements');
51
+ const requirementsUiDir = path.join(requirementsDir, 'ui');
50
52
 
51
53
  // File paths
52
54
  const featurePath = path.join(featuresDir, `${filename}.feature`);
@@ -66,6 +68,7 @@ export class ScreenManager {
66
68
  fs.mkdirSync(featuresDir, { recursive: true });
67
69
  fs.mkdirSync(selectorsDir, { recursive: true });
68
70
  fs.mkdirSync(testDataDir, { recursive: true });
71
+ fs.mkdirSync(requirementsUiDir, { recursive: true });
69
72
  } catch (error) {
70
73
  console.error(`Error: Failed to create directories`);
71
74
  console.error(` ${error instanceof Error ? error.message : String(error)}`);
@@ -111,15 +114,27 @@ export class ScreenManager {
111
114
  ].join('\n'), 'utf-8');
112
115
  }
113
116
 
117
+ // Generate requirements spec.md (only on first screen creation)
118
+ const specPath = path.join(requirementsDir, 'spec.md');
119
+ if (!fs.existsSync(specPath)) {
120
+ fs.writeFileSync(specPath, this.generateSpecTemplate(options, screenName), 'utf-8');
121
+ }
122
+
114
123
  // Display success
115
124
  console.log(`Created files:`);
116
125
  console.log(` ${path.relative(this.cwd, featurePath)}`);
117
126
  console.log(` ${path.relative(this.cwd, selectorPath)}`);
118
- console.log(` ${path.relative(this.cwd, testDataPath)}\n`);
127
+ console.log(` ${path.relative(this.cwd, testDataPath)}`);
128
+ if (isFirstFile) {
129
+ console.log(` ${path.relative(this.cwd, specPath)}`);
130
+ console.log(` ${path.relative(this.cwd, requirementsUiDir)}/`);
131
+ }
132
+ console.log('');
119
133
 
120
134
  console.log('Next steps:');
121
- console.log(` 1. Use AI (Copilot/Claude) to generate Gherkin + selectors from the live page`);
122
- console.log(` 2. Fill in test-data values`);
135
+ console.log(` 1. Fill requirements/spec.md with screen spec (fields, validation, business rules)`);
136
+ console.log(` Optionally add UI designs to requirements/ui/ (screenshots, mockups)`);
137
+ console.log(` 2. Generate test cases: /sungen:make-tc ${screenName} (or /sungen-make-tc)`);
123
138
  console.log(` 3. Compile: sungen generate --screen ${screenName}`);
124
139
  console.log(` 4. Run: npx playwright test\n`);
125
140
  }
@@ -150,6 +165,60 @@ export class ScreenManager {
150
165
  return this.normalizeScreenName(lastSegment);
151
166
  }
152
167
 
168
+ private generateSpecTemplate(options: ScreenOptions, screenName: string): string {
169
+ const pagePath = options.path || `/${screenName}`;
170
+ return `# ${options.name} Screen Specification
171
+
172
+ ## Overview
173
+ - **URL Path:** ${pagePath}
174
+ - **Auth Required:** no
175
+ - **Platform:** web
176
+
177
+ ## Sections
178
+
179
+ ### Section: [Section Name]
180
+ - **Type:** form | table | list | card | tabs | modal | search | navigation
181
+ - **Description:** [Brief description of this section]
182
+
183
+ #### Fields
184
+ <!-- Remove this table if section has no input fields -->
185
+ | Field | Type | Required | Constraints | Default |
186
+ |-------|------|----------|-------------|---------|
187
+ | [Field Name] | input (text) | yes | max 255 | — |
188
+
189
+ #### Actions
190
+ | Action | Element | Behavior |
191
+ |--------|---------|----------|
192
+ | [Action Name] | button | [What happens on click] |
193
+
194
+ #### Validation Rules
195
+ <!-- Exact error messages help AI generate accurate assertions -->
196
+ | Condition | Error Message |
197
+ |-----------|---------------|
198
+ | Empty required field | "[Exact error message from UI]" |
199
+
200
+ #### States
201
+ | State | Condition | Visual |
202
+ |-------|-----------|--------|
203
+ | Default | Page load | [Default appearance] |
204
+ | Loading | After submit | [Loading indicator] |
205
+ | Error | Validation fail | [Error appearance] |
206
+ | Success | Action complete | [Success behavior] |
207
+
208
+ ## Business Rules
209
+ <!-- Rules that affect test logic: limits, permissions, conditions -->
210
+ - [Rule 1]
211
+
212
+ ## Accessibility
213
+ <!-- Tab order, aria-labels, screen reader behavior -->
214
+ - Tab order: [field1] → [field2] → [submit]
215
+
216
+ ## Notes
217
+ <!-- Edge cases, known issues, environment-specific behavior -->
218
+ - [Note 1]
219
+ `;
220
+ }
221
+
153
222
  private generateFeatureTemplate(options: ScreenOptions, filename: string): string {
154
223
  const screenName = this.normalizeScreenName(options.name);
155
224
  const featurePath = options.path || `/${screenName}`;
@@ -25,19 +25,28 @@ Run:
25
25
  sungen add --screen <screen> --path <path>
26
26
  ```
27
27
 
28
- ### 2. Create test cases
28
+ ### 2. Fill requirements (recommended)
29
+
30
+ Ask the user: "Would you like to fill in `requirements/spec.md` now? This helps generate higher quality test cases."
31
+
32
+ - If yes → open `qa/screens/<screen>/requirements/spec.md` and help the user fill sections, fields, validation rules, business rules, and states.
33
+ - If they have UI designs (screenshots, Figma exports, mockups) → suggest copying them to `requirements/ui/`.
34
+ - If no → proceed to step 3.
35
+
36
+ ### 3. Create test cases
29
37
 
30
38
  Ask the user: "Would you like to create test cases now?"
31
39
 
32
- If yes, delegate to `/sungen:make-tc <screen>` to handle:
33
- - Exploring the live page or analyzing static designs
34
- - Gathering test viewpoints (UI/UX, Validation, Logic, Security)
35
- - Generating the 3 files (feature, selectors, test-data)
40
+ If yes **you MUST use the Skill tool** to invoke `/sungen:make-tc <screen>`. This is critical because `make-tc` auto-loads the `sungen-gherkin-syntax` and `sungen-tc-generation` skills which contain the full Gherkin syntax rules, pattern shapes, viewpoint checklists, and output format. **Do NOT attempt to generate test cases yourself** — always invoke the Skill tool so these skills are properly loaded.
41
+
42
+ ```
43
+ Skill: make-tc
44
+ Args: <screen>
45
+ ```
36
46
 
37
- ### 3. Confirm
47
+ ### 4. Confirm
38
48
 
39
- Tell the user what was created and next steps:
40
- - If the page requires authentication, the user will be asked to log in via the MCP browser during `/sungen:make-tc`
41
- - Edit the generated files as needed
49
+ If the user declined test case creation, tell them next steps:
50
+ - Fill `requirements/spec.md` with screen specs (if not done)
42
51
  - Run `/sungen:make-tc <screen>` to create test cases
43
52
  - Run `/sungen:make-test <screen>` to generate selectors, compile, and run tests
@@ -17,9 +17,16 @@ Parse **screen** from `$ARGUMENTS`. If missing, ask the user.
17
17
 
18
18
  1. Verify `qa/screens/<screen>/` exists. If not → `/sungen:add-screen` first.
19
19
  2. Check if `.feature` file already has scenarios. If yes → use `AskUserQuestion` to ask the update mode (see `sungen-tc-generation` skill for details). If no → fresh creation.
20
- 3. Use `AskUserQuestion` to ask: **Live page** (explore via Playwright MCP) or **Static designs** (screenshots, Figma)? Explore accordingly (see CLAUDE.md for MCP rules).
21
- 4. Follow the `sungen-tc-generation` skill for section identification, viewpoint generation, and output format.
22
- 5. Generate or update `.feature` + `test-data.yaml` following `sungen-gherkin-syntax` and `sungen-tc-generation` skills.
23
- 6. Show summary next: `/sungen:make-test <screen>`
20
+ 3. **Read requirements** check `qa/screens/<screen>/requirements/`:
21
+ - If `spec.md` exists read it as PRIMARY source (sections, fields, validation rules, business rules, states).
22
+ - If `ui/` has images read them for visual context (layout, element positions, states).
23
+ - If `notes.md` existsread for edge cases and additional context.
24
+ - Summarize what you found in requirements and present to the user.
25
+ 4. **Explore page** (supplements requirements, or is primary source if no requirements):
26
+ - Use `AskUserQuestion` to ask: **Live page** (explore via Playwright MCP) or **Static designs** (screenshots, Figma)? Or **Skip** (if requirements are sufficient)?
27
+ - If exploring, verify and supplement requirements — flag any discrepancies found.
28
+ 5. Follow the `sungen-tc-generation` skill for section identification, viewpoint generation, and output format. When requirements exist, use the "Requirements-Driven Generation" strategy.
29
+ 6. Generate or update `.feature` + `test-data.yaml` following `sungen-gherkin-syntax` and `sungen-tc-generation` skills.
30
+ 7. Show summary → next: `/sungen:make-test <screen>`
24
31
 
25
32
  **No selectors.yaml** — selectors are generated during `/sungen:make-test`.
@@ -16,16 +16,14 @@ Parse **screen** from `$ARGUMENTS`. If missing, ask the user.
16
16
  ## Steps
17
17
 
18
18
  1. Verify `qa/screens/<screen>/` has `.feature` + `test-data.yaml`. If not → `/sungen:make-tc` first.
19
- 2. Generate `selectors.yaml` from live page using `sungen-selector-fix` and `sungen-selector-keys` skills.
20
- 3. Compile: `sungen generate --screen <screen>`
21
- 4. **Initial run** run ALL tests to get the full failure picture:
22
- `npx playwright test specs/generated/<screen>/<screen>.spec.ts --reporter=line`
23
- 5. **Batched fix loop** (max 5 attempts) — see `sungen-selector-fix` skill for details:
24
- - Group failures by root cause (same selector, same error type)
25
- - Fix selectors/test-data for the current batch
26
- - Recompile, then re-run ONLY the previously-failing tests (max 20 at a time via `--grep`)
27
- - If the batch passes, pick the next batch of remaining failures
28
- - Repeat until no failures remain or max attempts reached
29
- 6. **Final confirmation** — run ALL tests once to ensure no regressions.
19
+ 2. **Generate selectors** explore live page via MCP, build `selectors.yaml` using `sungen-selector-fix` and `sungen-selector-keys` skills.
20
+ 3. **Proactive validation** — verify EVERY selector against the live page using `browser_snapshot` + `browser_evaluate` BEFORE running any test. Fix mismatches immediately. See `sungen-selector-fix` skill "Proactive Selector Validation" section. Target: 80%+ issues fixed before first run.
21
+ 4. **Compile**: `sungen generate --screen <screen>`
22
+ 5. **Batched test run** — run tests in batches of 20 via `--grep`:
23
+ `npx playwright test specs/generated/<screen>/*.spec.ts --grep "VP-UI-001|...|VP-UI-020" --reporter=line`
24
+ - If failures in batch → group by root cause, fix, recompile, re-run only failing tests
25
+ - If batch passes move to next 20 tests
26
+ - Max 5 fix attempts per batch
27
+ 6. **Final confirmation** run ALL tests once to catch regressions.
30
28
  7. After 5 fix attempts still failing → ask user about direct `.spec.ts` fix.
31
29
  8. Show: pass/fail, attempt count, files changed.
@@ -7,12 +7,13 @@ user-invocable: false
7
7
  ## Standard Syntax
8
8
 
9
9
  ```
10
- [Keyword] User <Action> [Target Name] <Target Type> <with {{Value}}> <is State>
10
+ [Keyword] User <Action> [Target Name] <Target Type> <in [Parent Name] <Parent Type>> <with {{Value}}> <is State>
11
11
  ```
12
12
 
13
13
  - **Actor**: Always `User`, always active voice.
14
14
  - **Value**: `with {{snake_case}}` — never hardcode static data.
15
15
  - **State**: `is <keyword>` — never use `{{}}` for states.
16
+ - **Parent scope**: `in [Parent] parentType` — optional, only when page has 2+ similar blocks needing disambiguation.
16
17
 
17
18
  ## Keyword → Action Rules
18
19
 
@@ -112,11 +113,11 @@ User switch to [T] frame # enter iframe
112
113
  User switch to [main] frame # exit iframe
113
114
  ```
114
115
 
115
- ### Assertions (6 verify patterns)
116
+ ### Assertions (8 verify patterns)
116
117
 
117
118
  ```
118
119
  # 1. Visibility
119
- User see [T] message # visible (default)
120
+ User see [T] message # visible (default — NEVER add "is visible")
120
121
  User see [T] modal is hidden # hidden
121
122
 
122
123
  # 2. Text Content (exact full match — toHaveText)
@@ -140,26 +141,44 @@ User see [T] checkbox is checked # checked state
140
141
  User see [T] toggle is unchecked # unchecked state
141
142
  User see [T] dialog with {{v}} is hidden # text + state combined
142
143
 
143
- # 6. Count
144
+ # 6. Attribute (toHaveAttribute — when selector YAML has `attribute` field)
145
+ User see [T] image with {{v}} # image src
146
+ User see [T] link with {{v}} # link href
147
+
148
+ # 7. Count
144
149
  User see [T] row with {{count}} # element count
145
150
 
146
- # 7. Page Context
151
+ # 8. Page Context
147
152
  User see [T] page # URL assertion
148
153
  ```
149
154
 
150
155
  ### Table
151
156
 
152
157
  ```
153
- User see [Table] table has row with {{f}} # row exists
158
+ User see [Col] column in [Table] table # column exists (parent scoping)
159
+ User see [Table] table row with {{f}} # row exists
154
160
  User see [Table] table has no row with {{f}} # row not exists
155
- User see [Table] table has {{count}} rows # row count
156
- User see [Table] table has [Col] column # column exists
161
+ User see [Table] table with {{count}} rows # row count
157
162
  User see [Table] table is empty # empty table
158
163
  User see [Table] table row with {{f}} has [Col] with {{v}} # cell by filter
159
164
  User see [Table] table row 1 [Col] cell with {{v}} # cell by index
160
165
  User click [Act] in [Table] table row with {{f}} # action in row
161
166
  ```
162
167
 
168
+ ### Parent Scoping (disambiguation)
169
+
170
+ ```
171
+ User click [Submit] button in [User Info] form # button inside specific form
172
+ User fill [Email] field in [Registration] form with {{v}} # field inside specific form
173
+ User see [Total] text in [Summary] section with {{v}} # text inside specific section
174
+ User click [Delete] button in [Active Users] table # button inside specific table
175
+ ```
176
+
177
+ - **Optional** — only use when page has 2+ similar UI blocks
178
+ - **Valid parent types**: `table`, `list`, `section`, `dialog`, `form`
179
+ - **Max 2 levels**: `[Target] in [Parent]`. **NEVER** nest 3 levels: `[A] in [B] in [C]`
180
+ - Parent resolves from selectors YAML first, falls back to auto-infer `getByRole(parentType, { name })`
181
+
163
182
  ### States
164
183
 
165
184
  `hidden` `visible` `disabled` `enabled` `checked` `unchecked` `focused` `empty` `loading` `selected` `sorted ascending` `sorted descending`