@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.
Files changed (71) hide show
  1. package/.morph/.morphversion +3 -3
  2. package/.morph/analytics/threads-log.jsonl +6 -44
  3. package/.morph/config/config.json +1 -1
  4. package/.morph/framework/standards/frontend/nextjs/nextjs-patterns.md +17 -0
  5. package/.morph/framework/templates/docs/user-stories.md +34 -0
  6. package/.morph/logs/tool-failures.log +7 -51
  7. package/.morph/memory/{pre-compact-2026-02-22T17-01-01-658Z.json → pre-compact-2026-02-23T15-43-03-521Z.json} +1 -1
  8. package/CLAUDE.md +77 -56
  9. package/framework/{skills/level-2-domains → agents}/ai-agents/ai-system-architect.md +1 -4
  10. package/framework/{skills/level-2-domains → agents}/architecture/po-pm-advisor.md +1 -2
  11. package/framework/{skills/level-2-domains → agents}/architecture/prompt-engineer.md +1 -2
  12. package/framework/{skills/level-2-domains → agents}/architecture/seo-growth-hacker.md +1 -2
  13. package/framework/{skills/level-2-domains → agents}/architecture/standards-architect.md +1 -4
  14. package/framework/agents/backend/api-designer.md +103 -0
  15. package/framework/{skills/level-2-domains → agents}/backend/dotnet-senior.md +1 -2
  16. package/framework/agents/backend/ef-modeler.md +119 -0
  17. package/framework/{skills/level-2-domains → agents}/backend/hangfire-orchestrator.md +1 -4
  18. package/framework/{skills/level-2-domains → agents}/backend/ms-agent-expert.md +1 -4
  19. package/framework/{skills/level-2-domains → agents}/frontend/blazor-builder.md +1 -4
  20. package/framework/{skills/level-2-domains → agents}/frontend/nextjs-expert.md +1 -4
  21. package/framework/{skills/level-2-domains → agents}/frontend/ui-ux-designer.md +1 -2
  22. package/framework/{skills/level-2-domains → agents}/infrastructure/azure-architect.md +1 -2
  23. package/framework/{skills/level-2-domains → agents}/infrastructure/azure-deploy-specialist.md +1 -2
  24. package/framework/{skills/level-2-domains → agents}/infrastructure/bicep-architect.md +1 -4
  25. package/framework/{skills/level-2-domains → agents}/infrastructure/container-specialist.md +1 -4
  26. package/framework/{skills/level-2-domains → agents}/infrastructure/devops-engineer.md +1 -4
  27. package/framework/{skills/level-2-domains → agents}/integrations/asaas-financial.md +1 -4
  28. package/framework/{skills/level-2-domains → agents}/integrations/azure-identity.md +1 -4
  29. package/framework/{skills/level-2-domains → agents}/integrations/clerk-auth.md +1 -4
  30. package/framework/{skills/level-2-domains → agents}/integrations/hangfire-integration.md +1 -2
  31. package/framework/{skills/level-2-domains → agents}/integrations/resend-email.md +1 -4
  32. package/framework/{skills/level-2-domains → agents}/quality/code-analyzer.md +1 -4
  33. package/framework/{skills/level-2-domains → agents}/quality/testing-specialist.md +1 -4
  34. package/framework/hooks/claude-code/statusline.py +384 -85
  35. package/framework/hooks/shared/phase-utils.js +129 -129
  36. package/framework/skills/README.md +66 -0
  37. package/framework/skills/level-0-meta/{brainstorming.md → brainstorming/SKILL.md} +3 -1
  38. package/framework/skills/level-0-meta/brainstorming/references/proposal-example.md +138 -0
  39. package/framework/skills/level-0-meta/{code-review.md → code-review/SKILL.md} +3 -2
  40. package/framework/skills/level-0-meta/code-review/references/review-example.md +164 -0
  41. package/framework/skills/level-0-meta/code-review/scripts/scan-csharp.mjs +121 -0
  42. package/framework/skills/level-0-meta/{morph-checklist.md → morph-checklist/SKILL.md} +2 -5
  43. package/framework/skills/{level-1-workflows/morph-replicate.md → level-0-meta/morph-replicate/SKILL.md} +6 -7
  44. package/framework/skills/level-0-meta/{simulation-checklist.md → simulation-checklist/SKILL.md} +3 -6
  45. package/framework/skills/level-0-meta/{tool-usage-guide.md → tool-usage-guide/SKILL.md} +1 -2
  46. package/framework/skills/level-0-meta/{verification-before-completion.md → verification-before-completion/SKILL.md} +3 -1
  47. package/framework/skills/level-0-meta/verification-before-completion/scripts/check-phase-outputs.mjs +110 -0
  48. package/framework/skills/level-1-workflows/{phase-clarify.md → phase-clarify/SKILL.md} +3 -3
  49. package/framework/skills/level-1-workflows/phase-clarify/references/clarifications-example.md +117 -0
  50. package/framework/skills/level-1-workflows/{phase-codebase-analysis.md → phase-codebase-analysis/SKILL.md} +2 -3
  51. package/framework/skills/level-1-workflows/{phase-design.md → phase-design/SKILL.md} +13 -185
  52. package/framework/skills/level-1-workflows/phase-design/references/spec-example.md +253 -0
  53. package/framework/skills/level-1-workflows/{phase-implement.md → phase-implement/SKILL.md} +3 -3
  54. package/framework/skills/level-1-workflows/phase-implement/references/recap-example.md +132 -0
  55. package/framework/skills/level-1-workflows/{phase-setup.md → phase-setup/SKILL.md} +2 -3
  56. package/framework/skills/level-1-workflows/{phase-tasks.md → phase-tasks/SKILL.md} +4 -3
  57. package/framework/skills/level-1-workflows/phase-tasks/references/tasks-example.md +231 -0
  58. package/framework/skills/level-1-workflows/phase-tasks/scripts/validate-tasks.mjs +112 -0
  59. package/framework/skills/level-1-workflows/{phase-uiux.md → phase-uiux/SKILL.md} +2 -3
  60. package/package.json +1 -1
  61. package/src/commands/project/init.js +4 -64
  62. package/src/commands/project/update.js +1 -62
  63. package/src/lib/detectors/claude-config-detector.js +1 -3
  64. package/src/utils/agents-installer.js +2 -2
  65. package/src/utils/skills-installer.js +59 -15
  66. package/.morph/context/README.md +0 -17
  67. package/framework/skills/level-2-domains/backend/api-designer.md +0 -66
  68. package/framework/skills/level-2-domains/backend/ef-modeler.md +0 -65
  69. package/framework/skills/level-3-technologies/README.md +0 -7
  70. package/framework/skills/level-4-patterns/README.md +0 -7
  71. /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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@polymorphism-tech/morph-spec",
3
- "version": "4.5.0",
3
+ "version": "4.6.0",
4
4
  "description": "MORPH-SPEC: AI-First development framework with validation pipeline and multi-stack support",
5
5
  "keywords": [
6
6
  "claude-code",
@@ -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 create symlinks for skills
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
- if (symlinkCount > 0) {
546
- const linkType = process.platform === 'win32' ? 'junction-linked' : 'symlinked';
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/ (flat .md for /skill-name discovery)');
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 level-2-domains skill files as native Claude Code subagents
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, 'skills', 'level-2-domains');
181
+ const domainsDir = join(frameworkDir, 'agents');
182
182
  if (!existsSync(domainsDir)) return;
183
183
 
184
184
  const targetDir = join(projectDir, '.claude', 'agents');