wogiflow 1.0.46 → 1.0.47

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.
@@ -0,0 +1,60 @@
1
+ # Project Rules
2
+
3
+ This directory contains coding rules and patterns for this project, organized by category.
4
+
5
+ ## Structure
6
+
7
+ ```
8
+ .claude/rules/
9
+ ├── code-style/ # Naming conventions, formatting
10
+ │ └── naming-conventions.md
11
+ ├── security/ # Security patterns and practices
12
+ │ └── security-patterns.md
13
+ ├── architecture/ # Design decisions and patterns
14
+ │ ├── component-reuse.md
15
+ │ └── model-management.md
16
+ └── README.md
17
+ ```
18
+
19
+ ## How Rules Work
20
+
21
+ Rules are automatically loaded by Claude Code based on:
22
+ - **alwaysApply: true** - Rule is always loaded
23
+ - **alwaysApply: false** - Rule is loaded based on `globs` or `description` relevance
24
+ - **globs** - File patterns that trigger rule loading
25
+
26
+ ## Adding New Rules
27
+
28
+ 1. Choose the appropriate category subdirectory
29
+ 2. Create a `.md` file with frontmatter:
30
+
31
+ ```yaml
32
+ ---
33
+ alwaysApply: false
34
+ description: "Brief description for relevance matching"
35
+ globs: src/**/*.ts # Optional: only load for these files
36
+ ---
37
+ ```
38
+
39
+ 3. Write the rule content in markdown
40
+
41
+ ## Categories
42
+
43
+ | Category | Purpose |
44
+ |----------|---------|
45
+ | code-style | Naming conventions, formatting, file structure |
46
+ | security | Security patterns, input validation, safe practices |
47
+ | architecture | Design decisions, component patterns, system organization |
48
+
49
+ ## Auto-Generation
50
+
51
+ Some rules can be auto-generated from `.workflow/state/decisions.md`:
52
+
53
+ ```bash
54
+ node scripts/flow-rules-sync.js
55
+ ```
56
+
57
+ The sync script will route rules to appropriate category subdirectories.
58
+
59
+ ---
60
+ Last updated: 2026-01-12
@@ -0,0 +1,38 @@
1
+ ---
2
+ globs: src/components/**/*
3
+ alwaysApply: false
4
+ description: "Component reuse policy - always check app-map.md before creating components"
5
+ ---
6
+
7
+ # Component Reuse Policy
8
+
9
+ **Rule**: Always check `app-map.md` before creating any component.
10
+
11
+ ## Priority Order
12
+
13
+ 1. **Use existing** - Check if component already exists in app-map
14
+ 2. **Add variant** - Extend existing component with a new variant
15
+ 3. **Extend** - Create a wrapper/HOC around existing component
16
+ 4. **Create new** - Only as last resort
17
+
18
+ ## Before Creating Components
19
+
20
+ ```bash
21
+ # Check app-map first
22
+ cat .workflow/state/app-map.md | grep -i "button"
23
+
24
+ # Or search codebase
25
+ grep -r "Button" src/components/
26
+ ```
27
+
28
+ ## Variant vs New Component
29
+
30
+ Prefer variants when:
31
+ - Same base functionality, different appearance
32
+ - Same HTML structure, different styling
33
+ - Same component, different size/color/state
34
+
35
+ Create new component when:
36
+ - Fundamentally different functionality
37
+ - Different DOM structure
38
+ - Different state management
@@ -0,0 +1,76 @@
1
+ ---
2
+ alwaysApply: true
3
+ description: "All AI-context documents must use PIN markers for targeted context loading"
4
+ ---
5
+
6
+ # Document Structure for AI Context
7
+
8
+ All documents in `.workflow/` that are used as AI context MUST follow the PIN standard.
9
+
10
+ ## Required Structure
11
+
12
+ ### 1. Header with PIN List
13
+ Every document starts with a comment listing all pins in the document:
14
+ ```markdown
15
+ <!-- PINS: pin1, pin2, pin3 -->
16
+ ```
17
+
18
+ ### 2. Section PIN Markers
19
+ Each major section has a PIN marker comment:
20
+ ```markdown
21
+ ### Section Title
22
+ <!-- PIN: section-specific-pin -->
23
+ [Content]
24
+ ```
25
+
26
+ ### 3. PIN Naming Convention
27
+ - Use kebab-case: `user-authentication`, not `userAuthentication`
28
+ - Use semantic names: `error-handling`, not `eh`
29
+ - Use compound names for specificity: `json-parse-safety`
30
+
31
+ ## Why PINs Matter
32
+
33
+ The PIN system enables:
34
+ 1. **Targeted context loading**: Only load sections relevant to current task
35
+ 2. **Cheaper model routing**: Haiku can fetch only relevant sections for Opus
36
+ 3. **Change detection**: Hash sections independently for smart invalidation
37
+ 4. **Cross-reference**: Link sections by PIN across documents
38
+
39
+ ## Example Document
40
+
41
+ ```markdown
42
+ # Config Reference
43
+
44
+ <!-- PINS: database, authentication, api-keys, environment -->
45
+
46
+ ## Database Settings
47
+ <!-- PIN: database -->
48
+ | Setting | Default | Description |
49
+ |---------|---------|-------------|
50
+
51
+ ## Authentication
52
+ <!-- PIN: authentication -->
53
+ | Setting | Default | Description |
54
+ |---------|---------|-------------|
55
+ ```
56
+
57
+ ## Parsing
58
+
59
+ The PIN system automatically parses documents with:
60
+ - `flow-section-index.js` - Generates section index with pins
61
+ - `flow-section-resolver.js` - Resolves sections by PIN lookup
62
+ - `getSectionsByPins(['auth', 'security'])` - Fetch only relevant sections
63
+
64
+ ## Files That Must Have PINs
65
+
66
+ | File | Required PINs |
67
+ |------|---------------|
68
+ | `decisions.md` | Per coding rule/pattern |
69
+ | `app-map.md` | Per component/screen |
70
+ | `product.md` | Per product section |
71
+ | `stack.md` | Per technology |
72
+
73
+ ## Validation
74
+
75
+ Run `node scripts/flow-section-index.js --force` to regenerate the index.
76
+ Check `.workflow/state/section-index.json` for indexed sections and their pins.
@@ -0,0 +1,87 @@
1
+ ---
2
+ alwaysApply: false
3
+ description: "Cleanup checklist when refactoring or renaming features"
4
+ globs:
5
+ - "scripts/*.js"
6
+ - ".claude/skills/**/*"
7
+ ---
8
+
9
+ # Feature Refactoring Cleanup
10
+
11
+ When refactoring, renaming, or replacing a feature, ensure complete cleanup of the old implementation.
12
+
13
+ ## Mandatory Cleanup Checklist
14
+
15
+ When a feature is refactored or renamed, you MUST:
16
+
17
+ ### 1. Remove Old Code
18
+ - [ ] Delete old script files (e.g., `flow-old-feature.js`)
19
+ - [ ] Remove old skill directories (e.g., `.claude/skills/old-feature/`)
20
+ - [ ] Remove old hook files if applicable
21
+
22
+ ### 2. Update Configuration
23
+ - [ ] Rename config keys (e.g., `oldFeature` → `newFeature`)
24
+ - [ ] Remove from `skills.installed` array if skill was removed
25
+ - [ ] Update any feature flags
26
+
27
+ ### 3. Update Documentation
28
+ - [ ] Rename/update doc files in `.claude/docs/`
29
+ - [ ] Update command references in `commands.md`
30
+ - [ ] Update skill-matching.md if skill changed
31
+ - [ ] Search for old name in all `.md` files
32
+
33
+ ### 4. Clean References
34
+ - [ ] Search codebase: `grep -r "old-feature-name" .`
35
+ - [ ] Update imports in dependent scripts
36
+ - [ ] Update any hardcoded references
37
+
38
+ ### 5. Update State Files
39
+ - [ ] Clean `.workflow/state/` of old state files
40
+ - [ ] Update `ready.json` if tasks reference old feature
41
+ - [ ] Archive old change specs
42
+
43
+ ## Search Commands
44
+
45
+ Run these to find lingering references:
46
+
47
+ ```bash
48
+ # Find all references to old feature
49
+ grep -r "old-feature-name" --include="*.js" --include="*.md" --include="*.json" .
50
+
51
+ # Find in config
52
+ grep "oldFeatureName" .workflow/config.json
53
+
54
+ # Find skill references
55
+ grep -r "old-feature" .claude/
56
+ ```
57
+
58
+ ## Why This Matters
59
+
60
+ Incomplete cleanup causes:
61
+ - **Confusion**: Old commands/skills appear to work but don't
62
+ - **Bloat**: Dead code accumulates
63
+ - **Errors**: Old references cause runtime failures
64
+ - **Documentation drift**: Docs describe non-existent features
65
+
66
+ ## Example: transcript-digestion → long-input-gate
67
+
68
+ When this refactoring happened without proper cleanup:
69
+
70
+ | Artifact | Status | Should Have Been |
71
+ |----------|--------|------------------|
72
+ | `.claude/skills/transcript-digestion/` | Left behind | Deleted |
73
+ | `config.transcriptDigestion` | Left as-is | Renamed to `longInputGate` |
74
+ | `skills.installed` array | Still listed | Removed |
75
+ | `skill-matching.md` | Old references | Updated |
76
+ | `transcript-digestion.md` doc | Still existed | Renamed/rewritten |
77
+
78
+ ## Automation Opportunity
79
+
80
+ Consider adding a `flow refactor-cleanup <old-name> <new-name>` command that:
81
+ 1. Searches for all references
82
+ 2. Shows what needs updating
83
+ 3. Optionally auto-updates simple cases
84
+
85
+ ---
86
+
87
+ Last updated: 2026-01-14
@@ -0,0 +1,35 @@
1
+ ---
2
+ globs: scripts/flow-model*.js
3
+ alwaysApply: false
4
+ description: "Model management architecture - two separate systems for different purposes"
5
+ ---
6
+
7
+ # Model Management Architecture
8
+
9
+ **Context**: Phase 1 introduced model registry and stats system alongside existing model-adapter.
10
+
11
+ ## Two Model Systems
12
+
13
+ ### 1. flow-model-adapter.js - Prompt Adaptation
14
+
15
+ - `getCurrentModel()` returns normalized model name (string)
16
+ - Focus: Per-model prompt adjustments, learning, and corrections
17
+ - Imports: Used by flow-knowledge-router.js
18
+
19
+ ### 2. flow-models.js - Registry and Stats
20
+
21
+ - `getCurrentModel()` returns `{name, info, source}` object
22
+ - Focus: Model listing, routing recommendations, cost tracking
23
+ - Standalone CLI commands: `flow models [subcommand]`
24
+
25
+ ## Design Decision
26
+
27
+ **Keep them separate** because:
28
+ - Different return types serve different consumers
29
+ - Adapter system needs just the name for pattern matching
30
+ - Registry system needs full model metadata for display/routing
31
+ - Merging would create unnecessary coupling
32
+
33
+ ## Future Consideration
34
+
35
+ Could extract shared model detection logic into a common utility if they drift apart, but avoid premature abstraction.
@@ -0,0 +1,55 @@
1
+ ---
2
+ alwaysApply: true
3
+ description: "Naming conventions for files and code variants"
4
+ ---
5
+
6
+ # Naming Conventions
7
+
8
+ ## File Names
9
+
10
+ Use **kebab-case** for all file names in this project.
11
+
12
+ Examples:
13
+ - `flow-health.js` (correct)
14
+ - `flowHealth.js` (incorrect)
15
+ - `flow_health.js` (incorrect)
16
+
17
+ ## Variant Names
18
+
19
+ Use consistent variant names for components:
20
+
21
+ | Category | Values |
22
+ |----------|--------|
23
+ | Size | `sm`, `md`, `lg`, `xl` |
24
+ | Intent | `primary`, `secondary`, `danger`, `success`, `warning` |
25
+ | State | `default`, `hover`, `active`, `disabled` |
26
+
27
+ Examples:
28
+ ```jsx
29
+ <Button size="sm" intent="primary" />
30
+ <Badge variant="warning" />
31
+ ```
32
+
33
+ ## Catch Block Variables
34
+
35
+ Use `err` for all catch blocks in this codebase.
36
+
37
+ **Avoid**: `e`, `error`, `ex`, `exception` - these cause confusion with loop variables.
38
+
39
+ ```javascript
40
+ // Good
41
+ try {
42
+ doSomething();
43
+ } catch (err) {
44
+ console.error(err.message);
45
+ }
46
+
47
+ // Bad - 'e' conflicts with common iterator variables
48
+ try {
49
+ items.map(e => e.value); // 'e' used as iterator
50
+ } catch (e) {
51
+ console.error(e.message); // Easy to confuse with iterator 'e'
52
+ }
53
+ ```
54
+
55
+ **Reason**: Standardizing on `err` prevents mix-ups when `.map(e => ...)` is used nearby.
@@ -0,0 +1,143 @@
1
+ ---
2
+ alwaysApply: true
3
+ description: "Security patterns for file operations, JSON parsing, and path handling"
4
+ ---
5
+
6
+ # Security Patterns
7
+
8
+ Critical security patterns for this project.
9
+
10
+ ## 1. File Read Safety
11
+
12
+ Always wrap `fs.readFileSync()` in try-catch, even after `fileExists()` check.
13
+
14
+ **Reason**: Race conditions, permission changes, symlink issues can still cause failures.
15
+
16
+ ```javascript
17
+ // Good
18
+ try {
19
+ const content = fs.readFileSync(path, 'utf-8');
20
+ } catch (err) {
21
+ // Handle gracefully
22
+ }
23
+
24
+ // Bad - can still throw even if file existed
25
+ if (fs.existsSync(path)) {
26
+ const content = fs.readFileSync(path, 'utf-8');
27
+ }
28
+ ```
29
+
30
+ ## 2. JSON Parsing Safety
31
+
32
+ Use `safeJsonParse()` from flow-utils.js instead of raw `JSON.parse()`.
33
+
34
+ - Check for `__proto__`, `constructor`, `prototype` injection
35
+ - Validate parsed structure has expected fields before use
36
+ - Located in: `scripts/flow-utils.js`
37
+
38
+ ```javascript
39
+ // Good
40
+ const config = safeJsonParse(filePath, {});
41
+
42
+ // Bad - vulnerable to prototype pollution
43
+ const config = JSON.parse(fs.readFileSync(filePath));
44
+ ```
45
+
46
+ ## 3. Template Substitution Safety
47
+
48
+ When implementing template substitution:
49
+ - Block access to `__proto__`, `constructor`, `prototype` keys
50
+ - Use `Object.prototype.hasOwnProperty.call()` for property access
51
+ - Example: See `applyTemplate()` in flow-prompt-composer.js
52
+
53
+ ## 4. Path Safety
54
+
55
+ - Validate patterns before `path.join()` with user/config data
56
+ - Use `isPathWithinProject()` for defense-in-depth
57
+ - Glob-to-regex: Use `[^/]*` not `.*` to prevent path separator matching
58
+
59
+ ```javascript
60
+ // Good
61
+ if (!isPathWithinProject(targetPath)) {
62
+ throw new Error('Path outside project');
63
+ }
64
+
65
+ // Bad - allows path traversal
66
+ const fullPath = path.join(baseDir, userInput);
67
+ ```
68
+
69
+ ## 5. Module Dependencies
70
+
71
+ - Check for circular dependencies when refactoring shared functions
72
+ - Node.js handles circular deps but can cause undefined exports during load
73
+
74
+ ## 6. Claude Code Permission Patterns (2.1.7+)
75
+
76
+ When configuring permission rules in Claude Code, avoid overly permissive wildcards.
77
+
78
+ **Vulnerability fixed in 2.1.7**: Wildcard permission rules could match compound commands containing shell operators (`;`, `&&`, `||`, `|`).
79
+
80
+ ```javascript
81
+ // DANGEROUS - could match "npm test && rm -rf /"
82
+ "allow": "npm *"
83
+
84
+ // SAFER - be specific about allowed commands
85
+ "allow": "npm test"
86
+ "allow": "npm run build"
87
+ "allow": "npm install"
88
+
89
+ // BEST - use semantic prompts instead of wildcards
90
+ // In ExitPlanMode allowedPrompts:
91
+ { "tool": "Bash", "prompt": "run tests" }
92
+ { "tool": "Bash", "prompt": "install dependencies" }
93
+ ```
94
+
95
+ **Best practices:**
96
+ - Avoid `*` wildcards in permission rules
97
+ - Use specific command patterns
98
+ - Prefer semantic permission prompts over literal command matching
99
+ - Never allow broad patterns like `rm *` or `git *`
100
+ - Review permission rules after Claude Code updates
101
+
102
+ ## 7. Windows Path Safety
103
+
104
+ On Windows, be aware of path-related issues:
105
+
106
+ - Temp directory paths may contain characters like `\t` or `\n` that could be misinterpreted as escape sequences
107
+ - Use raw strings or proper escaping when constructing paths
108
+ - Cloud sync tools (OneDrive, Dropbox) and antivirus may touch file timestamps without changing content
109
+
110
+ ```javascript
111
+ // Good - use path.join() which handles platform differences
112
+ const tempPath = path.join(os.tmpdir(), 'myfile.txt');
113
+
114
+ // Bad - manual concatenation can break on Windows
115
+ const tempPath = os.tmpdir() + '/myfile.txt';
116
+ ```
117
+
118
+ ## 8. Shell Command Parameter Validation
119
+
120
+ When executing shell commands with dynamic parameters, always validate inputs.
121
+
122
+ **Risk**: Command injection via unvalidated parameters passed to execSync/spawn.
123
+
124
+ ```javascript
125
+ // DANGEROUS - lang parameter not validated
126
+ execSync(`sg --pattern "${pattern}" --lang ${lang} --json "${path}"`);
127
+
128
+ // SAFER - validate against whitelist
129
+ const ALLOWED_LANGUAGES = new Set(['typescript', 'javascript', 'python', 'go']);
130
+ if (!ALLOWED_LANGUAGES.has(lang)) {
131
+ throw new Error(`Unsupported language: ${lang}`);
132
+ }
133
+
134
+ // BEST - use execFile with array arguments (no shell interpretation)
135
+ const { execFileSync } = require('child_process');
136
+ execFileSync('sg', ['--pattern', pattern, '--lang', lang, '--json', path]);
137
+ ```
138
+
139
+ **Best practices:**
140
+ - Validate all dynamic parameters against allowlists
141
+ - Prefer `execFile`/`execFileSync` with array arguments over `exec`/`execSync` with template strings
142
+ - When using template strings, escape all user-controlled values
143
+ - Never interpolate user input directly into shell commands
@@ -58,28 +58,91 @@ class ClaudeBridge extends BaseBridge {
58
58
  }
