// resolver.dart — Maps brief values → template file paths (Pillar 3, 4 improvements) import 'models.dart'; class Resolver { /// Returns an ordered list of template file keys to render. /// Each key maps to: templates/.mdc.tmpl (or SKILL.md.tmpl for skills) static List resolve(ProjectBrief brief) { final files = []; // ── Universal (every project) ────────────────────────────────────── files.addAll([ 'rules/universal/flutter-core', 'rules/universal/ui-ux-standards', 'rules/universal/project-context', ]); // ── Security (Pillar 5: always-on, not just for large/auth projects) ── files.add('rules/security/security-standards'); // ── Error handling (always-on) ──────────────────────────────────── files.add('rules/error-handling/error-handling'); // ── State management — exactly one ─────────────────────────────── files.add('rules/state-management/${brief.stateManagement}'); // ── Architecture — exactly one ──────────────────────────────────── files.add('rules/architecture/${brief.architecture}'); // ── Backend — one or more ───────────────────────────────────────── for (final b in brief.backends) files.add('rules/backend/$b'); if (brief.specialFeatures.contains('realtime')) { files.add('rules/backend/realtime'); } // ── Routing — exactly one ───────────────────────────────────────── files.add('rules/routing/${brief.routing}'); // ── Testing — SM-matched ────────────────────────────────────────── files.add('rules/testing/testing-${brief.stateManagement}'); if (brief.testingDepth == 'full' || brief.testingDepth == 'e2e') { files.add('rules/testing/testing-e2e-${brief.e2eTool}'); } // ── Platform targets (Pillar 4) ─────────────────────────────────── for (final platform in brief.platforms) { files.add('rules/platform/platform-$platform'); } // ── Code generation tools (Pillar 4) ───────────────────────────── for (final tool in brief.codegenTools) { files.add('rules/codegen/codegen-$tool'); } // ── Hooks (Pillar 4) — tied to codegen, not state_management ───── if (brief.codegenTools.isNotEmpty) { files.addAll([ 'hooks/hooks-json', 'hooks/flutter-analyze', 'hooks/grind-tests', 'hooks/arch-guard', ]); } // ── Localization ────────────────────────────────────────────────── if (brief.i18nEnabled) { files.add('rules/i18n/localization'); } // ── Skills ──────────────────────────────────────────────────────── files.addAll([ 'skills/scaffold-feature', 'skills/scaffold-screen', 'skills/generate-tests', 'skills/build', ]); if (brief.apiDocsFormat != 'none') files.add('skills/generate-api-client'); if (brief.flavors.length > 1) files.add('skills/create-flavor'); if (brief.cicd.isNotEmpty) files.add('skills/deploy'); // ── Agents ──────────────────────────────────────────────────────── files.addAll([ 'agents/code-reviewer', 'agents/test-writer', 'agents/ui-validator', ]); if (brief.backends.contains('rest')) files.add('agents/api-client-gen'); // Pillar 5: Security agent as ADDITIONAL layer (rules are primary) if (brief.scale == 'large' || brief.auth != 'none') { files.add('agents/security-agent'); } if (brief.stateManagement == 'getx') { files.add('agents/migration-agent'); } files.addAll(['root/AGENTS.md', 'root/lefthook.yaml']); return files; } /// Returns human-readable description of why each file was included static Map resolveWithReasons(ProjectBrief brief) { final resolved = resolve(brief); final reasons = {}; for (final f in resolved) { reasons[f] = _reason(f, brief); } return reasons; } static String _reason(String key, ProjectBrief brief) { if (key.contains('universal')) return 'Always included'; if (key.contains('security')) return 'Always included — Pillar 5'; if (key.contains('error-handling')) return 'Always included'; if (key.contains('state-management')) return 'Matches stack.state_management: ${brief.stateManagement}'; if (key.contains('architecture')) return 'Matches stack.architecture: ${brief.architecture}'; if (key.contains('routing')) return 'Matches stack.routing: ${brief.routing}'; if (key.contains('testing-e2e')) return 'testing.depth includes e2e'; if (key.contains('testing')) return 'Matches state_management testing patterns'; if (key.contains('platform')) return 'Matches stack.platforms'; if (key.startsWith('hooks/')) { return 'stack.codegen non-empty — Cursor hooks for analyze, boundaries, and tests'; } if (key.contains('codegen')) return 'Matches stack.codegen'; if (key.contains('i18n')) return 'localization.enabled: true'; if (key.contains('migration')) return 'state_management is GetX — migration guidance included'; if (key.contains('security-agent')) return 'scale: ${brief.scale} or auth is configured'; if (key == 'skills/build') return 'Always included — universal TDD-first feature implementation command'; if (key.contains('api-client')) return 'api_docs.format is set'; if (key.contains('realtime')) return 'features.special contains realtime'; if (key.startsWith('root/')) return 'Repo-level companion files'; return 'Included'; } }