@@ -1142,6 +1142,77 @@ function generateClaudeMd(model: SemanticAppModel, stats: GeneratedProject['stat
11421142 lines . push ( '| `// TODO:` in loadData() | Morphkit detected a type mismatch and emitted a safe placeholder | Read the TODO comment for the specific mismatch and fix the types |' ) ;
11431143 lines . push ( '' ) ;
11441144
1145+ // ── Swift Conventions (Step 7) ──
1146+ lines . push ( '## Swift Conventions (Must Follow)' ) ;
1147+ lines . push ( '' ) ;
1148+ lines . push ( 'These rules prevent common AI coding mistakes in this project:' ) ;
1149+ lines . push ( '' ) ;
1150+ lines . push ( '| Rule | Wrong | Right |' ) ;
1151+ lines . push ( '|------|-------|-------|' ) ;
1152+ lines . push ( '| Observable pattern | `class Store: ObservableObject` | `@Observable class Store` |' ) ;
1153+ lines . push ( '| Task lifecycle | `.onAppear { Task { await load() } }` | `.task { await load() }` |' ) ;
1154+ lines . push ( '| Token storage | `UserDefaults.standard.set(token, ...)` | `KeychainHelper.save(key: "authToken", value: token)` |' ) ;
1155+ lines . push ( '| ForEach identity | `ForEach(items) { item in` | `ForEach(items, id: \\.id) { item in` or conform to `Identifiable` |' ) ;
1156+ lines . push ( '| State access | `@State var name = ""` | `@State private var name = ""` |' ) ;
1157+ lines . push ( '| Async button | `Button("Save") { await save() }` | `Button("Save") { Task { await save() } }` |' ) ;
1158+ lines . push ( '| Navigation | `NavigationView { }` | `NavigationStack { }` |' ) ;
1159+ lines . push ( '| Preview macro | `struct X_Previews: PreviewProvider` | `#Preview { }` |' ) ;
1160+ lines . push ( '| Error handling | `try! await ...` | `do { try await ... } catch { errorMessage = error.localizedDescription }` |' ) ;
1161+ lines . push ( '| List deletion | `.onDelete` with non-binding array | `.swipeActions { Button(role: .destructive) { } }` |' ) ;
1162+ lines . push ( '' ) ;
1163+
1164+ // ── Completion Manifest (Step 4) ──
1165+ lines . push ( '## Completion Manifest' ) ;
1166+ lines . push ( '' ) ;
1167+ lines . push ( 'Machine-readable project state for AI tooling. Do not edit manually.' ) ;
1168+ lines . push ( '' ) ;
1169+ lines . push ( '```json' ) ;
1170+
1171+ const manifestScreens = screens . map ( s => {
1172+ const isRef = referenceScreenNames . has ( s . name ) ;
1173+ const apiReqs = ( s . dataRequirements ?? [ ] ) . filter ( r => r . fetchStrategy === 'api' ) ;
1174+ const apiMethods = apiReqs . map ( r => {
1175+ const source = r . source ?? '' ;
1176+ return `fetch${ pascalCase ( source . endsWith ( 's' ) ? source : pluralize ( source ) ) } ` ;
1177+ } ) ;
1178+ // Determine navigation targets from actions
1179+ const navTargets = ( s . actions ?? [ ] )
1180+ . filter ( a => a . effect ?. type === 'navigate' )
1181+ . map ( a => pascalCase ( a . effect ?. target ?? '' ) )
1182+ . filter ( Boolean ) ;
1183+
1184+ const entry : Record < string , unknown > = {
1185+ name : s . name ,
1186+ file : `Views/${ pascalCase ( s . name ) } View.swift` ,
1187+ status : isRef ? 'reference' : 'scaffold' ,
1188+ layout : s . layout ?? 'custom' ,
1189+ } ;
1190+ if ( ! isRef ) {
1191+ entry . todoCount = '?' ; // Will be filled by verify
1192+ }
1193+ if ( apiMethods . length > 0 ) entry . apiMethods = apiMethods ;
1194+ if ( navTargets . length > 0 ) entry . navigatesTo = navTargets ;
1195+ return entry ;
1196+ } ) ;
1197+
1198+ const manifest = {
1199+ screens : manifestScreens ,
1200+ incompleteModels : incompleteEntities . map ( e => pascalCase ( e . name ) ) ,
1201+ unwiredEndpoints : endpoints
1202+ . filter ( ep => {
1203+ const url = ep . url . toLowerCase ( ) ;
1204+ return ! url . includes ( 'auth' ) && ! url . includes ( 'login' ) && ! url . includes ( 'register' ) ;
1205+ } )
1206+ . map ( ep => `${ ep . method ?? 'GET' } ${ ep . url . replace ( / ` / g, '' ) . replace ( / \$ \{ [ ^ } ] + \} / g, ':param' ) } ` ) ,
1207+ completionOrder : screens
1208+ . filter ( s => ! referenceScreenNames . has ( s . name ) )
1209+ . map ( s => s . name ) ,
1210+ } ;
1211+
1212+ lines . push ( JSON . stringify ( manifest , null , 2 ) ) ;
1213+ lines . push ( '```' ) ;
1214+ lines . push ( '' ) ;
1215+
11451216 // ── Warnings ──
11461217 if ( stats . warnings . length > 0 ) {
11471218 lines . push ( '## Build Warnings' ) ;
@@ -1348,6 +1419,203 @@ async function validateSwiftSyntax(appDir: string, files: GeneratedFile[]): Prom
13481419 return warnings ;
13491420}
13501421
1422+ // ---------------------------------------------------------------------------
1423+ // .claude/commands/ generation (Step 1)
1424+ // ---------------------------------------------------------------------------
1425+
1426+ function generateClaudeCommands ( model : SemanticAppModel ) : GeneratedFile [ ] {
1427+ const appName = pascalCase ( model . appName ?? 'MyApp' ) ;
1428+ const screens = ( model . screens ?? [ ] ) . filter ( s => ! isMarketingScreen ( s ) ) ;
1429+ const referenceScreenNames = getReferenceScreenNames ( model ) ;
1430+ const files : GeneratedFile [ ] = [ ] ;
1431+
1432+ // /complete-screen command
1433+ files . push ( {
1434+ path : '../.claude/commands/complete-screen.md' ,
1435+ content : `# Complete Screen: $ARGUMENTS
1436+
1437+ Gather all context needed to complete the screen named \`$ARGUMENTS\`, then implement all TODOs.
1438+
1439+ ## Steps
1440+
1441+ 1. Read \`${ appName } /Views/\${ARGUMENTS}View.swift\` — find all \`MORPHKIT-TODO:\` blocks
1442+ 2. Read the reference implementation: ${ [ ...referenceScreenNames ] . slice ( 0 , 2 ) . map ( n => `\`${ appName } /Views/${ pascalCase ( n ) } View.swift\`` ) . join ( ' or ' ) }
1443+ 3. Read relevant model files from \`${ appName } /Models/\` — check @State declarations for type names
1444+ 4. Read \`${ appName } /Networking/APIClient.swift\` — find the methods referenced in the TODOs
1445+ 5. For each MORPHKIT-TODO block:
1446+ - Follow the pattern shown in the Reference line
1447+ - Use the APIClient method specified
1448+ - Match the endpoint and request/response types
1449+ 6. Run \`swift build\` to verify the changes compile
1450+ 7. Report completion status
1451+
1452+ ## Rules
1453+ - Use \`@Observable\` not \`ObservableObject\`
1454+ - Use \`.task { }\` not \`.onAppear { Task { } }\`
1455+ - Use \`KeychainHelper\` not \`UserDefaults\` for tokens
1456+ - \`ForEach\` requires \`id:\` parameter or \`Identifiable\` conformance
1457+ - \`@State\` must be \`private\`
1458+ - Button async actions need \`Task { }\` wrapper
1459+ ` ,
1460+ sourceMapping : 'morphkit:claude-commands' ,
1461+ confidence : 'high' ,
1462+ warnings : [ ] ,
1463+ } ) ;
1464+
1465+ // /verify command
1466+ files . push ( {
1467+ path : '../.claude/commands/verify.md' ,
1468+ content : `# Verify Project Completion
1469+
1470+ Count remaining TODOs, check build status, and report completion percentage.
1471+
1472+ ## Steps
1473+
1474+ 1. Search all \`.swift\` files in \`${ appName } /\` for \`MORPHKIT-TODO:\` comments
1475+ 2. Group TODOs by category (wire-api-fetch, wire-api-action, wire-navigation, wire-mutation)
1476+ 3. Count TODOs per file from \`MORPHKIT-TODO-COUNT:\` headers
1477+ 4. Run \`swift build\` and report errors
1478+ 5. Check if API base URL is still placeholder in \`${ appName } /Networking/APIConfiguration.swift\`
1479+ 6. Check if auth is still stubbed in \`${ appName } /State/AuthManager.swift\`
1480+
1481+ ## Output Format
1482+
1483+ \`\`\`
1484+ Build Status: ✅ PASS | ❌ FAIL (N errors)
1485+ Screen Completion: N/M (XX%)
1486+ API Wiring: N/M (XX%)
1487+ Overall: XX% complete
1488+
1489+ TODO Breakdown:
1490+ wire-api-fetch: N
1491+ wire-api-action: N
1492+ wire-navigation: N
1493+
1494+ Files with TODOs:
1495+ Views/CartView.swift: 3 TODOs
1496+ Views/CheckoutView.swift: 2 TODOs
1497+
1498+ Next step: Complete <ScreenName> (N TODOs)
1499+ \`\`\`
1500+ ` ,
1501+ sourceMapping : 'morphkit:claude-commands' ,
1502+ confidence : 'high' ,
1503+ warnings : [ ] ,
1504+ } ) ;
1505+
1506+ // /next command
1507+ const screenList = screens
1508+ . filter ( s => ! referenceScreenNames . has ( s . name ) )
1509+ . map ( s => {
1510+ const apiReqs = ( s . dataRequirements ?? [ ] ) . filter ( r => r . fetchStrategy === 'api' ) ;
1511+ return `- ${ s . name } (${ s . layout } , ${ apiReqs . length } API deps, ${ ( s . actions ?? [ ] ) . length } actions)` ;
1512+ } ) . join ( '\n' ) ;
1513+
1514+ files . push ( {
1515+ path : '../.claude/commands/next.md' ,
1516+ content : `# What to Complete Next
1517+
1518+ Analyze which screen to complete next based on dependency order and TODO count.
1519+
1520+ ## Decision Criteria (in priority order)
1521+
1522+ 1. **Leaf screens first** — screens that don't navigate to other incomplete screens
1523+ 2. **Fewer TODOs** — easier wins build momentum
1524+ 3. **API method availability** — prefer screens whose APIClient methods are already implemented
1525+
1526+ ## Screens to Consider
1527+
1528+ ${ screenList }
1529+
1530+ ## Steps
1531+
1532+ 1. Read each View file's \`MORPHKIT-TODO-COUNT:\` header to get TODO counts
1533+ 2. Check the completion manifest in \`CLAUDE.md\` for dependency info (\`navigatesTo\` field)
1534+ 3. Identify leaf screens (screens whose \`navigatesTo\` targets are all complete or reference impls)
1535+ 4. Among leaves, pick the one with fewest TODOs
1536+ 5. Output: screen name, file path, TODO count, and what TODOs need to be resolved
1537+
1538+ ## Output Format
1539+
1540+ \`\`\`
1541+ Next: Complete <ScreenName>View (<N> TODOs)
1542+ File: ${ appName } /Views/<ScreenName>View.swift
1543+ Reason: Leaf screen with fewest TODOs
1544+
1545+ TODOs:
1546+ - wire-api-fetch: fetchItems() -> [Item] (GET /api/items)
1547+ - wire-api-action: addToCart(...) (POST /api/cart)
1548+
1549+ Run: /complete-screen <ScreenName>
1550+ \`\`\`
1551+ ` ,
1552+ sourceMapping : 'morphkit:claude-commands' ,
1553+ confidence : 'high' ,
1554+ warnings : [ ] ,
1555+ } ) ;
1556+
1557+ // /complete-all command
1558+ files . push ( {
1559+ path : '../.claude/commands/complete-all.md' ,
1560+ content : `# Complete All Screens
1561+
1562+ Systematically complete all remaining screens in dependency order.
1563+
1564+ ## Strategy
1565+
1566+ 1. Run \`/verify\` to get current completion status
1567+ 2. Read the reference implementation(s): ${ [ ...referenceScreenNames ] . slice ( 0 , 2 ) . map ( n => `\`${ appName } /Views/${ pascalCase ( n ) } View.swift\`` ) . join ( ', ' ) }
1568+ 3. Build a dependency graph from the completion manifest in \`CLAUDE.md\`
1569+ 4. Complete screens bottom-up (leaf screens first):
1570+ a. For each screen, run \`/complete-screen <name>\`
1571+ b. After each screen, run \`swift build\` to verify
1572+ c. If build fails, fix errors before moving to next screen
1573+ 5. After all screens: run \`/verify\` to confirm 100% completion
1574+
1575+ ## Completion Order Rules
1576+
1577+ - Reference implementation screens are already done — skip them
1578+ - Auth screens should be completed early (other screens may depend on auth state)
1579+ - Detail screens before their parent list screens (list needs NavigationLink targets)
1580+ - Settings/profile screens last (least critical path)
1581+
1582+ ## Rules
1583+ - Use \`@Observable\` not \`ObservableObject\`
1584+ - Use \`.task { }\` not \`.onAppear { Task { } }\`
1585+ - Use \`KeychainHelper\` not \`UserDefaults\` for tokens
1586+ - \`ForEach\` requires \`id:\` parameter or \`Identifiable\` conformance
1587+ - \`@State\` must be \`private\`
1588+ - Button async actions need \`Task { }\` wrapper
1589+ ` ,
1590+ sourceMapping : 'morphkit:claude-commands' ,
1591+ confidence : 'high' ,
1592+ warnings : [ ] ,
1593+ } ) ;
1594+
1595+ return files ;
1596+ }
1597+
1598+ // ---------------------------------------------------------------------------
1599+ // .claude/settings.json generation (Step 6)
1600+ // ---------------------------------------------------------------------------
1601+
1602+ function generateClaudeSettings ( ) : GeneratedFile {
1603+ return {
1604+ path : '../.claude/settings.json' ,
1605+ content : JSON . stringify ( {
1606+ mcpServers : {
1607+ morphkit : {
1608+ command : 'npx' ,
1609+ args : [ '-y' , 'morphkit-cli@latest' , 'mcp' ] ,
1610+ } ,
1611+ } ,
1612+ } , null , 2 ) + '\n' ,
1613+ sourceMapping : 'morphkit:claude-settings' ,
1614+ confidence : 'high' ,
1615+ warnings : [ ] ,
1616+ } ;
1617+ }
1618+
13511619// ---------------------------------------------------------------------------
13521620// Orchestrator
13531621// ---------------------------------------------------------------------------
@@ -1415,6 +1683,13 @@ export async function generateXcodeProject(
14151683 // 11. Xcode workspace settings (opens cleanly in Xcode)
14161684 allFiles . push ( generateWorkspaceSettings ( ) ) ;
14171685
1686+ // 12b. .claude/commands/ (slash commands for AI assistant workflow)
1687+ const commandFiles = generateClaudeCommands ( model ) ;
1688+ allFiles . push ( ...commandFiles ) ;
1689+
1690+ // 12c. .claude/settings.json (MCP server auto-registration)
1691+ allFiles . push ( generateClaudeSettings ( ) ) ;
1692+
14181693 // --- Calculate stats ---
14191694
14201695 const stats : GeneratedProject [ 'stats' ] = {
0 commit comments