59
59
 
60
60
  /**
61
- * Generate CLAUDE.md from Handlebars template
61
+ * Generate CLAUDE.md from Handlebars-like template
62
+ * Supports: {{variable}}, {{config.path}}, {{#if}}, {{#each}}, {{/if}}, {{/each}}
62
63
  */
63
64
  generateFromTemplate(templatePath, config) {
64
65
  const template = fs.readFileSync(templatePath, 'utf-8');
65
-
66
- // Simple template variable replacement (not full Handlebars)
67
66
  let content = template;
68
67
 
68
+ // Process {{#if config.path.to.value}}...{{/if}} blocks
69
+ // Non-greedy match to handle nested conditions
70
+ content = this.processConditionals(content, config);
71
+
72
+ // Process {{#each array}}...{{/each}} blocks
73
+ content = this.processEachBlocks(content, config);
74
+
69
75
  // Replace {{variable}} patterns
70
76
  content = content.replace(/\{\{(\w+)\}\}/g, (match, key) => {
71
77
  return config[key] || match;
72
78
  });
73
79
 
74
80
  // Replace {{config.path.to.value}} patterns
75
- content = content.replace(/\{\{config\.([^}]+)\}\}/g, (match, path) => {
76
- const value = this.getNestedValue(config, path);
81
+ content = content.replace(/\{\{config\.([^}]+)\}\}/g, (match, configPath) => {
82
+ const value = this.getNestedValue(config, configPath);
77
83
  return value !== undefined ? String(value) : match;
78
84
  });
79
85
 
86
+ // Replace {{timestamp}} with current time
87
+ content = content.replace(/\{\{timestamp\}\}/g, new Date().toISOString());
88
+
89
+ return content;
90
+ }
91
+
92
+ /**
93
+ * Process {{#if condition}}...{{/if}} blocks
94
+ */
95
+ processConditionals(content, config) {
96
+ // Regex to match {{#if path}}...{{/if}} - handles nested by processing innermost first
97
+ const ifRegex = /\{\{#if\s+([^}]+)\}\}([\s\S]*?)\{\{\/if\}\}/g;
98
+
99
+ let lastContent;
100
+ // Keep processing until no more changes (handles nested conditions)
101
+ do {
102
+ lastContent = content;
103
+ content = content.replace(ifRegex, (match, condition, body) => {
104
+ // Evaluate condition
105
+ let value;
106
+ if (condition.startsWith('config.')) {
107
+ value = this.getNestedValue(config, condition.replace('config.', ''));
108
+ } else if (condition === 'skills') {
109
+ value = config.skills?.installed?.length > 0;
110
+ } else {
111
+ value = this.getNestedValue(config, condition);
112
+ }
113
+
114
+ // If truthy, return the body (stripped of the conditionals)
115
+ // If falsy, return empty string
116
+ return value ? body : '';
117
+ });
118
+ } while (content !== lastContent);
119
+
80
120
  return content;
81
121
  }
82
122
 
123
+ /**
124
+ * Process {{#each array}}...{{/each}} blocks
125
+ */
126
+ processEachBlocks(content, config) {
127
+ const eachRegex = /\{\{#each\s+(\w+)\}\}([\s\S]*?)\{\{\/each\}\}/g;
128
+
129
+ return content.replace(eachRegex, (match, arrayName, body) => {
130
+ let array;
131
+ if (arrayName === 'skills') {
132
+ array = config.skills?.installed || [];
133
+ } else {
134
+ array = config[arrayName] || [];
135
+ }
136
+
137
+ if (!Array.isArray(array) || array.length === 0) {
138
+ return '';
139
+ }
140
+
141
+ // Replace {{this}} in body with each array item
142
+ return array.map(item => body.replace(/\{\{this\}\}/g, String(item))).join('');
143
+ });
144
+ }
145
+
83
146
  /**
84
147
  * Generate default CLAUDE.md when no template exists
85
148
  */
@@ -0,0 +1,24 @@
1
+ # Architecture
2
+
3
+ ## Pattern
4
+
5
+ <!-- Detected during onboarding -->
6
+ <!-- Examples: MVC, Clean Architecture, DDD, Microservices, Monolith -->
7
+
8
+ ## Structure
9
+
10
+ <!-- Project structure overview -->
11
+ <!-- Key directories and their purposes -->
12
+
13
+ ## Key Decisions
14
+
15
+ <!-- Architecture decisions made for this project -->
16
+ <!-- Trade-offs and rationale -->
17
+
18
+ ## Dependencies
19
+
20
+ <!-- Major dependencies and their purposes -->
21
+
22
+ ---
23
+
24
+ *This file is auto-populated during `flow onboard`. Update manually as architecture evolves.*
@@ -0,0 +1,33 @@
1
+ # Tech Stack
2
+
3
+ ## Framework
4
+
5
+ <!-- Primary framework (e.g., Next.js, NestJS, FastAPI) -->
6
+
7
+ ## Language
8
+
9
+ <!-- Primary language (e.g., TypeScript, Python, Go) -->
10
+
11
+ ## Database
12
+
13
+ <!-- Database system if applicable (e.g., PostgreSQL, MongoDB) -->
14
+
15
+ ## Package Manager
16
+
17
+ <!-- npm, yarn, pnpm, pip, etc. -->
18
+
19
+ ## Build Tools
20
+
21
+ <!-- Build and bundling tools (e.g., Vite, Webpack, esbuild) -->
22
+
23
+ ## Testing
24
+
25
+ <!-- Test frameworks (e.g., Jest, Vitest, pytest) -->
26
+
27
+ ## Other Tools
28
+
29
+ <!-- Linters, formatters, CI/CD tools -->
30
+
31
+ ---
32
+
33
+ *This file is auto-populated during `flow onboard`. Update manually as stack evolves.*
@@ -0,0 +1,36 @@
1
+ # Testing
2
+
3
+ ## Test Framework
4
+
5
+ <!-- Primary test framework (e.g., Jest, Vitest, pytest) -->
6
+
7
+ ## Test Commands
8
+
9
+ ```bash
10
+ # Run all tests
11
+ npm test
12
+
13
+ # Run tests in watch mode
14
+ npm run test:watch
15
+
16
+ # Run specific test file
17
+ npm test -- path/to/test.ts
18
+ ```
19
+
20
+ ## Test Structure
21
+
22
+ <!-- Where tests are located -->
23
+ <!-- Naming conventions -->
24
+
25
+ ## Coverage
26
+
27
+ <!-- Coverage requirements if any -->
28
+ <!-- Coverage commands -->
29
+
30
+ ## E2E Testing
31
+
32
+ <!-- E2E framework if used (e.g., Playwright, Cypress) -->
33
+
34
+ ---
35
+
36
+ *This file is auto-populated during `flow onboard`. Update manually as testing strategy evolves.*