@eskoubar95/spec 0.1.0 ā 0.1.3
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/dist/commands/help.d.ts +5 -0
- package/dist/commands/help.d.ts.map +1 -0
- package/dist/commands/help.js +23 -0
- package/dist/commands/help.js.map +1 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +30 -14
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/install.d.ts +5 -0
- package/dist/commands/install.d.ts.map +1 -0
- package/dist/commands/install.js +88 -0
- package/dist/commands/install.js.map +1 -0
- package/dist/commands/update.d.ts +5 -0
- package/dist/commands/update.d.ts.map +1 -0
- package/dist/commands/update.js +72 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/commands/workspace.d.ts +5 -0
- package/dist/commands/workspace.d.ts.map +1 -0
- package/dist/commands/workspace.js +17 -0
- package/dist/commands/workspace.js.map +1 -0
- package/dist/index.js +42 -9
- package/dist/index.js.map +1 -1
- package/dist/lib/backup-cursor.d.ts +16 -0
- package/dist/lib/backup-cursor.d.ts.map +1 -0
- package/dist/lib/backup-cursor.js +50 -0
- package/dist/lib/backup-cursor.js.map +1 -0
- package/dist/lib/copy-template.d.ts +9 -1
- package/dist/lib/copy-template.d.ts.map +1 -1
- package/dist/lib/copy-template.js +94 -3
- package/dist/lib/copy-template.js.map +1 -1
- package/dist/lib/cursor-detection.d.ts +6 -0
- package/dist/lib/cursor-detection.d.ts.map +1 -0
- package/dist/lib/cursor-detection.js +31 -0
- package/dist/lib/cursor-detection.js.map +1 -0
- package/dist/lib/detection.d.ts +25 -0
- package/dist/lib/detection.d.ts.map +1 -0
- package/dist/lib/detection.js +186 -0
- package/dist/lib/detection.js.map +1 -0
- package/dist/lib/install-existing.d.ts +6 -0
- package/dist/lib/install-existing.d.ts.map +1 -0
- package/dist/lib/install-existing.js +63 -0
- package/dist/lib/install-existing.js.map +1 -0
- package/dist/lib/project-name.d.ts +7 -0
- package/dist/lib/project-name.d.ts.map +1 -0
- package/dist/lib/project-name.js +13 -0
- package/dist/lib/project-name.js.map +1 -0
- package/dist/lib/prompts.d.ts +6 -5
- package/dist/lib/prompts.d.ts.map +1 -1
- package/dist/lib/prompts.js +114 -0
- package/dist/lib/prompts.js.map +1 -1
- package/dist/lib/version-check.d.ts +21 -0
- package/dist/lib/version-check.d.ts.map +1 -0
- package/dist/lib/version-check.js +49 -0
- package/dist/lib/version-check.js.map +1 -0
- package/dist/lib/workspace.d.ts +7 -0
- package/dist/lib/workspace.d.ts.map +1 -0
- package/dist/lib/workspace.js +38 -0
- package/dist/lib/workspace.js.map +1 -0
- package/dist/types.d.ts +32 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +2 -2
- package/template/.cursor/commands/_shared/activation.md +220 -0
- package/template/.cursor/commands/_shared/coderabbit-integration.md +278 -0
- package/template/.cursor/commands/_shared/command-stacks.md +124 -0
- package/template/.cursor/commands/_shared/deployment-detection.md +294 -0
- package/template/.cursor/commands/_shared/detection.md +277 -0
- package/template/.cursor/commands/_shared/documentation-lookup.md +321 -0
- package/template/.cursor/commands/_shared/git-workflow.md +288 -0
- package/template/.cursor/commands/_shared/github-helpers.md +337 -0
- package/template/.cursor/commands/_shared/github-workflows.md +351 -0
- package/template/.cursor/commands/_shared/helper-metadata.md +481 -0
- package/template/.cursor/commands/_shared/linear-automation.md +388 -0
- package/template/.cursor/commands/_shared/linear-helpers.md +254 -0
- package/template/.cursor/commands/_shared/performance-monitoring.md +369 -0
- package/template/.cursor/commands/_shared/pr-description.md +279 -0
- package/template/.cursor/commands/_shared/retrospective-spec-creation.md +977 -0
- package/template/.cursor/commands/_shared/scaling.md +264 -0
- package/template/.cursor/commands/_shared/state-assertions.md +174 -0
- package/template/.cursor/commands/_shared/test-automation.md +388 -0
- package/template/.cursor/commands/_shared/verification-checkpoints.md +145 -0
- package/template/.cursor/commands/spec/audit.md +240 -0
- package/template/.cursor/commands/spec/evolve.md +163 -0
- package/template/.cursor/commands/spec/sync.md +196 -0
- package/template/.cursor/commands/tools/refactor.md +555 -0
- package/template/.cursor/rules/10-engineering.mdc +149 -0
- package/template/.cursor/rules/11-design.mdc +129 -0
- package/template/.cursor/rules/12-business.mdc +132 -0
- package/template/.cursor/rules/20-nextjs.mdc +146 -0
- package/template/.cursor/rules/21-api-design.mdc +176 -0
- package/template/.cursor/rules/30-database.mdc +183 -0
- package/template/.cursor/rules/31-testing.mdc +191 -0
- package/template/.cursor/scripts/validate-helpers.js +254 -0
- package/template/.sdd/detection-cache.json +1 -0
- package/template/.sdd/install-info.json +1 -0
- package/template/.sdd/version +1 -0
- package/template/spec/00-root-spec.md +8 -1
- package/template/work/backlog/tasks.local.md +92 -0
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Database Patterns
|
|
3
|
+
description: Database patterns - applies when database is detected
|
|
4
|
+
owner: sdd-system
|
|
5
|
+
severity: warn
|
|
6
|
+
globs: "**/migrations/**, **/supabase/**, **/prisma/**"
|
|
7
|
+
alwaysApply: false
|
|
8
|
+
activation:
|
|
9
|
+
projectTypes: [web-app, api-service]
|
|
10
|
+
projectSizes: [small, medium, large, enterprise]
|
|
11
|
+
projectPhases: [initialization, expansion, maintenance, migration, legacy]
|
|
12
|
+
technologies: [postgres, mysql, mongodb, supabase, prisma]
|
|
13
|
+
requires: [10-engineering]
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
# Database Patterns
|
|
17
|
+
|
|
18
|
+
## Purpose
|
|
19
|
+
|
|
20
|
+
Database patterns for schema design, migrations, queries, ORM patterns, and raw SQL patterns. Applies when database work is detected (PostgreSQL, MySQL, MongoDB, Supabase, Prisma, etc.).
|
|
21
|
+
|
|
22
|
+
## Principles
|
|
23
|
+
|
|
24
|
+
- **Schema-First**: Design schema before writing queries
|
|
25
|
+
- **Migration-Based**: All schema changes via migrations
|
|
26
|
+
- **Index Hot Paths**: Index frequently queried columns
|
|
27
|
+
- **RLS (Row Level Security)**: Enable RLS on user-facing tables
|
|
28
|
+
- **Backup Strategy**: Regular backups, tested restore process
|
|
29
|
+
|
|
30
|
+
## Schema Design
|
|
31
|
+
|
|
32
|
+
**Table Organization:**
|
|
33
|
+
- Group related tables
|
|
34
|
+
- Use consistent naming (snake_case or camelCase)
|
|
35
|
+
- Document table purposes
|
|
36
|
+
- Define relationships explicitly
|
|
37
|
+
|
|
38
|
+
**Column Design:**
|
|
39
|
+
- Use appropriate data types
|
|
40
|
+
- Set NOT NULL where appropriate
|
|
41
|
+
- Use defaults where sensible
|
|
42
|
+
- Add indexes for foreign keys
|
|
43
|
+
|
|
44
|
+
**Relationships:**
|
|
45
|
+
- One-to-many: Foreign key in child table
|
|
46
|
+
- Many-to-many: Junction table
|
|
47
|
+
- One-to-one: Foreign key in either table
|
|
48
|
+
|
|
49
|
+
## Migrations
|
|
50
|
+
|
|
51
|
+
**Migration Best Practices:**
|
|
52
|
+
- All schema changes via migrations
|
|
53
|
+
- Versioned migrations (timestamped)
|
|
54
|
+
- Never edit existing migrations
|
|
55
|
+
- Test migrations on staging first
|
|
56
|
+
- Include rollback strategy
|
|
57
|
+
|
|
58
|
+
**Migration Structure:**
|
|
59
|
+
```
|
|
60
|
+
migrations/
|
|
61
|
+
20240101000000_create_users_table.sql
|
|
62
|
+
20240102000000_add_email_to_users.sql
|
|
63
|
+
20240103000000_create_posts_table.sql
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
**Migration Guidelines:**
|
|
67
|
+
- One logical change per migration
|
|
68
|
+
- Include both up and down migrations
|
|
69
|
+
- Test migrations on sample data
|
|
70
|
+
- Document breaking changes
|
|
71
|
+
|
|
72
|
+
## Queries
|
|
73
|
+
|
|
74
|
+
**Query Patterns:**
|
|
75
|
+
- Use parameterized queries (prevent SQL injection)
|
|
76
|
+
- Select only needed columns (avoid SELECT *)
|
|
77
|
+
- Use JOINs instead of N+1 queries
|
|
78
|
+
- Batch related queries when possible
|
|
79
|
+
|
|
80
|
+
**Performance:**
|
|
81
|
+
- Index frequently queried columns
|
|
82
|
+
- Use EXPLAIN ANALYZE to optimize
|
|
83
|
+
- Avoid full table scans
|
|
84
|
+
- Use pagination for large result sets
|
|
85
|
+
|
|
86
|
+
## ORM Patterns
|
|
87
|
+
|
|
88
|
+
**If Using ORM (Prisma, TypeORM, etc.):**
|
|
89
|
+
- Use ORM query builder for type safety
|
|
90
|
+
- Keep queries simple and readable
|
|
91
|
+
- Use raw SQL only when necessary
|
|
92
|
+
- Leverage ORM relationships
|
|
93
|
+
|
|
94
|
+
**If Using Raw SQL:**
|
|
95
|
+
- Use parameterized queries
|
|
96
|
+
- Centralize query logic
|
|
97
|
+
- Document complex queries
|
|
98
|
+
- Test queries with sample data
|
|
99
|
+
|
|
100
|
+
## Row Level Security (RLS)
|
|
101
|
+
|
|
102
|
+
**RLS Best Practices:**
|
|
103
|
+
- Enable RLS on all user-facing tables
|
|
104
|
+
- Policies check `auth.uid()` for ownership
|
|
105
|
+
- Service role bypasses RLS (server-side only)
|
|
106
|
+
- Test RLS policies thoroughly
|
|
107
|
+
|
|
108
|
+
**RLS Patterns:**
|
|
109
|
+
- Users can only access their own data
|
|
110
|
+
- Public data accessible to all
|
|
111
|
+
- Admin data accessible to admins only
|
|
112
|
+
- Complex policies for shared resources
|
|
113
|
+
|
|
114
|
+
## Indexing
|
|
115
|
+
|
|
116
|
+
**Index Strategy:**
|
|
117
|
+
- Index foreign keys
|
|
118
|
+
- Index frequently queried columns
|
|
119
|
+
- Index columns used in WHERE clauses
|
|
120
|
+
- Index columns used in JOINs
|
|
121
|
+
- Don't over-index (writes become slower)
|
|
122
|
+
|
|
123
|
+
**Index Types:**
|
|
124
|
+
- B-tree indexes (default, most common)
|
|
125
|
+
- GIN indexes (for arrays, full-text search)
|
|
126
|
+
- GiST indexes (for geometric data)
|
|
127
|
+
- Partial indexes (for filtered queries)
|
|
128
|
+
|
|
129
|
+
## Transactions
|
|
130
|
+
|
|
131
|
+
**Transaction Patterns:**
|
|
132
|
+
- Use transactions for multi-step operations
|
|
133
|
+
- Keep transactions short
|
|
134
|
+
- Handle transaction errors
|
|
135
|
+
- Rollback on errors
|
|
136
|
+
|
|
137
|
+
**Transaction Guidelines:**
|
|
138
|
+
- Start transaction
|
|
139
|
+
- Perform operations
|
|
140
|
+
- Commit on success
|
|
141
|
+
- Rollback on error
|
|
142
|
+
|
|
143
|
+
## Backup and Recovery
|
|
144
|
+
|
|
145
|
+
**Backup Strategy:**
|
|
146
|
+
- Regular automated backups
|
|
147
|
+
- Test restore process
|
|
148
|
+
- Document backup schedule
|
|
149
|
+
- Store backups securely
|
|
150
|
+
|
|
151
|
+
**Recovery:**
|
|
152
|
+
- Test restore on staging
|
|
153
|
+
- Document recovery procedure
|
|
154
|
+
- Have rollback plan
|
|
155
|
+
- Monitor backup success
|
|
156
|
+
|
|
157
|
+
## Database-Specific Patterns
|
|
158
|
+
|
|
159
|
+
**PostgreSQL:**
|
|
160
|
+
- Use JSONB for flexible schemas
|
|
161
|
+
- Use arrays for simple lists
|
|
162
|
+
- Use enums for fixed sets
|
|
163
|
+
- Use full-text search (tsvector)
|
|
164
|
+
|
|
165
|
+
**MySQL:**
|
|
166
|
+
- Use appropriate storage engines
|
|
167
|
+
- Optimize for InnoDB
|
|
168
|
+
- Use proper charset (utf8mb4)
|
|
169
|
+
|
|
170
|
+
**MongoDB:**
|
|
171
|
+
- Design documents for queries
|
|
172
|
+
- Use indexes effectively
|
|
173
|
+
- Avoid deep nesting
|
|
174
|
+
- Use aggregation pipeline
|
|
175
|
+
|
|
176
|
+
## Supabase-Specific
|
|
177
|
+
|
|
178
|
+
**If Supabase Detected:**
|
|
179
|
+
- Server-side: Use service role (bypasses RLS)
|
|
180
|
+
- Client-side: Use anon key (respects RLS)
|
|
181
|
+
- Never expose service role key
|
|
182
|
+
- Use Supabase query builder
|
|
183
|
+
- Generate TypeScript types
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Testing Patterns
|
|
3
|
+
description: Testing patterns - applies when testing framework is detected
|
|
4
|
+
owner: sdd-system
|
|
5
|
+
severity: warn
|
|
6
|
+
globs: "**/*.test.ts, **/*.test.tsx, **/*.spec.ts, **/*.spec.tsx, **/__tests__/**"
|
|
7
|
+
alwaysApply: false
|
|
8
|
+
activation:
|
|
9
|
+
projectTypes: [web-app, cli-tool, library, api-service, mobile-app]
|
|
10
|
+
projectSizes: [small, medium, large, enterprise]
|
|
11
|
+
projectPhases: [initialization, expansion, maintenance, migration, legacy]
|
|
12
|
+
technologies: [jest, vitest, playwright, cypress, testing-library]
|
|
13
|
+
requires: [10-engineering]
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
# Testing Patterns
|
|
17
|
+
|
|
18
|
+
## Purpose
|
|
19
|
+
|
|
20
|
+
Testing patterns for unit, integration, and e2e testing. Applies when testing framework is detected (Jest, Vitest, Playwright, Cypress, Testing Library, etc.).
|
|
21
|
+
|
|
22
|
+
## Principles
|
|
23
|
+
|
|
24
|
+
- **Test Behavior, Not Implementation**: Test what the code does, not how
|
|
25
|
+
- **AAA Pattern**: Arrange-Act-Assert
|
|
26
|
+
- **Cover Critical Paths**: Happy path, failure paths, edge cases
|
|
27
|
+
- **Fast Feedback**: Tests should run quickly
|
|
28
|
+
- **Maintainable Tests**: Tests should be easy to read and update
|
|
29
|
+
|
|
30
|
+
## Test Structure
|
|
31
|
+
|
|
32
|
+
**Test Organization:**
|
|
33
|
+
- Co-locate tests with source files
|
|
34
|
+
- OR: Mirror structure under `__tests__/`
|
|
35
|
+
- Group related tests with `describe` blocks
|
|
36
|
+
- Use intention-revealing test names
|
|
37
|
+
|
|
38
|
+
**Test File Naming:**
|
|
39
|
+
- `*.test.ts` or `*.test.tsx` for component tests
|
|
40
|
+
- `*.spec.ts` or `*.spec.tsx` for specification tests
|
|
41
|
+
- Match source file name
|
|
42
|
+
|
|
43
|
+
## Unit Testing
|
|
44
|
+
|
|
45
|
+
**What to Test:**
|
|
46
|
+
- Individual functions and methods
|
|
47
|
+
- Business logic
|
|
48
|
+
- Utility functions
|
|
49
|
+
- Pure functions
|
|
50
|
+
|
|
51
|
+
**Unit Test Pattern:**
|
|
52
|
+
```typescript
|
|
53
|
+
describe('functionName', () => {
|
|
54
|
+
it('should do something specific', () => {
|
|
55
|
+
// Arrange
|
|
56
|
+
const input = { ... };
|
|
57
|
+
|
|
58
|
+
// Act
|
|
59
|
+
const result = functionName(input);
|
|
60
|
+
|
|
61
|
+
// Assert
|
|
62
|
+
expect(result).toEqual({ ... });
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
**Best Practices:**
|
|
68
|
+
- Test one thing per test
|
|
69
|
+
- Use descriptive test names
|
|
70
|
+
- Test edge cases
|
|
71
|
+
- Test error cases
|
|
72
|
+
- Avoid testing implementation details
|
|
73
|
+
|
|
74
|
+
## Integration Testing
|
|
75
|
+
|
|
76
|
+
**What to Test:**
|
|
77
|
+
- Component interactions
|
|
78
|
+
- API endpoint behavior
|
|
79
|
+
- Database operations
|
|
80
|
+
- Service layer integration
|
|
81
|
+
|
|
82
|
+
**Integration Test Pattern:**
|
|
83
|
+
```typescript
|
|
84
|
+
describe('API endpoint', () => {
|
|
85
|
+
it('should handle request correctly', async () => {
|
|
86
|
+
// Arrange
|
|
87
|
+
const request = { ... };
|
|
88
|
+
|
|
89
|
+
// Act
|
|
90
|
+
const response = await fetch('/api/v1/endpoint', {
|
|
91
|
+
method: 'POST',
|
|
92
|
+
body: JSON.stringify(request)
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
// Assert
|
|
96
|
+
expect(response.status).toBe(201);
|
|
97
|
+
expect(await response.json()).toMatchObject({ ... });
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## E2E Testing
|
|
103
|
+
|
|
104
|
+
**What to Test:**
|
|
105
|
+
- User workflows
|
|
106
|
+
- Critical user paths
|
|
107
|
+
- Cross-browser compatibility
|
|
108
|
+
- Real user scenarios
|
|
109
|
+
|
|
110
|
+
**E2E Test Pattern:**
|
|
111
|
+
```typescript
|
|
112
|
+
describe('User workflow', () => {
|
|
113
|
+
it('should complete user journey', async () => {
|
|
114
|
+
// Navigate to page
|
|
115
|
+
await page.goto('/');
|
|
116
|
+
|
|
117
|
+
// Interact with page
|
|
118
|
+
await page.click('button[data-testid="submit"]');
|
|
119
|
+
|
|
120
|
+
// Verify result
|
|
121
|
+
await expect(page.locator('.success')).toBeVisible();
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Test Data and Fixtures
|
|
127
|
+
|
|
128
|
+
**Fixtures:**
|
|
129
|
+
- Create reusable test data
|
|
130
|
+
- Use factories for complex objects
|
|
131
|
+
- Keep fixtures realistic
|
|
132
|
+
- Avoid hard-coded test data in tests
|
|
133
|
+
|
|
134
|
+
**Mocking:**
|
|
135
|
+
- Mock external dependencies
|
|
136
|
+
- Mock API calls
|
|
137
|
+
- Mock database operations
|
|
138
|
+
- Keep mocks simple and focused
|
|
139
|
+
|
|
140
|
+
## Test Coverage
|
|
141
|
+
|
|
142
|
+
**Coverage Goals:**
|
|
143
|
+
- Critical paths: 100% coverage
|
|
144
|
+
- Business logic: High coverage
|
|
145
|
+
- UI components: Moderate coverage
|
|
146
|
+
- Utilities: High coverage
|
|
147
|
+
|
|
148
|
+
**What Not to Test:**
|
|
149
|
+
- Third-party library code
|
|
150
|
+
- Framework code
|
|
151
|
+
- Trivial getters/setters
|
|
152
|
+
- Implementation details
|
|
153
|
+
|
|
154
|
+
## Testing Best Practices
|
|
155
|
+
|
|
156
|
+
**Fast Tests:**
|
|
157
|
+
- Unit tests should be fast (< 100ms)
|
|
158
|
+
- Integration tests moderate speed
|
|
159
|
+
- E2E tests can be slower
|
|
160
|
+
|
|
161
|
+
**Reliable Tests:**
|
|
162
|
+
- Tests should be deterministic
|
|
163
|
+
- Avoid flaky tests
|
|
164
|
+
- Use proper cleanup
|
|
165
|
+
- Isolate tests from each other
|
|
166
|
+
|
|
167
|
+
**Maintainable Tests:**
|
|
168
|
+
- Tests should be readable
|
|
169
|
+
- Use helper functions
|
|
170
|
+
- Avoid duplication
|
|
171
|
+
- Keep tests focused
|
|
172
|
+
|
|
173
|
+
## Framework-Specific
|
|
174
|
+
|
|
175
|
+
**Jest/Vitest:**
|
|
176
|
+
- Use `describe` and `it` blocks
|
|
177
|
+
- Use `beforeEach`/`afterEach` for setup
|
|
178
|
+
- Use `mock` for mocking
|
|
179
|
+
- Use `expect` for assertions
|
|
180
|
+
|
|
181
|
+
**Testing Library:**
|
|
182
|
+
- Query by role, label, test id
|
|
183
|
+
- Avoid querying by implementation details
|
|
184
|
+
- Test user-visible behavior
|
|
185
|
+
- Use `screen` for queries
|
|
186
|
+
|
|
187
|
+
**Playwright/Cypress:**
|
|
188
|
+
- Test real user interactions
|
|
189
|
+
- Use page object pattern
|
|
190
|
+
- Keep tests independent
|
|
191
|
+
- Use proper waits
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Helper Metadata Validation Script
|
|
5
|
+
*
|
|
6
|
+
* Validates helper metadata for consistency and correctness:
|
|
7
|
+
* - All helpers have YAML frontmatter
|
|
8
|
+
* - Section line ranges match actual section markers
|
|
9
|
+
* - Section titles match headings
|
|
10
|
+
* - All helpers documented in helper-metadata.md
|
|
11
|
+
* - Metadata consistency between frontmatter and registry
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const fs = require('fs');
|
|
15
|
+
const path = require('path');
|
|
16
|
+
|
|
17
|
+
const HELPERS_DIR = path.join(__dirname, '../commands/_shared');
|
|
18
|
+
const METADATA_FILE = path.join(HELPERS_DIR, 'helper-metadata.md');
|
|
19
|
+
|
|
20
|
+
// Colors for output
|
|
21
|
+
const colors = {
|
|
22
|
+
reset: '\x1b[0m',
|
|
23
|
+
green: '\x1b[32m',
|
|
24
|
+
red: '\x1b[31m',
|
|
25
|
+
yellow: '\x1b[33m',
|
|
26
|
+
blue: '\x1b[34m',
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
function log(message, color = 'reset') {
|
|
30
|
+
console.log(`${colors[color]}${message}${colors.reset}`);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function parseYAMLFrontmatter(content) {
|
|
34
|
+
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
35
|
+
if (!frontmatterMatch) return null;
|
|
36
|
+
|
|
37
|
+
const frontmatter = {};
|
|
38
|
+
const lines = frontmatterMatch[1].split('\n');
|
|
39
|
+
|
|
40
|
+
let currentKey = null;
|
|
41
|
+
let currentValue = [];
|
|
42
|
+
let inArray = false;
|
|
43
|
+
|
|
44
|
+
for (const line of lines) {
|
|
45
|
+
if (line.trim() === '') continue;
|
|
46
|
+
|
|
47
|
+
// Check for array start
|
|
48
|
+
if (line.match(/^(\s*)([a-z_]+):\s*$/)) {
|
|
49
|
+
if (currentKey) {
|
|
50
|
+
frontmatter[currentKey] = currentValue.length === 1 ? currentValue[0] : currentValue;
|
|
51
|
+
}
|
|
52
|
+
currentKey = line.match(/^(\s*)([a-z_]+):\s*$/)[2];
|
|
53
|
+
currentValue = [];
|
|
54
|
+
inArray = true;
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Check for array item
|
|
59
|
+
if (inArray && line.match(/^\s*-\s*(.+)$/)) {
|
|
60
|
+
currentValue.push(line.match(/^\s*-\s*(.+)$/)[1]);
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Check for key-value
|
|
65
|
+
if (line.match(/^(\s*)([a-z_]+):\s*(.+)$/)) {
|
|
66
|
+
if (currentKey) {
|
|
67
|
+
frontmatter[currentKey] = currentValue.length === 1 ? currentValue[0] : currentValue;
|
|
68
|
+
}
|
|
69
|
+
const match = line.match(/^(\s*)([a-z_]+):\s*(.+)$/);
|
|
70
|
+
currentKey = match[2];
|
|
71
|
+
currentValue = [match[3]];
|
|
72
|
+
inArray = false;
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Check for nested structure (sections)
|
|
77
|
+
if (line.match(/^\s+([a-z_]+):/)) {
|
|
78
|
+
// This is a nested key, skip for now (simplified parsing)
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (currentKey) {
|
|
84
|
+
frontmatter[currentKey] = currentValue.length === 1 ? currentValue[0] : currentValue;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return frontmatter;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function findSectionMarkers(content) {
|
|
91
|
+
const sections = [];
|
|
92
|
+
const lines = content.split('\n');
|
|
93
|
+
|
|
94
|
+
for (let i = 0; i < lines.length; i++) {
|
|
95
|
+
const line = lines[i];
|
|
96
|
+
const sectionMatch = line.match(/^## Section: (.+?) \(Lines (\d+)-(\d+)\)$/);
|
|
97
|
+
if (sectionMatch) {
|
|
98
|
+
sections.push({
|
|
99
|
+
title: sectionMatch[1],
|
|
100
|
+
startLine: parseInt(sectionMatch[2]),
|
|
101
|
+
endLine: parseInt(sectionMatch[3]),
|
|
102
|
+
actualLine: i + 1,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return sections;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function validateHelper(helperFile) {
|
|
111
|
+
const filePath = path.join(HELPERS_DIR, helperFile);
|
|
112
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
113
|
+
const issues = [];
|
|
114
|
+
|
|
115
|
+
// 1. Check frontmatter exists
|
|
116
|
+
const frontmatter = parseYAMLFrontmatter(content);
|
|
117
|
+
if (!frontmatter) {
|
|
118
|
+
issues.push({ type: 'error', message: 'Missing YAML frontmatter' });
|
|
119
|
+
return { file: helperFile, issues };
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// 2. Check required fields
|
|
123
|
+
const requiredFields = ['helper_id', 'load_when', 'sections', 'always_load'];
|
|
124
|
+
for (const field of requiredFields) {
|
|
125
|
+
if (!(field in frontmatter)) {
|
|
126
|
+
issues.push({ type: 'error', message: `Missing required field: ${field}` });
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// 3. Check section markers match metadata
|
|
131
|
+
const sections = findSectionMarkers(content);
|
|
132
|
+
if (sections.length > 0 && frontmatter.sections) {
|
|
133
|
+
// Note: Simplified check - would need to parse sections from frontmatter properly
|
|
134
|
+
// For now, just check that sections exist
|
|
135
|
+
for (const section of sections) {
|
|
136
|
+
if (section.actualLine !== section.startLine) {
|
|
137
|
+
issues.push({
|
|
138
|
+
type: 'warning',
|
|
139
|
+
message: `Section "${section.title}" marker at line ${section.actualLine}, but metadata says ${section.startLine}`,
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return { file: helperFile, frontmatter, sections, issues };
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function validateRegistry() {
|
|
149
|
+
const content = fs.readFileSync(METADATA_FILE, 'utf-8');
|
|
150
|
+
const helperFiles = fs.readdirSync(HELPERS_DIR)
|
|
151
|
+
.filter(f => f.endsWith('.md') && f !== 'helper-metadata.md');
|
|
152
|
+
|
|
153
|
+
const registryHelpers = [];
|
|
154
|
+
const registryMatch = content.match(/### ([a-z-]+)\.md/g);
|
|
155
|
+
if (registryMatch) {
|
|
156
|
+
registryHelpers.push(...registryMatch.map(m => m.replace('### ', '').replace('.md', '')));
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const issues = [];
|
|
160
|
+
|
|
161
|
+
// Check all helpers are in registry
|
|
162
|
+
for (const helperFile of helperFiles) {
|
|
163
|
+
const helperId = helperFile.replace('.md', '');
|
|
164
|
+
if (!registryHelpers.includes(helperId)) {
|
|
165
|
+
issues.push({
|
|
166
|
+
type: 'warning',
|
|
167
|
+
message: `${helperId}.md not documented in helper-metadata.md`,
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Check registry has all helpers
|
|
173
|
+
for (const registryHelper of registryHelpers) {
|
|
174
|
+
if (!helperFiles.includes(`${registryHelper}.md`)) {
|
|
175
|
+
issues.push({
|
|
176
|
+
type: 'error',
|
|
177
|
+
message: `Registry documents ${registryHelper}.md but file doesn't exist`,
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return { helperFiles, registryHelpers, issues };
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function main() {
|
|
186
|
+
log('š Validating Helper Metadata...\n', 'blue');
|
|
187
|
+
|
|
188
|
+
const helperFiles = fs.readdirSync(HELPERS_DIR)
|
|
189
|
+
.filter(f => f.endsWith('.md') && f !== 'helper-metadata.md');
|
|
190
|
+
|
|
191
|
+
let totalIssues = 0;
|
|
192
|
+
let totalErrors = 0;
|
|
193
|
+
let totalWarnings = 0;
|
|
194
|
+
|
|
195
|
+
// Validate each helper
|
|
196
|
+
for (const helperFile of helperFiles) {
|
|
197
|
+
const result = validateHelper(helperFile);
|
|
198
|
+
if (result.issues.length > 0) {
|
|
199
|
+
log(`\nš ${helperFile}:`, 'blue');
|
|
200
|
+
for (const issue of result.issues) {
|
|
201
|
+
if (issue.type === 'error') {
|
|
202
|
+
log(` ā ${issue.message}`, 'red');
|
|
203
|
+
totalErrors++;
|
|
204
|
+
} else {
|
|
205
|
+
log(` ā ļø ${issue.message}`, 'yellow');
|
|
206
|
+
totalWarnings++;
|
|
207
|
+
}
|
|
208
|
+
totalIssues++;
|
|
209
|
+
}
|
|
210
|
+
} else {
|
|
211
|
+
log(`ā
${helperFile}`, 'green');
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Validate registry
|
|
216
|
+
log('\nš Validating Helper Registry...\n', 'blue');
|
|
217
|
+
const registryResult = validateRegistry();
|
|
218
|
+
|
|
219
|
+
if (registryResult.issues.length > 0) {
|
|
220
|
+
for (const issue of registryResult.issues) {
|
|
221
|
+
if (issue.type === 'error') {
|
|
222
|
+
log(` ā ${issue.message}`, 'red');
|
|
223
|
+
totalErrors++;
|
|
224
|
+
} else {
|
|
225
|
+
log(` ā ļø ${issue.message}`, 'yellow');
|
|
226
|
+
totalWarnings++;
|
|
227
|
+
}
|
|
228
|
+
totalIssues++;
|
|
229
|
+
}
|
|
230
|
+
} else {
|
|
231
|
+
log('ā
Helper registry is consistent', 'green');
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Summary
|
|
235
|
+
log('\n' + '='.repeat(50), 'blue');
|
|
236
|
+
if (totalIssues === 0) {
|
|
237
|
+
log('ā
All validations passed!', 'green');
|
|
238
|
+
process.exit(0);
|
|
239
|
+
} else {
|
|
240
|
+
log(`\nš Summary:`, 'blue');
|
|
241
|
+
log(` Total issues: ${totalIssues}`, totalErrors > 0 ? 'red' : 'yellow');
|
|
242
|
+
log(` Errors: ${totalErrors}`, totalErrors > 0 ? 'red' : 'reset');
|
|
243
|
+
log(` Warnings: ${totalWarnings}`, totalWarnings > 0 ? 'yellow' : 'reset');
|
|
244
|
+
log('\nā ļø Some issues found. Please review and fix.', 'yellow');
|
|
245
|
+
process.exit(totalErrors > 0 ? 1 : 0);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
if (require.main === module) {
|
|
250
|
+
main();
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
module.exports = { validateHelper, validateRegistry };
|
|
254
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":"0.1.0","installed_date":"","installed_by":"cli","installation_type":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.1.0
|
|
@@ -26,4 +26,11 @@ List uncertainties, assumptions, or things that need clarification.
|
|
|
26
26
|
Only include risks that are already visible at this stage.
|
|
27
27
|
|
|
28
28
|
## 8. Notes
|
|
29
|
-
Anything else discovered during discussion or research.
|
|
29
|
+
Anything else discovered during discussion or research.
|
|
30
|
+
|
|
31
|
+
## 9. Related Specifications
|
|
32
|
+
If applicable, reference:
|
|
33
|
+
- `spec/01-prd.md` (if PRD was created)
|
|
34
|
+
- `spec/02-architecture.md` (if architecture is documented)
|
|
35
|
+
- `spec/07-design-system.md` (if design is critical)
|
|
36
|
+
- `spec/08-infrastructure.md` (if infrastructure is critical)
|