@polymorphism-tech/morph-spec 4.5.0 → 4.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.
- package/.morph/.morphversion +3 -3
- package/.morph/analytics/threads-log.jsonl +6 -44
- package/.morph/config/config.json +1 -1
- package/.morph/framework/standards/frontend/nextjs/nextjs-patterns.md +17 -0
- package/.morph/framework/templates/docs/user-stories.md +34 -0
- package/.morph/logs/tool-failures.log +7 -51
- package/.morph/memory/{pre-compact-2026-02-22T17-01-01-658Z.json → pre-compact-2026-02-23T15-43-03-521Z.json} +1 -1
- package/CLAUDE.md +77 -56
- package/framework/{skills/level-2-domains → agents}/ai-agents/ai-system-architect.md +1 -4
- package/framework/{skills/level-2-domains → agents}/architecture/po-pm-advisor.md +1 -2
- package/framework/{skills/level-2-domains → agents}/architecture/prompt-engineer.md +1 -2
- package/framework/{skills/level-2-domains → agents}/architecture/seo-growth-hacker.md +1 -2
- package/framework/{skills/level-2-domains → agents}/architecture/standards-architect.md +1 -4
- package/framework/agents/backend/api-designer.md +103 -0
- package/framework/{skills/level-2-domains → agents}/backend/dotnet-senior.md +1 -2
- package/framework/agents/backend/ef-modeler.md +119 -0
- package/framework/{skills/level-2-domains → agents}/backend/hangfire-orchestrator.md +1 -4
- package/framework/{skills/level-2-domains → agents}/backend/ms-agent-expert.md +1 -4
- package/framework/{skills/level-2-domains → agents}/frontend/blazor-builder.md +1 -4
- package/framework/{skills/level-2-domains → agents}/frontend/nextjs-expert.md +1 -4
- package/framework/{skills/level-2-domains → agents}/frontend/ui-ux-designer.md +1 -2
- package/framework/{skills/level-2-domains → agents}/infrastructure/azure-architect.md +1 -2
- package/framework/{skills/level-2-domains → agents}/infrastructure/azure-deploy-specialist.md +1 -2
- package/framework/{skills/level-2-domains → agents}/infrastructure/bicep-architect.md +1 -4
- package/framework/{skills/level-2-domains → agents}/infrastructure/container-specialist.md +1 -4
- package/framework/{skills/level-2-domains → agents}/infrastructure/devops-engineer.md +1 -4
- package/framework/{skills/level-2-domains → agents}/integrations/asaas-financial.md +1 -4
- package/framework/{skills/level-2-domains → agents}/integrations/azure-identity.md +1 -4
- package/framework/{skills/level-2-domains → agents}/integrations/clerk-auth.md +1 -4
- package/framework/{skills/level-2-domains → agents}/integrations/hangfire-integration.md +1 -2
- package/framework/{skills/level-2-domains → agents}/integrations/resend-email.md +1 -4
- package/framework/{skills/level-2-domains → agents}/quality/code-analyzer.md +1 -4
- package/framework/{skills/level-2-domains → agents}/quality/testing-specialist.md +1 -4
- package/framework/hooks/claude-code/statusline.py +384 -85
- package/framework/hooks/shared/phase-utils.js +129 -129
- package/framework/skills/README.md +66 -0
- package/framework/skills/level-0-meta/{brainstorming.md → brainstorming/SKILL.md} +3 -1
- package/framework/skills/level-0-meta/brainstorming/references/proposal-example.md +138 -0
- package/framework/skills/level-0-meta/{code-review.md → code-review/SKILL.md} +3 -2
- package/framework/skills/level-0-meta/code-review/references/review-example.md +164 -0
- package/framework/skills/level-0-meta/code-review/scripts/scan-csharp.mjs +121 -0
- package/framework/skills/level-0-meta/{morph-checklist.md → morph-checklist/SKILL.md} +2 -5
- package/framework/skills/{level-1-workflows/morph-replicate.md → level-0-meta/morph-replicate/SKILL.md} +6 -7
- package/framework/skills/level-0-meta/{simulation-checklist.md → simulation-checklist/SKILL.md} +3 -6
- package/framework/skills/level-0-meta/{tool-usage-guide.md → tool-usage-guide/SKILL.md} +1 -2
- package/framework/skills/level-0-meta/{verification-before-completion.md → verification-before-completion/SKILL.md} +3 -1
- package/framework/skills/level-0-meta/verification-before-completion/scripts/check-phase-outputs.mjs +110 -0
- package/framework/skills/level-1-workflows/{phase-clarify.md → phase-clarify/SKILL.md} +3 -3
- package/framework/skills/level-1-workflows/phase-clarify/references/clarifications-example.md +117 -0
- package/framework/skills/level-1-workflows/{phase-codebase-analysis.md → phase-codebase-analysis/SKILL.md} +2 -3
- package/framework/skills/level-1-workflows/{phase-design.md → phase-design/SKILL.md} +13 -185
- package/framework/skills/level-1-workflows/phase-design/references/spec-example.md +253 -0
- package/framework/skills/level-1-workflows/{phase-implement.md → phase-implement/SKILL.md} +3 -3
- package/framework/skills/level-1-workflows/phase-implement/references/recap-example.md +132 -0
- package/framework/skills/level-1-workflows/{phase-setup.md → phase-setup/SKILL.md} +2 -3
- package/framework/skills/level-1-workflows/{phase-tasks.md → phase-tasks/SKILL.md} +4 -3
- package/framework/skills/level-1-workflows/phase-tasks/references/tasks-example.md +231 -0
- package/framework/skills/level-1-workflows/phase-tasks/scripts/validate-tasks.mjs +112 -0
- package/framework/skills/level-1-workflows/{phase-uiux.md → phase-uiux/SKILL.md} +2 -3
- package/package.json +1 -1
- package/src/commands/project/init.js +4 -64
- package/src/commands/project/update.js +1 -62
- package/src/lib/detectors/claude-config-detector.js +1 -3
- package/src/utils/agents-installer.js +2 -2
- package/src/utils/skills-installer.js +59 -15
- package/.morph/context/README.md +0 -17
- package/framework/skills/level-2-domains/backend/api-designer.md +0 -66
- package/framework/skills/level-2-domains/backend/ef-modeler.md +0 -65
- package/framework/skills/level-3-technologies/README.md +0 -7
- package/framework/skills/level-4-patterns/README.md +0 -7
- /package/framework/{skills/level-2-domains → agents}/README.md +0 -0
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
# Tasks - Photo Processing Pipeline
|
|
2
|
+
|
|
3
|
+
> Example of a well-structured tasks.md. This is a filled-in reference — not a template.
|
|
4
|
+
> All tasks are atomic (15–60 min), independently commitable, and directly traceable to spec.md.
|
|
5
|
+
|
|
6
|
+
| Task ID | Type | Title | Status |
|
|
7
|
+
|---------|------|-------|--------|
|
|
8
|
+
| T001 | Contract | Define C# contracts and interfaces | Done |
|
|
9
|
+
| T002 | Domain | Create ProcessingJob entity and enum | Done |
|
|
10
|
+
| T003 | Infra | EF Core migration and DbContext setup | Done |
|
|
11
|
+
| T004 | Infra | Azure Blob Storage service | Done |
|
|
12
|
+
| T005 | Service | Implement PhotoProcessingService | In Progress |
|
|
13
|
+
| T006 | Job | Implement Hangfire background job | Pending |
|
|
14
|
+
| T007 | API | Upload and status endpoints (Minimal API) | Pending |
|
|
15
|
+
| T008 | UI | Blazor upload page component | Pending |
|
|
16
|
+
| T009 | UI | Blazor status polling page | Pending |
|
|
17
|
+
| T010 | Test | Unit tests for PhotoProcessingService | Pending |
|
|
18
|
+
| T011 | Test | Integration tests for upload + status API | Pending |
|
|
19
|
+
|
|
20
|
+
**Status Values:** `Pending | In Progress | Blocked | Done`
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Phase 1: Contracts & Data Model
|
|
25
|
+
|
|
26
|
+
### T001: Define C# contracts and interfaces
|
|
27
|
+
- **Type:** Contract
|
|
28
|
+
- **Priority:** 1
|
|
29
|
+
- **Estimate:** 30min
|
|
30
|
+
- **Assigned:** dotnet-senior
|
|
31
|
+
- **Dependencies:** None
|
|
32
|
+
|
|
33
|
+
**Description:**
|
|
34
|
+
Define all service interfaces, command/result records, and DTOs needed for the feature. These become the API contract other tasks depend on — implement data model and services against these interfaces.
|
|
35
|
+
|
|
36
|
+
**Deliverables:**
|
|
37
|
+
- [ ] Create: `src/Application/PhotoProcessing/Interfaces/IPhotoProcessingService.cs`
|
|
38
|
+
- [ ] Create: `src/Application/PhotoProcessing/Commands/UploadPhotoCommand.cs`
|
|
39
|
+
- [ ] Create: `src/Application/PhotoProcessing/Queries/GetJobStatusQuery.cs`
|
|
40
|
+
- [ ] Create: `src/Application/PhotoProcessing/DTOs/JobStatusDto.cs`
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
### T002: Create ProcessingJob entity and enum
|
|
45
|
+
- **Type:** Domain
|
|
46
|
+
- **Priority:** 1
|
|
47
|
+
- **Estimate:** 30min
|
|
48
|
+
- **Assigned:** dotnet-senior
|
|
49
|
+
- **Dependencies:** T001
|
|
50
|
+
|
|
51
|
+
**Description:**
|
|
52
|
+
Create the `ProcessingJob` domain entity and `ProcessingStatus` enum per the data model in spec.md. Entity must use private setters and expose domain methods for state transitions (`MarkProcessing()`, `MarkCompleted()`, `MarkFailed()`).
|
|
53
|
+
|
|
54
|
+
**Deliverables:**
|
|
55
|
+
- [ ] Create: `src/Domain/Entities/ProcessingJob.cs`
|
|
56
|
+
- [ ] Create: `src/Domain/Enums/ProcessingStatus.cs`
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
### T003: EF Core migration and DbContext setup
|
|
61
|
+
- **Type:** Infrastructure
|
|
62
|
+
- **Priority:** 1
|
|
63
|
+
- **Estimate:** 45min
|
|
64
|
+
- **Assigned:** dotnet-senior
|
|
65
|
+
- **Dependencies:** T002
|
|
66
|
+
|
|
67
|
+
**Description:**
|
|
68
|
+
Add `ProcessingJob` to `AppDbContext`, configure via Fluent API (column lengths, indexes, defaults), and create initial migration. Verify migration applies cleanly to a local SQL Server instance.
|
|
69
|
+
|
|
70
|
+
**Deliverables:**
|
|
71
|
+
- [ ] Update: `src/Infrastructure/Persistence/AppDbContext.cs`
|
|
72
|
+
- [ ] Create: `src/Infrastructure/Persistence/Configurations/ProcessingJobConfiguration.cs`
|
|
73
|
+
- [ ] Run: `dotnet ef migrations add AddProcessingJob`
|
|
74
|
+
- [ ] Verify: migration applies without errors
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## Phase 2: Infrastructure Services
|
|
79
|
+
|
|
80
|
+
### T004: Azure Blob Storage service
|
|
81
|
+
- **Type:** Infrastructure
|
|
82
|
+
- **Priority:** 1
|
|
83
|
+
- **Estimate:** 45min
|
|
84
|
+
- **Assigned:** azure-architect
|
|
85
|
+
- **Dependencies:** None
|
|
86
|
+
|
|
87
|
+
**Description:**
|
|
88
|
+
Implement `IBlobStorageService` with upload and generate-URL operations using `Azure.Storage.Blobs` SDK. Register as scoped in DI. Add connection string to `appsettings.json` (placeholder) and Key Vault reference for production.
|
|
89
|
+
|
|
90
|
+
**Deliverables:**
|
|
91
|
+
- [ ] Create: `src/Infrastructure/Storage/IBlobStorageService.cs`
|
|
92
|
+
- [ ] Create: `src/Infrastructure/Storage/AzureBlobStorageService.cs`
|
|
93
|
+
- [ ] Create: `src/Infrastructure/Storage/FakeBlobStorageService.cs` (for simulation mode)
|
|
94
|
+
- [ ] Update: `src/Infrastructure/DependencyInjection.cs`
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
### T005: Implement PhotoProcessingService
|
|
99
|
+
- **Type:** Service
|
|
100
|
+
- **Priority:** 2
|
|
101
|
+
- **Estimate:** 1h
|
|
102
|
+
- **Assigned:** dotnet-senior
|
|
103
|
+
- **Dependencies:** T001, T002, T003, T004
|
|
104
|
+
|
|
105
|
+
**Description:**
|
|
106
|
+
Implement `IPhotoProcessingService` with `UploadAsync` (validate → blob upload → create job → enqueue Hangfire job) and `GetStatusAsync`. Use result pattern for validation errors; throw `NotFoundException` for missing jobs.
|
|
107
|
+
|
|
108
|
+
**Deliverables:**
|
|
109
|
+
- [ ] Create: `src/Application/PhotoProcessing/PhotoProcessingService.cs`
|
|
110
|
+
- [ ] Update: `src/Application/DependencyInjection.cs`
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
### T006: Implement Hangfire background job
|
|
115
|
+
- **Type:** Job
|
|
116
|
+
- **Priority:** 2
|
|
117
|
+
- **Estimate:** 1h
|
|
118
|
+
- **Assigned:** dotnet-senior
|
|
119
|
+
- **Dependencies:** T002, T004
|
|
120
|
+
|
|
121
|
+
**Description:**
|
|
122
|
+
Implement `IPhotoProcessingJob` that: sets status = Processing → downloads original from Blob → calls AI API → uploads processed photo → sets status = Completed. On exception: increment RetryCount, set Failed if >= 3. Add `[AutomaticRetry(Attempts = 3)]` attribute.
|
|
123
|
+
|
|
124
|
+
**Deliverables:**
|
|
125
|
+
- [ ] Create: `src/Application/PhotoProcessing/PhotoProcessingJob.cs`
|
|
126
|
+
- [ ] Create: `src/Application/PhotoProcessing/FakePhotoProcessingJob.cs` (simulation mode)
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## Phase 3: API Endpoints
|
|
131
|
+
|
|
132
|
+
### T007: Upload and status endpoints (Minimal API)
|
|
133
|
+
- **Type:** API
|
|
134
|
+
- **Priority:** 2
|
|
135
|
+
- **Estimate:** 45min
|
|
136
|
+
- **Assigned:** dotnet-senior
|
|
137
|
+
- **Dependencies:** T005
|
|
138
|
+
|
|
139
|
+
**Description:**
|
|
140
|
+
Create two endpoints per spec.md:
|
|
141
|
+
- `POST /api/upload` — multipart/form-data, returns `{ jobId }`, maps to `UploadPhotoCommand`
|
|
142
|
+
- `GET /api/status/{jobId}` — returns `JobStatusDto`
|
|
143
|
+
- `GET /api/download/{jobId}` — redirects to Blob URL if Completed, else 404
|
|
144
|
+
|
|
145
|
+
**Deliverables:**
|
|
146
|
+
- [ ] Create: `src/Web/Endpoints/PhotoProcessingEndpoints.cs`
|
|
147
|
+
- [ ] Update: `src/Web/Program.cs` to register endpoint group
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
## Phase 4: UI Components
|
|
152
|
+
|
|
153
|
+
### T008: Blazor upload page component
|
|
154
|
+
- **Type:** UI
|
|
155
|
+
- **Priority:** 2
|
|
156
|
+
- **Estimate:** 1h
|
|
157
|
+
- **Assigned:** blazor-builder
|
|
158
|
+
- **Dependencies:** T007
|
|
159
|
+
|
|
160
|
+
**Description:**
|
|
161
|
+
Blazor Server page at `/upload`. File input with client-side validation (type + size before submit). Loading state during upload. Redirect to `/processing/{jobId}` on success. Error message display per spec wireframe.
|
|
162
|
+
|
|
163
|
+
**Deliverables:**
|
|
164
|
+
- [ ] Create: `src/Web/Pages/Upload.razor`
|
|
165
|
+
- [ ] Create: `src/Web/Pages/Upload.razor.cs`
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
### T009: Blazor status polling page
|
|
170
|
+
- **Type:** UI
|
|
171
|
+
- **Priority:** 3
|
|
172
|
+
- **Estimate:** 1h
|
|
173
|
+
- **Assigned:** blazor-builder
|
|
174
|
+
- **Dependencies:** T007
|
|
175
|
+
|
|
176
|
+
**Description:**
|
|
177
|
+
Blazor Server page at `/processing/{jobId}`. Polls `GET /api/status/{jobId}` every 5 seconds using `PeriodicTimer`. Shows status label + progress indicator. "Download" button when Completed. "Retry" suggestion when Failed. Stops polling on terminal state.
|
|
178
|
+
|
|
179
|
+
**Deliverables:**
|
|
180
|
+
- [ ] Create: `src/Web/Pages/ProcessingStatus.razor`
|
|
181
|
+
- [ ] Create: `src/Web/Pages/ProcessingStatus.razor.cs`
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## Phase 5: Tests
|
|
186
|
+
|
|
187
|
+
### T010: Unit tests for PhotoProcessingService
|
|
188
|
+
- **Type:** Test
|
|
189
|
+
- **Priority:** 2
|
|
190
|
+
- **Estimate:** 1h
|
|
191
|
+
- **Assigned:** dotnet-senior
|
|
192
|
+
- **Dependencies:** T005
|
|
193
|
+
|
|
194
|
+
**Description:**
|
|
195
|
+
Unit tests covering: happy path upload, invalid file type rejection, file too large rejection, status retrieval for each status value, not-found case. Use Moq for `IBlobStorageService` and `IProcessingJobRepository`. FluentAssertions for assertions.
|
|
196
|
+
|
|
197
|
+
**Deliverables:**
|
|
198
|
+
- [ ] Create: `tests/Unit/Application/PhotoProcessing/PhotoProcessingServiceTests.cs`
|
|
199
|
+
|
|
200
|
+
---
|
|
201
|
+
|
|
202
|
+
### T011: Integration tests for upload + status API
|
|
203
|
+
- **Type:** Test
|
|
204
|
+
- **Priority:** 3
|
|
205
|
+
- **Estimate:** 1h
|
|
206
|
+
- **Assigned:** dotnet-senior
|
|
207
|
+
- **Dependencies:** T007, T010
|
|
208
|
+
|
|
209
|
+
**Description:**
|
|
210
|
+
Integration tests using `WebApplicationFactory` + in-memory SQLite (or TestContainers for SQL Server). Tests: POST /api/upload returns 200 with jobId, GET /api/status/{jobId} returns correct status, GET /api/download/{completedJobId} returns redirect.
|
|
211
|
+
|
|
212
|
+
**Deliverables:**
|
|
213
|
+
- [ ] Create: `tests/Integration/Api/PhotoProcessingEndpointTests.cs`
|
|
214
|
+
- [ ] Create: `tests/Integration/Fixtures/PhotoProcessingApiFixture.cs`
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
## Summary
|
|
219
|
+
|
|
220
|
+
| Phase | Tasks | Estimate |
|
|
221
|
+
|-------|-------|----------|
|
|
222
|
+
| Contracts & Data Model | T001–T003 | 1h45m |
|
|
223
|
+
| Infrastructure Services | T004–T006 | 2h45m |
|
|
224
|
+
| API Endpoints | T007 | 45m |
|
|
225
|
+
| UI Components | T008–T009 | 2h |
|
|
226
|
+
| Tests | T010–T011 | 2h |
|
|
227
|
+
| **Total** | **11 tasks** | **~9h15m** |
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
|
|
231
|
+
*MORPH-SPEC by Polymorphism Tech*
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* validate-tasks.mjs
|
|
4
|
+
*
|
|
5
|
+
* Validates a MORPH-SPEC tasks.md file for structure and completeness.
|
|
6
|
+
* Executed locally by Claude via Bash — output returned to Claude.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* node validate-tasks.mjs <tasks-file>
|
|
10
|
+
* node validate-tasks.mjs .morph/features/my-feature/3-tasks/tasks.md
|
|
11
|
+
*
|
|
12
|
+
* Checks:
|
|
13
|
+
* - Summary table exists with T### IDs and valid statuses
|
|
14
|
+
* - Each task header has T### format
|
|
15
|
+
* - Each task has Type, Priority, Estimate, Dependencies fields
|
|
16
|
+
* - Each task has at least one deliverable checkbox
|
|
17
|
+
* - Task IDs are sequential (no gaps)
|
|
18
|
+
* - Reports counts by status
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import { readFileSync, existsSync } from 'fs';
|
|
22
|
+
|
|
23
|
+
const taskFile = process.argv[2];
|
|
24
|
+
|
|
25
|
+
if (!taskFile) {
|
|
26
|
+
console.error(JSON.stringify({ error: 'Usage: validate-tasks.mjs <tasks-file>' }));
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (!existsSync(taskFile)) {
|
|
31
|
+
console.error(JSON.stringify({ error: `File not found: ${taskFile}` }));
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const content = readFileSync(taskFile, 'utf-8');
|
|
36
|
+
const lines = content.split('\n');
|
|
37
|
+
|
|
38
|
+
const errors = [];
|
|
39
|
+
const warnings = [];
|
|
40
|
+
|
|
41
|
+
// 1. Parse summary table (| T001 | Type | Title | Status |)
|
|
42
|
+
const tableRows = lines.filter(l => /^\|\s*T\d{3}/.test(l));
|
|
43
|
+
const VALID_STATUSES = new Set(['Pending', 'In Progress', 'Blocked', 'Done']);
|
|
44
|
+
const summaryTasks = [];
|
|
45
|
+
|
|
46
|
+
for (const row of tableRows) {
|
|
47
|
+
const parts = row.split('|').map(s => s.trim()).filter(Boolean);
|
|
48
|
+
if (parts.length < 4) continue;
|
|
49
|
+
const [id, , , status] = parts;
|
|
50
|
+
summaryTasks.push({ id, status });
|
|
51
|
+
if (!VALID_STATUSES.has(status)) {
|
|
52
|
+
errors.push(`Task ${id}: invalid status '${status}' — must be one of: ${[...VALID_STATUSES].join(', ')}`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (summaryTasks.length === 0) {
|
|
57
|
+
errors.push('No summary table found — expected rows matching | T### | Type | Title | Status |');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// 2. Parse task headers (### T001: Title)
|
|
61
|
+
const taskHeaders = lines
|
|
62
|
+
.map((l, i) => ({ line: i + 1, match: l.match(/^###\s+(T\d{3}):\s+(.+)/) }))
|
|
63
|
+
.filter(r => r.match);
|
|
64
|
+
|
|
65
|
+
// Check sequential IDs
|
|
66
|
+
const ids = taskHeaders.map(r => parseInt(r.match[1].slice(1)));
|
|
67
|
+
for (let i = 0; i < ids.length; i++) {
|
|
68
|
+
if (ids[i] !== i + 1) {
|
|
69
|
+
warnings.push(`Task ID gap or duplicate near T${String(ids[i]).padStart(3, '0')} (expected T${String(i + 1).padStart(3, '0')})`);
|
|
70
|
+
break;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// 3. Check each task section has required fields
|
|
75
|
+
for (const { line, match } of taskHeaders) {
|
|
76
|
+
const taskId = match[1];
|
|
77
|
+
// Collect lines in this task section (until next ### or end)
|
|
78
|
+
const start = line; // 1-indexed
|
|
79
|
+
let end = lines.length;
|
|
80
|
+
for (let i = start; i < lines.length; i++) {
|
|
81
|
+
if (i > start - 1 && /^#{1,3}\s/.test(lines[i])) { end = i; break; }
|
|
82
|
+
}
|
|
83
|
+
const section = lines.slice(start, end).join('\n');
|
|
84
|
+
|
|
85
|
+
if (!/\*\*Type:\*\*/.test(section)) errors.push(`${taskId}: missing **Type:** field`);
|
|
86
|
+
if (!/\*\*Priority:\*\*/.test(section)) errors.push(`${taskId}: missing **Priority:** field`);
|
|
87
|
+
if (!/\*\*Estimate:\*\*/.test(section)) errors.push(`${taskId}: missing **Estimate:** field`);
|
|
88
|
+
if (!/\*\*Dependencies:\*\*/.test(section)) warnings.push(`${taskId}: missing **Dependencies:** field`);
|
|
89
|
+
if (!/- \[ \]/.test(section)) warnings.push(`${taskId}: no deliverable checkboxes found`);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// 4. Status counts
|
|
93
|
+
const statusCounts = {};
|
|
94
|
+
for (const { status } of summaryTasks) {
|
|
95
|
+
statusCounts[status] = (statusCounts[status] ?? 0) + 1;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const result = {
|
|
99
|
+
file: taskFile,
|
|
100
|
+
valid: errors.length === 0,
|
|
101
|
+
tasks: {
|
|
102
|
+
total: summaryTasks.length,
|
|
103
|
+
byStatus: statusCounts,
|
|
104
|
+
completed: statusCounts['Done'] ?? 0,
|
|
105
|
+
remaining: (summaryTasks.length) - (statusCounts['Done'] ?? 0),
|
|
106
|
+
},
|
|
107
|
+
errors,
|
|
108
|
+
warnings,
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
console.log(JSON.stringify(result, null, 2));
|
|
112
|
+
process.exit(errors.length > 0 ? 1 : 0);
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: phase-uiux
|
|
3
|
-
description:
|
|
4
|
-
MORPH-SPEC Phase 1.5 (UI/UX). Creates design-system.md, mockups.md, components.md, and flows.md before technical spec. Auto-activates for features with UI keywords.
|
|
3
|
+
description: MORPH-SPEC Phase 1.5 (UI/UX). Creates design-system.md, mockups.md, components.md, and flows.md using Figma/Playwright/Context7 MCPs. Use after setup for features with frontend UI components, pages, dashboards, forms, wizards, or visual interactions requiring design documentation.
|
|
5
4
|
argument-hint: "[feature-name]"
|
|
6
5
|
user-invocable: false
|
|
7
6
|
allowed-tools: Read, Write, Edit, Bash, Glob, Grep
|
|
@@ -21,7 +20,7 @@ Fase condicional para features com front-end. Coleta requisitos de UI/UX, gera w
|
|
|
21
20
|
|
|
22
21
|
## Ferramentas Recomendadas
|
|
23
22
|
|
|
24
|
-
> **Ref:** `framework/skills/level-0-meta/tool-usage-guide.md` para guia completo.
|
|
23
|
+
> **Ref:** `framework/skills/level-0-meta/tool-usage-guide/SKILL.md` para guia completo.
|
|
25
24
|
> **Ref:** `framework/standards/integration/mcp/mcp-tools.md` para referência MCP.
|
|
26
25
|
|
|
27
26
|
| Ação | Ferramenta | Alternativa |
|
package/package.json
CHANGED
|
@@ -4,7 +4,6 @@ import ora from 'ora';
|
|
|
4
4
|
import chalk from 'chalk';
|
|
5
5
|
import { logger } from '../../utils/logger.js';
|
|
6
6
|
import {
|
|
7
|
-
getContentDir,
|
|
8
7
|
copyDirectory,
|
|
9
8
|
copyFile,
|
|
10
9
|
pathExists,
|
|
@@ -13,9 +12,7 @@ import {
|
|
|
13
12
|
ensureDir,
|
|
14
13
|
writeFile,
|
|
15
14
|
readFile,
|
|
16
|
-
updateGitignore
|
|
17
|
-
createSymlink,
|
|
18
|
-
createDirectoryLink
|
|
15
|
+
updateGitignore
|
|
19
16
|
} from '../../utils/file-copier.js';
|
|
20
17
|
import { saveProjectMorphVersion, getInstalledCLIVersion } from '../../utils/version-checker.js';
|
|
21
18
|
import { installClaudeHooks, installGlobalStatusline } from '../../utils/claude-settings-manager.js';
|
|
@@ -122,7 +119,6 @@ Run \`morph-spec detect\` to analyze your project.
|
|
|
122
119
|
await writeFile(contextReadme, readmeContent);
|
|
123
120
|
|
|
124
121
|
// 5. Copy framework templates (project-local overrides start from framework defaults)
|
|
125
|
-
const contentDir = getContentDir();
|
|
126
122
|
const frameworkTemplatesSrc = join(import.meta.dirname, '..', '..', '..', 'framework', 'templates');
|
|
127
123
|
const templatesDest = join(frameworkDestDir, 'templates');
|
|
128
124
|
let templatesCopied = false;
|
|
@@ -151,14 +147,12 @@ Run \`morph-spec detect\` to analyze your project.
|
|
|
151
147
|
await copyFile(agentsSrc, agentsDest);
|
|
152
148
|
}
|
|
153
149
|
|
|
154
|
-
// 8. Copy .claude commands and
|
|
150
|
+
// 8. Copy .claude commands and install skills
|
|
155
151
|
// Source: framework/ (canonical for all stacks)
|
|
156
152
|
spinner.text = 'Setting up Claude Code integration...';
|
|
157
153
|
const frameworkDir = join(import.meta.dirname, '..', '..', '..', 'framework');
|
|
158
154
|
const claudeDest = join(targetPath, '.claude');
|
|
159
155
|
|
|
160
|
-
let symlinkCount = 0;
|
|
161
|
-
let copyCount = 0;
|
|
162
156
|
let commandsCopied = false;
|
|
163
157
|
|
|
164
158
|
// Copy commands directory (slash commands): framework/commands/ → .claude/commands/
|
|
@@ -171,55 +165,6 @@ Run \`morph-spec detect\` to analyze your project.
|
|
|
171
165
|
logger.warn(' ⚠ framework/commands/ source missing — commands not installed');
|
|
172
166
|
}
|
|
173
167
|
|
|
174
|
-
// Create directory links for skill categories (or copy if linking fails)
|
|
175
|
-
// Source: framework/skills/ → .claude/skills/
|
|
176
|
-
{
|
|
177
|
-
const skillsSrc = join(frameworkDir, 'skills');
|
|
178
|
-
const skillsDest = join(claudeDest, 'skills');
|
|
179
|
-
|
|
180
|
-
if (await pathExists(skillsSrc)) {
|
|
181
|
-
await ensureDir(skillsDest);
|
|
182
|
-
|
|
183
|
-
const entries = await fs.readdir(skillsSrc, { withFileTypes: true });
|
|
184
|
-
|
|
185
|
-
let linkedCategories = 0;
|
|
186
|
-
let copiedCategories = 0;
|
|
187
|
-
let totalSkillFiles = 0;
|
|
188
|
-
|
|
189
|
-
// Link category directories (specialists/, infra/, checklists/, etc.)
|
|
190
|
-
for (const entry of entries) {
|
|
191
|
-
if (entry.isDirectory()) {
|
|
192
|
-
const categorySrc = join(skillsSrc, entry.name);
|
|
193
|
-
const categoryDest = join(skillsDest, entry.name);
|
|
194
|
-
|
|
195
|
-
// Count .md files for reporting
|
|
196
|
-
const categoryEntries = await fs.readdir(categorySrc, { withFileTypes: true });
|
|
197
|
-
const mdFiles = categoryEntries.filter(e => e.isFile() && e.name.endsWith('.md'));
|
|
198
|
-
totalSkillFiles += mdFiles.length;
|
|
199
|
-
|
|
200
|
-
const result = await createDirectoryLink(categorySrc, categoryDest);
|
|
201
|
-
if (result === 'copy') {
|
|
202
|
-
copiedCategories++;
|
|
203
|
-
} else {
|
|
204
|
-
linkedCategories++;
|
|
205
|
-
}
|
|
206
|
-
} else if (entry.isFile() && entry.name.endsWith('.md')) {
|
|
207
|
-
// Handle .md files in skills root
|
|
208
|
-
totalSkillFiles++;
|
|
209
|
-
const result = await createSymlink(join(skillsSrc, entry.name), join(skillsDest, entry.name), 'file');
|
|
210
|
-
if (result === 'symlink') {
|
|
211
|
-
linkedCategories++;
|
|
212
|
-
} else {
|
|
213
|
-
copiedCategories++;
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
symlinkCount = linkedCategories > 0 ? totalSkillFiles : 0;
|
|
219
|
-
copyCount = linkedCategories === 0 ? totalSkillFiles : 0;
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
|
|
223
168
|
// 9a. Install path-scoped rules to .claude/rules/ (native Claude Code pattern)
|
|
224
169
|
spinner.text = 'Installing path-scoped rules to .claude/rules/...';
|
|
225
170
|
const rulesSrc = join(frameworkDir, 'rules');
|
|
@@ -542,13 +487,8 @@ Run \`morph-spec detect\` to analyze your project.
|
|
|
542
487
|
}
|
|
543
488
|
}
|
|
544
489
|
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
logger.dim(` ✓ .claude/skills/ (${symlinkCount} ${linkType})`);
|
|
548
|
-
} else if (copyCount > 0) {
|
|
549
|
-
logger.dim(` ✓ .claude/skills/ (${copyCount} copied)`);
|
|
550
|
-
logger.warn(` ⚠ Directory links not supported (copied instead). Skills won't auto-update.`);
|
|
551
|
-
}
|
|
490
|
+
logger.dim(' ✓ .claude/skills/ (installed as skill directories)');
|
|
491
|
+
logger.dim(' ✓ .claude/agents/ (native subagents installed)');
|
|
552
492
|
|
|
553
493
|
logger.blank();
|
|
554
494
|
|
|
@@ -14,8 +14,6 @@ import {
|
|
|
14
14
|
copyFile,
|
|
15
15
|
pathExists,
|
|
16
16
|
ensureDir,
|
|
17
|
-
createDirectoryLink,
|
|
18
|
-
createSymlink,
|
|
19
17
|
readJson,
|
|
20
18
|
writeJson
|
|
21
19
|
} from '../../utils/file-copier.js';
|
|
@@ -239,8 +237,6 @@ export async function updateCommand(options) {
|
|
|
239
237
|
const frameworkDir = join(__dirname, '..', '..', '..', 'framework');
|
|
240
238
|
const claudeDest = join(targetPath, '.claude');
|
|
241
239
|
|
|
242
|
-
let symlinkCount = 0;
|
|
243
|
-
let copyCount = 0;
|
|
244
240
|
let commandsCopied = false;
|
|
245
241
|
|
|
246
242
|
// Copy commands directory (slash commands): framework/commands/ → .claude/commands/
|
|
@@ -253,55 +249,6 @@ export async function updateCommand(options) {
|
|
|
253
249
|
logger.warn(' ⚠ framework/commands/ source missing — commands not updated');
|
|
254
250
|
}
|
|
255
251
|
|
|
256
|
-
// Create directory links for skill categories (or copy if linking fails)
|
|
257
|
-
// Source: framework/skills/ → .claude/skills/
|
|
258
|
-
{
|
|
259
|
-
const skillsSrc = join(frameworkDir, 'skills');
|
|
260
|
-
const skillsDest = join(claudeDest, 'skills');
|
|
261
|
-
|
|
262
|
-
if (await pathExists(skillsSrc)) {
|
|
263
|
-
await ensureDir(skillsDest);
|
|
264
|
-
|
|
265
|
-
const entries = await fs.readdir(skillsSrc, { withFileTypes: true });
|
|
266
|
-
|
|
267
|
-
let linkedCategories = 0;
|
|
268
|
-
let copiedCategories = 0;
|
|
269
|
-
let totalSkillFiles = 0;
|
|
270
|
-
|
|
271
|
-
// Link category directories (specialists/, infra/, checklists/, etc.)
|
|
272
|
-
for (const entry of entries) {
|
|
273
|
-
if (entry.isDirectory()) {
|
|
274
|
-
const categorySrc = join(skillsSrc, entry.name);
|
|
275
|
-
const categoryDest = join(skillsDest, entry.name);
|
|
276
|
-
|
|
277
|
-
// Count .md files for reporting
|
|
278
|
-
const categoryEntries = await fs.readdir(categorySrc, { withFileTypes: true });
|
|
279
|
-
const mdFiles = categoryEntries.filter(e => e.isFile() && e.name.endsWith('.md'));
|
|
280
|
-
totalSkillFiles += mdFiles.length;
|
|
281
|
-
|
|
282
|
-
const result = await createDirectoryLink(categorySrc, categoryDest);
|
|
283
|
-
if (result === 'copy') {
|
|
284
|
-
copiedCategories++;
|
|
285
|
-
} else {
|
|
286
|
-
linkedCategories++;
|
|
287
|
-
}
|
|
288
|
-
} else if (entry.isFile() && entry.name.endsWith('.md')) {
|
|
289
|
-
// Handle .md files in skills root
|
|
290
|
-
totalSkillFiles++;
|
|
291
|
-
const result = await createSymlink(join(skillsSrc, entry.name), join(skillsDest, entry.name), 'file');
|
|
292
|
-
if (result === 'symlink') {
|
|
293
|
-
linkedCategories++;
|
|
294
|
-
} else {
|
|
295
|
-
copiedCategories++;
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
symlinkCount = linkedCategories > 0 ? totalSkillFiles : 0;
|
|
301
|
-
copyCount = linkedCategories === 0 ? totalSkillFiles : 0;
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
|
|
305
252
|
// Sync morph skills to .claude/skills/ for native Claude Code discovery
|
|
306
253
|
updateSpinner.text = 'Syncing morph skills to .claude/skills/...';
|
|
307
254
|
await installSkills(targetPath);
|
|
@@ -365,16 +312,8 @@ export async function updateCommand(options) {
|
|
|
365
312
|
logger.dim(' ✓ .morph/framework/agents.json');
|
|
366
313
|
if (commandsCopied) logger.dim(' ✓ .claude/commands/');
|
|
367
314
|
|
|
368
|
-
if (symlinkCount > 0) {
|
|
369
|
-
const linkType = process.platform === 'win32' ? 'junction-linked' : 'symlinked';
|
|
370
|
-
logger.dim(` ✓ .claude/skills/ (${symlinkCount} ${linkType})`);
|
|
371
|
-
} else if (copyCount > 0) {
|
|
372
|
-
logger.dim(` ✓ .claude/skills/ (${copyCount} copied)`);
|
|
373
|
-
logger.warn(` ⚠ Directory links not supported (copied instead). Skills won't auto-update.`);
|
|
374
|
-
}
|
|
375
|
-
|
|
376
315
|
logger.dim(` ✓ .claude/settings.local.json (${hooksResult.installed} hooks installed)`);
|
|
377
|
-
logger.dim(' ✓ .claude/skills/ (
|
|
316
|
+
logger.dim(' ✓ .claude/skills/ (skills installed as directories)');
|
|
378
317
|
logger.dim(' ✓ .claude/agents/ (native subagents refreshed)');
|
|
379
318
|
logger.dim(' ✓ .claude/rules/ (path-scoped rules synced)');
|
|
380
319
|
logger.dim(' ✓ .claude/CLAUDE.md (runtime quick reference)');
|
|
@@ -144,9 +144,7 @@ function detectCustomSkills(globalClaudeDir, projectClaudeDir) {
|
|
|
144
144
|
for (const file of walkMdFiles(projectSkills)) {
|
|
145
145
|
const name = extractSkillName(file);
|
|
146
146
|
// Skip morph-spec framework skills (they're managed by init)
|
|
147
|
-
if (file.includes('level-0-meta') || file.includes('level-1-workflows')
|
|
148
|
-
file.includes('level-2-domains') || file.includes('level-3-technologies') ||
|
|
149
|
-
file.includes('level-4-patterns')) {
|
|
147
|
+
if (file.includes('level-0-meta') || file.includes('level-1-workflows')) {
|
|
150
148
|
continue;
|
|
151
149
|
}
|
|
152
150
|
skills.push({
|
|
@@ -171,14 +171,14 @@ export function collectSkillFiles(dir) {
|
|
|
171
171
|
}
|
|
172
172
|
|
|
173
173
|
/**
|
|
174
|
-
* Installs
|
|
174
|
+
* Installs domain agent files as native Claude Code subagents
|
|
175
175
|
* in .claude/agents/ with memory:project, model:sonnet, maxTurns:20.
|
|
176
176
|
*
|
|
177
177
|
* @param {string} projectDir - Target project directory
|
|
178
178
|
* @param {string} frameworkDir - Path to morph-spec framework/ directory
|
|
179
179
|
*/
|
|
180
180
|
export async function installDomainAgents(projectDir, frameworkDir = 'framework') {
|
|
181
|
-
const domainsDir = join(frameworkDir, '
|
|
181
|
+
const domainsDir = join(frameworkDir, 'agents');
|
|
182
182
|
if (!existsSync(domainsDir)) return;
|
|
183
183
|
|
|
184
184
|
const targetDir = join(projectDir, '.claude', 'agents');
|