Compare commits

...

2 Commits

Author SHA1 Message Date
mansi.kansara da64f769da feat(flutter-cursor-templates): introduce MCP integration and conventions in project brief
- Added optional MCP integration settings in project-brief.yaml, allowing for environment-based server configurations.
- Introduced conventions for strict package imports to enhance code organization and maintainability.
- Updated brief schema to validate new MCP properties and ensure correct usage.
- Implemented MCP JSON builder to generate .cursor/mcp.json based on project brief settings.
- Enhanced resolver to include MCP configuration in generated files when enabled.

This update improves integration capabilities and enforces coding standards across the project.
2026-05-14 13:33:13 +05:30
mansi.kansara 2ee257c630 feat(flutter-cursor-templates): add /debug, /verify, /explain skills and goldens.
Wire debug-issue, verify-change, and explain-code into the universal resolver with reasons, extend AGENTS.md slash list, cross-link build Phase 3 and Phase 6, refresh generator goldens for three stacks, and note cross-skill links in the build skill design spec.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-14 12:38:13 +05:30
130 changed files with 3350 additions and 182 deletions
@@ -205,6 +205,14 @@ The `/build` skill orchestrates existing skills internally:
- Invokes `/generate-api-client` if `api_docs.format != none` and feature needs API (Phase 2) - Invokes `/generate-api-client` if `api_docs.format != none` and feature needs API (Phase 2)
- References `/deploy` checklist for flavor-scoped secret handling (Phase 7) - References `/deploy` checklist for flavor-scoped secret handling (Phase 7)
Generated alongside `/build` (same `cursor_gen` bundle) for lifecycle support:
- **`/debug`** (`debug-issue` skill) — structured BugReport + evidence before fixes; use when Phase 6 checks fail with unclear errors or weak reproduction.
- **`/verify`** (`verify-change` skill) — pasted-evidence gate for tests/analyze/hooks without walking the full seven-phase build doc; use for small PRs or post-fix sanity checks.
- **`/explain`** (`explain-code` skill) — read-only walkthrough of code paths; use when research needs human answers about runtime or native behavior before implementation.
The generated `build` skill template also cross-links **Phase 3** (TDD loop) and **Phase 6** (verification failures) to **`/debug`** and **`/verify`** so agents escalate without re-reading the full seven-phase doc.
--- ---
## 9. Rules Applied Every Phase ## 9. Rules Applied Every Phase
+11
View File
@@ -116,6 +116,17 @@ localization:
enabled: true enabled: true
locales: ["en", "es", "fr"] locales: ["en", "es", "fr"]
# -----------------------------------------------------------------------------
# Integrations & conventions (optional)
# -----------------------------------------------------------------------------
integrations:
mcp:
enabled: false # true → .cursor/mcp.json (secrets only via env vars)
preset: auto # auto | minimal
conventions:
strict_package_imports: false
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
# Telemetry (Pillar 6) — local-only generation / usage log under .cursor/ # Telemetry (Pillar 6) — local-only generation / usage log under .cursor/
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
@@ -162,6 +162,38 @@
"type": "boolean", "type": "boolean",
"description": "Pillar 6: Opt-in local telemetry for rule trigger analytics", "description": "Pillar 6: Opt-in local telemetry for rule trigger analytics",
"default": false "default": false
},
"integrations": {
"type": "object",
"description": "Optional third-party integrations for generated Cursor config",
"properties": {
"mcp": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean",
"default": false,
"description": "When true, emit .cursor/mcp.json with env-placeholder server stubs only"
},
"preset": {
"type": "string",
"enum": ["auto", "minimal"],
"default": "auto",
"description": "minimal = empty mcpServers; auto = brief-derived stubs (still no committed secrets)"
}
}
}
}
},
"conventions": {
"type": "object",
"properties": {
"strict_package_imports": {
"type": "boolean",
"default": false,
"description": "When true, flutter-core rule enforces package: imports across feature boundaries"
}
}
} }
} }
} }
@@ -22,6 +22,9 @@ class BriefLoader {
final appCtx = yaml['app_context'] as YamlMap? ?? YamlMap(); final appCtx = yaml['app_context'] as YamlMap? ?? YamlMap();
final features = yaml['features'] as YamlMap? ?? YamlMap(); final features = yaml['features'] as YamlMap? ?? YamlMap();
final l10n = yaml['localization'] as YamlMap? ?? YamlMap(); final l10n = yaml['localization'] as YamlMap? ?? YamlMap();
final integrations = yaml['integrations'] as YamlMap? ?? YamlMap();
final mcp = integrations['mcp'] as YamlMap? ?? YamlMap();
final conventions = yaml['conventions'] as YamlMap? ?? YamlMap();
// Parse backends (can be "firebase+rest" shorthand or list) // Parse backends (can be "firebase+rest" shorthand or list)
final backendRaw = stack['backend']?.toString() ?? 'rest'; final backendRaw = stack['backend']?.toString() ?? 'rest';
@@ -75,6 +78,11 @@ class BriefLoader {
}(), }(),
rolesEnabled: appCtx['roles_enabled'] as bool? ?? false, rolesEnabled: appCtx['roles_enabled'] as bool? ?? false,
roleNames: _toStringList(appCtx['role_names']) ?? [], roleNames: _toStringList(appCtx['role_names']) ?? [],
mcpConfigEnabled: mcp['enabled'] as bool? ?? false,
mcpPreset: mcp['preset']?.toString() ?? 'auto',
strictPackageImports:
conventions['strict_package_imports'] as bool? ?? false,
); );
} }
@@ -0,0 +1,88 @@
// mcp_json.dart — Builds .cursor/mcp.json from project brief (env placeholders only).
import 'dart:convert';
import 'models.dart';
class McpJsonBuilder {
/// JSON string for Cursor MCP config. Never embed secrets — use `\${VAR}` in strings.
static String build(ProjectBrief brief) {
if (brief.mcpPreset == 'minimal') {
return const JsonEncoder.withIndent(' ')
.convert(<String, dynamic>{'mcpServers': <String, dynamic>{}});
}
final servers = <String, Map<String, dynamic>>{
'filesystem': {
'command': 'npx',
'args': ['-y', '@modelcontextprotocol/server-filesystem', '.'],
'description': 'Read/write project files under the workspace root',
},
'flutter-docs': {
'command': 'npx',
'args': ['-y', '@modelcontextprotocol/server-fetch'],
'env': {'BASE_URL': 'https://api.flutter.dev/flutter'},
'description': 'Flutter API documentation lookup',
},
'dart-pub': {
'command': 'npx',
'args': ['-y', '@modelcontextprotocol/server-fetch'],
'env': {'BASE_URL': 'https://pub.dev/api'},
'description': 'Pub.dev package metadata and versions',
},
'github': {
'command': 'npx',
'args': ['-y', '@modelcontextprotocol/server-github'],
'env': {'GITHUB_TOKEN': r'${GITHUB_TOKEN}'},
'description': 'GitHub issues and PR context (requires GITHUB_TOKEN)',
},
};
if (brief.backends.contains('firebase')) {
servers['firebase'] = {
'command': 'npx',
'args': ['-y', '@modelcontextprotocol/server-firebase'],
'env': {'FIREBASE_PROJECT': r'${FIREBASE_PROJECT_ID}'},
'description':
'Firebase project context (configure FIREBASE_PROJECT_ID)',
};
}
if (brief.designSource == 'figma_mcp') {
servers['figma'] = {
'url': 'https://mcp.figma.com/mcp',
'type': 'http',
'headers': {
'Authorization': r'Bearer ${FIGMA_ACCESS_TOKEN}',
},
'description':
'Figma MCP — set FIGMA_ACCESS_TOKEN (never commit the value)',
};
}
if (brief.backends.contains('supabase')) {
servers['supabase'] = {
'command': 'npx',
'args': ['-y', '@supabase/mcp-server-supabase@latest'],
'env': {
'SUPABASE_ACCESS_TOKEN': r'${SUPABASE_ACCESS_TOKEN}',
},
'description': 'Supabase MCP — token from env only',
};
}
if (brief.apiDocsFormat == 'openapi' && brief.apiDocsPath.isNotEmpty) {
servers['openapi-ref'] = {
'command': 'npx',
'args': ['-y', '@modelcontextprotocol/server-fetch'],
'env': {
'OPENAPI_SPEC_URL': r'${OPENAPI_SPEC_URL}',
},
'description':
'Optional fetch MCP — point OPENAPI_SPEC_URL at a hosted spec or file:// URL you expose locally',
};
}
return const JsonEncoder.withIndent(' ').convert({'mcpServers': servers});
}
}
@@ -59,6 +59,15 @@ class ProjectBrief {
final bool rolesEnabled; final bool rolesEnabled;
final List<String> roleNames; final List<String> roleNames;
/// When true, emit `.cursor/mcp.json` with safe env-placeholder servers.
final bool mcpConfigEnabled;
/// When [mcpConfigEnabled]: `minimal` (empty `mcpServers`) or `auto` (stubs from brief).
final String mcpPreset;
/// When true, generated flutter-core rule enforces package imports (no cross-feature relatives).
final bool strictPackageImports;
const ProjectBrief({ const ProjectBrief({
required this.projectName, required this.projectName,
required this.packageId, required this.packageId,
@@ -90,6 +99,9 @@ class ProjectBrief {
this.themeVariants = const ['light', 'dark'], this.themeVariants = const ['light', 'dark'],
this.rolesEnabled = false, this.rolesEnabled = false,
this.roleNames = const [], this.roleNames = const [],
this.mcpConfigEnabled = false,
this.mcpPreset = 'auto',
this.strictPackageImports = false,
}); });
/// Local snapshot for tooling (written as `cursor-gen-metadata.json` under the output dir). /// Local snapshot for tooling (written as `cursor-gen-metadata.json` under the output dir).
@@ -2,6 +2,7 @@
import 'dart:io'; import 'dart:io';
import 'package:path/path.dart' as p; import 'package:path/path.dart' as p;
import 'mcp_json.dart';
import 'models.dart'; import 'models.dart';
class Renderer { class Renderer {
@@ -10,22 +11,32 @@ class Renderer {
required List<String> templateFiles, required List<String> templateFiles,
required String templateSrc, required String templateSrc,
}) async { }) async {
final context = _buildContext(brief); final baseContext = _buildContext(brief);
final output = <String, String>{}; final output = <String, String>{};
for (final key in templateFiles) { for (final key in templateFiles) {
if (key == 'config/mcp-json') {
output['mcp.json'] = McpJsonBuilder.build(brief);
continue;
}
final outPath = _outputPath(key);
final tmplPath = _templatePath(templateSrc, key); final tmplPath = _templatePath(templateSrc, key);
final file = File(tmplPath); final file = File(tmplPath);
if (!file.existsSync()) { if (!file.existsSync()) {
// Gracefully skip missing optional templates with a placeholder output[outPath] = _missingTemplatePlaceholder(key);
output[_outputPath(key)] = _missingTemplatePlaceholder(key);
continue; continue;
} }
var content = await file.readAsString(); var content = await file.readAsString();
content = _substituteAll(content, context); final ctx = Map<String, String>.from(baseContext);
_checkUnreplacedPlaceholders( if (key.startsWith('rules/features/')) {
content, key); // Pillar 3: validate no broken {{VAR}} final slug = p.basename(key);
output[_outputPath(key)] = content; ctx['FEATURE_MODULE'] = slug;
ctx['FEATURE_MODULE_TITLE'] = _titleCase(slug);
}
content = _substituteAll(content, ctx);
_checkUnreplacedPlaceholders(content, key);
output[outPath] = content;
} }
return output; return output;
} }
@@ -71,9 +82,35 @@ class Renderer {
'TEST_PATTERN': _testPattern(brief.stateManagement), 'TEST_PATTERN': _testPattern(brief.stateManagement),
'LOCALES_LIST': brief.locales.join(', '), 'LOCALES_LIST': brief.locales.join(', '),
'TEMPLATE_VERSION': '1.0.4', 'TEMPLATE_VERSION': '1.0.4',
'IMPORT_POLICY_BLOCK': _importPolicyBlock(brief.strictPackageImports),
}; };
} }
static String _titleCase(String slug) {
if (slug.isEmpty) return slug;
return slug
.split('_')
.where((w) => w.isNotEmpty)
.map((w) => '${w[0].toUpperCase()}${w.substring(1)}')
.join(' ');
}
static String _importPolicyBlock(bool strictPackage) {
if (strictPackage) {
return '''
### Imports (strict — `conventions.strict_package_imports: true`)
- Use `package:<your_pubspec_name>/...` imports everywhere in `lib/` and `test/` — **no** relative `../` across feature boundaries (the brief `project.package` id is often the app bundle id; prefer the **pubspec.yaml `name`** for Dart imports)
- Barrel files (`index.dart`) at feature roots; do not wildcard re-export third-party packages
''';
}
return '''
### Imports (default)
- Order: `dart:` → `package:` → relative
- Relative imports are allowed **within** the same feature directory; use `package:` imports for cross-feature code
- Never import another feature's internals from outside that feature
''';
}
static String _substituteAll(String content, Map<String, String> ctx) { static String _substituteAll(String content, Map<String, String> ctx) {
for (final entry in ctx.entries) { for (final entry in ctx.entries) {
content = content.replaceAll('{{${entry.key}}}', entry.value); content = content.replaceAll('{{${entry.key}}}', entry.value);
@@ -107,6 +144,25 @@ class Renderer {
} }
return p.join(templateSrc, 'hooks', '${p.basename(key)}.ts.tmpl'); return p.join(templateSrc, 'hooks', '${p.basename(key)}.ts.tmpl');
} }
if (key.startsWith('commands/')) {
final name = p.basename(key);
return p.join(templateSrc, 'commands', '$name.md.tmpl');
}
if (key == 'onboarding/ONBOARDING') {
return p.join(templateSrc, 'onboarding', 'ONBOARDING.md.tmpl');
}
if (key.startsWith('telemetry/')) {
final leaf = p.basename(key);
if (leaf == 'gitignore') {
return p.join(templateSrc, 'telemetry', 'gitignore.tmpl');
}
if (leaf == 'log-sh') {
return p.join(templateSrc, 'telemetry', 'log.sh.tmpl');
}
}
if (key.startsWith('rules/features/')) {
return p.join(templateSrc, 'rules', 'features', '_stub.mdc.tmpl');
}
return p.join(templateSrc, '$key.mdc.tmpl'); return p.join(templateSrc, '$key.mdc.tmpl');
} }
@@ -122,6 +178,22 @@ class Renderer {
if (key.endsWith('hooks-json')) return 'hooks/hooks.json'; if (key.endsWith('hooks-json')) return 'hooks/hooks.json';
return 'hooks/${p.basename(key)}.ts'; return 'hooks/${p.basename(key)}.ts';
} }
if (key.startsWith('commands/')) {
final name = p.basename(key);
return 'commands/$name.md';
}
if (key == 'onboarding/ONBOARDING') {
return 'ONBOARDING.md';
}
if (key == 'telemetry/gitignore') {
return 'telemetry/.gitignore';
}
if (key == 'telemetry/log-sh') {
return 'telemetry/log.sh';
}
if (key == 'config/mcp-json') {
return 'mcp.json';
}
return '${key.replaceAll('rules/', 'rules/')}.mdc'; return '${key.replaceAll('rules/', 'rules/')}.mdc';
} }
@@ -8,6 +8,9 @@ class Resolver {
static List<String> resolve(ProjectBrief brief) { static List<String> resolve(ProjectBrief brief) {
final files = <String>[]; final files = <String>[];
// ── Meta: rule authoring (first — governs other .mdc files) ─────────
files.add('rules/universal/rule-authoring');
// ── Universal (every project) ────────────────────────────────────── // ── Universal (every project) ──────────────────────────────────────
files.addAll([ files.addAll([
'rules/universal/flutter-core', 'rules/universal/flutter-core',
@@ -15,6 +18,11 @@ class Resolver {
'rules/universal/project-context', 'rules/universal/project-context',
]); ]);
// ── Theming tokens (when high contrast or extra variants) ───────────
if (_emitThemingRule(brief)) {
files.add('rules/theming/theming');
}
// ── Security (Pillar 5: always-on, not just for large/auth projects) ── // ── Security (Pillar 5: always-on, not just for large/auth projects) ──
files.add('rules/security/security-standards'); files.add('rules/security/security-standards');
@@ -28,11 +36,19 @@ class Resolver {
files.add('rules/architecture/${brief.architecture}'); files.add('rules/architecture/${brief.architecture}');
// ── Backend — one or more ───────────────────────────────────────── // ── Backend — one or more ─────────────────────────────────────────
for (final b in brief.backends) files.add('rules/backend/$b'); for (final b in brief.backends) {
files.add('rules/backend/$b');
}
if (brief.specialFeatures.contains('realtime')) { if (brief.specialFeatures.contains('realtime')) {
files.add('rules/backend/realtime'); files.add('rules/backend/realtime');
} }
// ── Push / deep linking ───────────────────────────────────────────
if (brief.specialFeatures.contains('push_notifications') ||
brief.specialFeatures.contains('deep_linking')) {
files.add('rules/integrations/push-deeplink');
}
// ── Routing — exactly one ───────────────────────────────────────── // ── Routing — exactly one ─────────────────────────────────────────
files.add('rules/routing/${brief.routing}'); files.add('rules/routing/${brief.routing}');
@@ -67,16 +83,32 @@ class Resolver {
files.add('rules/i18n/localization'); files.add('rules/i18n/localization');
} }
// ── CI/CD + flavours ─────────────────────────────────────────────
if (brief.cicd != 'none' || brief.flavors.length > 1) {
files.add('rules/cicd/cicd');
}
// ── Feature-scoped rule stubs ─────────────────────────────────────
final featureKeys = <String>{};
for (final m in brief.featureModules) {
final k = featureRuleKey(m);
if (k != null) featureKeys.add(k);
}
files.addAll(featureKeys);
// ── Skills ──────────────────────────────────────────────────────── // ── Skills ────────────────────────────────────────────────────────
files.addAll([ files.addAll([
'skills/scaffold-feature', 'skills/scaffold-feature',
'skills/scaffold-screen', 'skills/scaffold-screen',
'skills/generate-tests', 'skills/generate-tests',
'skills/build', 'skills/build',
'skills/debug-issue',
'skills/verify-change',
'skills/explain-code',
]); ]);
if (brief.apiDocsFormat != 'none') files.add('skills/generate-api-client'); if (brief.apiDocsFormat != 'none') files.add('skills/generate-api-client');
if (brief.flavors.length > 1) files.add('skills/create-flavor'); if (brief.flavors.length > 1) files.add('skills/create-flavor');
if (brief.cicd.isNotEmpty) files.add('skills/deploy'); if (brief.cicd != 'none') files.add('skills/deploy');
// ── Agents ──────────────────────────────────────────────────────── // ── Agents ────────────────────────────────────────────────────────
files.addAll([ files.addAll([
@@ -94,11 +126,51 @@ class Resolver {
files.add('agents/migration-agent'); files.add('agents/migration-agent');
} }
// ── Cursor workspace extras (reference architecture) ────────────────
files.addAll([
'root/.cursorignore',
'root/tool/cursor_audit.sh',
'onboarding/ONBOARDING',
'commands/build',
'commands/debug-issue',
'commands/verify-change',
'commands/explain-code',
]);
if (brief.mcpConfigEnabled) {
files.add('config/mcp-json');
}
if (brief.telemetryOptIn) {
files.addAll([
'telemetry/gitignore',
'telemetry/log-sh',
'rules/telemetry/usage-logging',
]);
}
files.addAll(['root/AGENTS.md', 'root/lefthook.yaml']); files.addAll(['root/AGENTS.md', 'root/lefthook.yaml']);
return files; return files;
} }
/// `lib/features/<slug>/` ↔ `rules/features/<slug>.mdc`
static String? featureRuleKey(String module) {
var s = module
.trim()
.toLowerCase()
.replaceAll(RegExp(r'[^a-z0-9]+'), '_');
s = s.replaceAll(RegExp(r'_+'), '_');
s = s.replaceAll(RegExp(r'(^_+|_+$)'), '');
if (s.isEmpty) return null;
return 'rules/features/$s';
}
static bool _emitThemingRule(ProjectBrief brief) {
return brief.themeVariants.contains('high_contrast') ||
brief.themeVariants.length > 2;
}
/// Returns human-readable description of why each file was included /// Returns human-readable description of why each file was included
static Map<String, String> resolveWithReasons(ProjectBrief brief) { static Map<String, String> resolveWithReasons(ProjectBrief brief) {
final resolved = resolve(brief); final resolved = resolve(brief);
@@ -110,26 +182,100 @@ class Resolver {
} }
static String _reason(String key, ProjectBrief brief) { static String _reason(String key, ProjectBrief brief) {
if (key.contains('universal')) return 'Always included'; if (key == 'rules/universal/rule-authoring') {
if (key.contains('security')) return 'Always included — Pillar 5'; return 'Always included — meta rules for authoring .cursor/rules';
}
if (key.contains('universal')) return 'Always included';
if (key == 'rules/theming/theming') {
return 'Theme variants include high contrast or extended set';
}
if (key.contains('security')) {
return 'Always included — Pillar 5';
}
if (key.contains('error-handling')) return 'Always included'; if (key.contains('error-handling')) return 'Always included';
if (key.contains('state-management')) return 'Matches stack.state_management: ${brief.stateManagement}'; if (key.contains('state-management')) {
if (key.contains('architecture')) return 'Matches stack.architecture: ${brief.architecture}'; return 'Matches stack.state_management: ${brief.stateManagement}';
if (key.contains('routing')) return 'Matches stack.routing: ${brief.routing}'; }
if (key.contains('testing-e2e')) return 'testing.depth includes e2e'; if (key.contains('architecture')) {
if (key.contains('testing')) return 'Matches state_management testing patterns'; return 'Matches stack.architecture: ${brief.architecture}';
if (key.contains('platform')) return 'Matches stack.platforms'; }
if (key == 'rules/integrations/push-deeplink') {
return 'features.special includes push_notifications or deep_linking';
}
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/')) { if (key.startsWith('hooks/')) {
return 'stack.codegen non-empty — Cursor hooks for analyze, boundaries, and tests'; return 'stack.codegen non-empty — Cursor hooks for analyze, boundaries, and tests';
} }
if (key.contains('codegen')) return 'Matches stack.codegen'; if (key.contains('codegen')) {
if (key.contains('i18n')) return 'localization.enabled: true'; return 'Matches stack.codegen';
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.contains('i18n')) {
if (key == 'skills/build') return 'Always included — universal TDD-first feature implementation command'; return 'localization.enabled: true';
if (key.contains('api-client')) return 'api_docs.format is set'; }
if (key.contains('realtime')) return 'features.special contains realtime'; if (key == 'rules/cicd/cicd') {
if (key.startsWith('root/')) return 'Repo-level companion files'; return 'environments.cicd is set or multiple flavors';
}
if (key.startsWith('rules/features/')) {
return 'Listed under features.modules in project-brief.yaml';
}
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 == 'skills/debug-issue') {
return 'Always included — structured bug triage and evidence-first debugging';
}
if (key == 'skills/verify-change') {
return 'Always included — pre-PR verification checklist without full /build lifecycle';
}
if (key == 'skills/explain-code') {
return 'Always included — explain-only walkthrough of code paths and stack behavior';
}
if (key.contains('api-client')) {
return 'api_docs.format is set';
}
if (key.contains('realtime')) {
return 'features.special contains realtime';
}
if (key == 'root/.cursorignore') {
return 'Root ignore patterns for Cursor indexing';
}
if (key == 'root/tool/cursor_audit.sh') {
return 'Maintenance script for rule drift checks';
}
if (key == 'onboarding/ONBOARDING') {
return 'Team onboarding for Cursor layout and slash skills';
}
if (key.startsWith('commands/')) {
return 'Project slash command → skill mapping';
}
if (key == 'config/mcp-json') {
return 'integrations.mcp.enabled: true in project-brief.yaml';
}
if (key == 'telemetry/gitignore' ||
key == 'telemetry/log-sh' ||
key == 'rules/telemetry/usage-logging') {
return 'telemetry_opt_in: true — local usage logging helpers';
}
if (key.startsWith('root/')) {
return 'Repo-level companion files';
}
return 'Included'; return 'Included';
} }
} }
@@ -52,6 +52,7 @@ class Validator {
}; };
static const _validApiFormats = {'openapi', 'postman', 'markdown', 'none'}; static const _validApiFormats = {'openapi', 'postman', 'markdown', 'none'};
static const _validThemeVariants = {'light', 'dark', 'high_contrast'}; static const _validThemeVariants = {'light', 'dark', 'high_contrast'};
static const _validMcpPresets = {'auto', 'minimal'};
static Future<ValidationResult> validateFile(String path) async { static Future<ValidationResult> validateFile(String path) async {
final file = File(path); final file = File(path);
@@ -178,6 +179,18 @@ class Validator {
} }
} }
final integrations = yaml['integrations'] as YamlMap?;
if (integrations != null) {
final mcp = integrations['mcp'] as YamlMap?;
if (mcp != null) {
final preset = mcp['preset']?.toString();
if (preset != null && !_validMcpPresets.contains(preset)) {
warnings.add(
'integrations.mcp.preset "$preset" is not valid. Use: ${_validMcpPresets.join(", ")}');
}
}
}
return ValidationResult( return ValidationResult(
isValid: errors.isEmpty, isValid: errors.isEmpty,
errors: errors, errors: errors,
@@ -214,6 +227,10 @@ class Validator {
if (brief.rolesEnabled && brief.roleNames.isEmpty) { if (brief.rolesEnabled && brief.roleNames.isEmpty) {
warnings.add('roles_enabled is true but role_names is empty'); warnings.add('roles_enabled is true but role_names is empty');
} }
if (!_validMcpPresets.contains(brief.mcpPreset)) {
warnings.add(
'integrations.mcp.preset "${brief.mcpPreset}" is not valid. Use: ${_validMcpPresets.join(", ")}');
}
return ValidationResult( return ValidationResult(
isValid: errors.isEmpty, errors: errors, warnings: warnings); isValid: errors.isEmpty, errors: errors, warnings: warnings);
@@ -0,0 +1 @@
End-to-end feature implementation (research, TDD, integration tests, verification). Follow the workflow and constraints in `@file:.cursor/skills/build/SKILL.md`. Use `project-brief.yaml` as the source of truth for stack and platforms.
@@ -0,0 +1 @@
Structured bug triage and evidence-first debugging. Follow `@file:.cursor/skills/debug-issue/SKILL.md`. Gather reproduction steps, logs, and failing commands before proposing fixes.
@@ -0,0 +1 @@
Explain-only walkthrough of code paths and stack behavior (no edits). Follow `@file:.cursor/skills/explain-code/SKILL.md`. Do not modify source files unless the user explicitly asks.
@@ -0,0 +1 @@
Pre-PR verification checklist (analyze, tests, hooks) without full /build lifecycle. Follow `@file:.cursor/skills/verify-change/SKILL.md` for the change in scope.
@@ -0,0 +1,20 @@
# Cursor — {{PROJECT_NAME}}
## Quick start
1. Open this repo in **Cursor** so `.cursor/rules/` and `.cursor/skills/` load automatically.
2. Read **`rules/universal/rule-authoring.mdc`** — how we maintain AI rules.
3. Stack and product context live in **`project-brief.yaml`** at the repo root; regenerate `.cursor/` with `cursor_gen` after changing it.
4. **Slash skills** (primary workflows): open the Command Palette and use the project commands, or reference:
- `.cursor/skills/build/SKILL.md` — end-to-end feature implementation
- `.cursor/skills/debug-issue/SKILL.md` — structured debugging
- `.cursor/skills/verify-change/SKILL.md` — pre-PR verification
- `.cursor/skills/explain-code/SKILL.md` — explain-only walkthroughs
5. **Agents** (`.cursor/agents/*.mdc`) are reusable reviewer personas — attach when asking for code review or focused passes.
6. **Custom overrides**: files under `.cursor/custom/` and `CURSOR:CUSTOM` blocks in generated files are preserved on `cursor_gen --refresh` (see generator docs).
## Optional
- **`integrations.mcp.enabled`** in `project-brief.yaml` — generates `.cursor/mcp.json` with env-based server entries (no secrets in git).
- **`telemetry_opt_in: true`** — emits local telemetry helpers under `.cursor/telemetry/` (never commit usage logs if you enable logging).
- Run **`bash tool/cursor_audit.sh`** from the project root periodically to catch stale feature rules and overly broad `alwaysApply` usage.
@@ -0,0 +1,27 @@
# Build artefacts
build/
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
*.g.dart
*.freezed.dart
*.gr.dart
*.config.dart
# Secrets
.env
.env.*
firebase_options.dart
google-services.json
GoogleService-Info.plist
# Large binary assets
assets/fonts/
assets/videos/
*.aab
*.apk
*.ipa
# IDE
.idea/
*.iml
@@ -3,3 +3,5 @@
Repo-level notes for AI assistants. Authoritative stack and conventions are in `project-brief.yaml` and `.cursor/` (regenerate with `cursor_gen` from the project root). Repo-level notes for AI assistants. Authoritative stack and conventions are in `project-brief.yaml` and `.cursor/` (regenerate with `cursor_gen` from the project root).
- **Package:** `{{PACKAGE_ID}}` - **Package:** `{{PACKAGE_ID}}`
- **Onboarding:** `.cursor/ONBOARDING.md` — layout, slash commands → skills, MCP opt-in, audits
- **Slash skills** (see `.cursor/skills/`): `/build`, `/debug`, `/verify`, `/explain` (see `.cursor/commands/*.md`)
@@ -1,4 +1,5 @@
# {{PROJECT_NAME}} — generated by cursor_gen; adjust commands to your repo # {{PROJECT_NAME}} — generated by cursor_gen; adjust commands to your repo
# Optional: run `bash tool/cursor_audit.sh` after changing features.modules or rule files.
pre-commit: pre-commit:
commands: commands:
flutter-analyze: flutter-analyze:
@@ -0,0 +1,35 @@
#!/usr/bin/env bash
# {{PROJECT_NAME}} — Cursor rule hygiene (generated by cursor_gen)
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
CURSOR="${ROOT}/.cursor"
RULES="${CURSOR}/rules"
echo "== cursor_audit (${ROOT}) =="
if [[ ! -d "$CURSOR" ]]; then
echo "ERROR: missing ${CURSOR}"
exit 1
fi
if [[ -d "$RULES" ]]; then
ac="$(grep -R "alwaysApply: true" "$RULES" --include='*.mdc' 2>/dev/null | wc -l | tr -d ' ')"
echo "alwaysApply: true occurrences in .cursor/rules: ${ac}"
echo " (expect a small number — meta/safety; prefer scoped globs for domain rules)"
else
echo "WARN: no ${RULES}"
fi
if [[ -d "$RULES/features" ]]; then
shopt -s nullglob
for f in "$RULES/features"/*.mdc; do
base="$(basename "$f" .mdc)"
if [[ ! -d "${ROOT}/lib/features/${base}" ]] && [[ ! -d "${ROOT}/lib/feature_${base}" ]]; then
echo "WARN: rules/features/${base}.mdc has no obvious lib/features/${base} folder — update features.modules or lib layout"
fi
done
shopt -u nullglob
fi
echo "OK — review warnings above after brief or folder renames"
@@ -0,0 +1,29 @@
---
description: CI/CD, flavours, and quality gates for {{PROJECT_NAME}}
globs: [".github/**", "codemagic.yaml", "Makefile", "pubspec.yaml", "fastlane/**"]
alwaysApply: false
---
# CI/CD & flavours — {{PROJECT_NAME}}
## Context
Pipeline and flavour setup must stay aligned with how the app is built and released.
## Flavours
- Documented in `project-brief.yaml`: **{{FLAVORS_LIST}}**
- Use `--flavor` / `-t lib/main_<flavour>.dart` (or your projects entrypoints) consistently across local, CI, and store builds
- Configuration via `--dart-define` / `--dart-define-from-file` — **never** hardcode secrets in source
## Quality gates (recommended stages)
- **Lint:** `dart format --set-exit-if-changed .` and `flutter analyze`
- **Unit + widget:** `flutter test` (with coverage if the team tracks it)
- **Goldens:** fixed device/locale; CI fails on drift unless intentionally updated
- **Smoke build:** e.g. `flutter build apk` or `flutter build ios --no-codesign` for the primary flavour
- **E2E:** when `testing.depth` includes e2e — {{E2E_TOOL}} on CI devices or Firebase Test Lab
## Cursor integration
- After substantive edits: run analyze and relevant tests before PR
- For release-oriented tasks, use the **deploy** skill at `.cursor/skills/deploy/SKILL.md` when present
## CI/CD tool
- Selected in brief: **{{CICD_TOOL}}** (`{{CICD_RAW}}`)
@@ -0,0 +1,21 @@
---
description: "Feature module {{FEATURE_MODULE}} — contracts, boundaries, and ownership (fill after design)"
globs: ["lib/**/{{FEATURE_MODULE}}/**", "test/**/{{FEATURE_MODULE}}/**"]
alwaysApply: false
---
# Feature — {{FEATURE_MODULE_TITLE}}
## Context
This stub was generated from `features.modules` in `project-brief.yaml`. Use it to capture **public contracts** (routes, DTOs, events) and **dependencies** for `{{FEATURE_MODULE}}` so agents do not invent cross-feature wiring.
## Constraints
- List external dependencies (other features, packages, backend endpoints) explicitly
- Document invariants (auth required, idempotency, offline behavior) when known
- Update or delete this file when the module is removed or renamed — run `bash tool/cursor_audit.sh` to catch drift
## Patterns
- Link to key entry points: primary screen(s), state holder(s), repository interface(s)
## Anti-patterns
- Empty file left forever — either fill it or delete the module entry from the brief
@@ -1,6 +1,7 @@
--- ---
description: "Localization / i18n conventions for {{PROJECT_NAME}}" description: "Localization / i18n conventions for {{PROJECT_NAME}}"
alwaysApply: true globs: ["lib/l10n/**", "lib/**/*.dart", "test/**/*.dart"]
alwaysApply: false
--- ---
# Localization Standards — {{PROJECT_NAME}} # Localization Standards — {{PROJECT_NAME}}
@@ -0,0 +1,21 @@
---
description: Push notifications and deep linking for {{PROJECT_NAME}}
globs: ["lib/**/*.dart", "android/**", "ios/**", "test/**/*.dart"]
alwaysApply: false
---
# Push & deep linking — {{PROJECT_NAME}}
## Context
Special capabilities from `project-brief.yaml`: **{{SPECIAL_FEATURES}}**. Native and server configuration must stay consistent.
## Constraints
- **Push:** request permissions through the platform flow; handle denial gracefully; no silent failures on token registration
- **Routing:** deep links and notification taps MUST go through **{{ROUTING}}** (no ad-hoc `Navigator` stacks for inbound links)
- **Payloads:** map FCM/APNs/Supabase payloads to domain models in the data layer — no JSON parsing scattered in widgets
- **iOS:** exercise push on a **physical device** when possible (simulator limitations)
- **Android:** declare required permissions explicitly; target API behaviour for POST_NOTIFICATIONS where applicable
## Testing
- Unit-test payload → domain mapping and routing targets
- Integration/E2E: cold start, background, and foreground tap-to-open flows when `testing.depth` allows
@@ -0,0 +1,19 @@
---
description: "Policy for optional local AI usage logs under .cursor/telemetry/"
globs: [".cursor/telemetry/**"]
alwaysApply: false
---
# Telemetry — {{PROJECT_NAME}}
## Context
When `telemetry_opt_in: true`, this repo may record **local-only** generation or usage notes under `.cursor/telemetry/`. This is **not** production analytics.
## Constraints
- **Never** commit secrets, tokens, or PII into JSONL or shell history
- Prefer redacted summaries over raw prompts
- Add `.cursor/telemetry/*.jsonl` to `.gitignore` unless your team explicitly version-controls sanitized samples
## Patterns
- Append one JSON object per line (JSONL) with ISO timestamps and event type
- Rotate or truncate files if they grow beyond a few MB
@@ -0,0 +1,20 @@
---
description: Semantic colours and theme extensions for {{PROJECT_NAME}}
globs: ["lib/**/*.dart", "test/**/*.dart"]
alwaysApply: false
---
# Theming — {{PROJECT_NAME}}
## Context
Theme variants from brief: **{{THEME_SUMMARY}}**.{{HIGH_CONTRAST_NOTE}}
## Constraints
- Prefer **`ThemeExtension`** (or design-system equivalents) for app-specific colours and radii — avoid raw `Color(0xFF...)` in feature code except in central token definitions
- Use **`Theme.of(context).colorScheme`** / `TextTheme` for Material-aligned roles where appropriate
- **High contrast:** when supported, verify WCAG contrast for text and controls; never rely on colour alone for state (pair with icon or label){{HIGH_CONTRAST_UX_LINE}}
- Touch targets: respect platform minimums (e.g. ~48 logical pixels) for interactive widgets
## Anti-patterns
- Hard-coded `Colors.*` scattered across feature widgets instead of theme tokens
- Ignoring `MediaQuery.of(context).platformBrightness` / high-contrast modes when the product claims support
@@ -1,6 +1,7 @@
--- ---
description: "Core Flutter conventions for {{PROJECT_NAME}} — always applied" description: "Core Flutter conventions for {{PROJECT_NAME}}"
alwaysApply: true globs: ["lib/**/*.dart", "test/**/*.dart", "integration_test/**/*.dart"]
alwaysApply: false
--- ---
# Flutter Core Standards — {{PROJECT_NAME}} # Flutter Core Standards — {{PROJECT_NAME}}
@@ -29,10 +30,7 @@ alwaysApply: true
- Private members: `_camelCase` - Private members: `_camelCase`
## Imports ## Imports
- Order: dart: → package: → relative {{IMPORT_POLICY_BLOCK}}
- Use relative imports within a feature; absolute for cross-feature
- Never import a feature's internal files from outside that feature
## Code quality ## Code quality
- Max function length: 40 lines. Extract widgets and helpers aggressively - Max function length: 40 lines. Extract widgets and helpers aggressively
- No `print()` in production code — use a logging package - No `print()` in production code — use a logging package
@@ -1,6 +1,7 @@
--- ---
description: "Project context for {{PROJECT_NAME}} — always applied" description: "Stack summary and product context for {{PROJECT_NAME}}"
alwaysApply: true globs: ["project-brief.yaml", ".cursor/**/*.md", ".cursor/**/*.mdc", "pubspec.yaml"]
alwaysApply: false
--- ---
# Project Context — {{PROJECT_NAME}} # Project Context — {{PROJECT_NAME}}
@@ -0,0 +1,24 @@
---
description: How Cursor rules in this repository must be written and maintained.
globs: [".cursor/rules/**/*.mdc"]
alwaysApply: true
---
# Rule authoring — {{PROJECT_NAME}}
## Context
Rules are version-controlled contracts for AI assistants. Poor rules waste context and silently steer every edit.
## Constraints
- One focused concern per file; split broad topics instead of one mega-rule
- Every rule MUST have a clear `description` in frontmatter (one sentence)
- Prefer `alwaysApply: false` with **narrow** `globs` for domain rules — reserve `alwaysApply: true` for meta and safety
- `globs` must be as specific as possible — never `["**/*"]` unless tooling requires it
- Code samples in rules MUST be valid for this project (Dart/Flutter/YAML as appropriate)
- Deprecated guidance is removed, not left commented out
- Each substantive rule includes **Context** (why), **Constraints** (must/must not), and where helpful **Patterns** / **Anti-patterns**
## Anti-patterns
- Domain rules (testing, l10n, a feature) with `alwaysApply: true` — burns context
- Rules with no concrete examples when the topic is code-facing
- Stale feature rules after modules are removed — run `tool/cursor_audit.sh` periodically
@@ -1,6 +1,7 @@
--- ---
description: "UI/UX standards for {{PROJECT_NAME}} — always applied" description: "UI/UX standards for {{PROJECT_NAME}}"
alwaysApply: true globs: ["lib/**/*.dart", "test/**/*.dart", "integration_test/**/*.dart"]
alwaysApply: false
--- ---
# UI / UX Standards — {{PROJECT_NAME}} # UI / UX Standards — {{PROJECT_NAME}}
@@ -346,6 +346,7 @@ Follow Red → Green → Refactor strictly. Show actual terminal output at each
flutter test test/features/[feature]/ --no-pub flutter test test/features/[feature]/ --no-pub
``` ```
**Paste the actual output here before proceeding.** **Paste the actual output here before proceeding.**
If failures are unclear or non-deterministic after one iteration, use **`/debug`** with full pasted output before guessing fixes.
### Step 3b — Green: Implement ### Step 3b — Green: Implement
@@ -371,6 +372,8 @@ Follow Red → Green → Refactor strictly. Show actual terminal output at each
**Paste the actual output here before proceeding.** **Paste the actual output here before proceeding.**
3. Run tests once more to confirm still green. 3. Run tests once more to confirm still green.
If output stays red or errors are ambiguous after two focused attempts, stop and use **`/debug`** with the full failing command and log before changing more code.
--- ---
## Phase 4: Integration Test Generation ## Phase 4: Integration Test Generation
@@ -503,6 +506,8 @@ VERIFICATION REQUIRED — paste real output for each:
``` ```
If any check fails: invoke `superpowers:systematic-debugging` to diagnose before retrying. If any check fails: invoke `superpowers:systematic-debugging` to diagnose before retrying.
For noisy errors or weak reproduction, run **`/debug`** and fill the BugReport skeleton with logs before retrying.
For small, isolated changes, **`/verify`** is enough to re-check tests and analyze without re-reading this whole skill.
Do not proceed to Phase 7 until all four checks are green. Do not proceed to Phase 7 until all four checks are green.
--- ---
@@ -0,0 +1,86 @@
---
name: Debug issue
description: Triage failing tests, analyze, CI, or runtime errors with an evidence-first BugReport. Use /debug and paste logs; invokes systematic-debugging before fixes. Stack {{STATE_MANAGEMENT}} / {{ARCHITECTURE}} / {{BACKEND}} / {{PLATFORMS_LIST}}.
---
# Debug — {{PROJECT_NAME}}
Triage failures (tests, CI, runtime, or build) **without** jumping to fixes. Stay in hypothesis-and-evidence mode until root cause is stated.
**Stack context:** **{{STATE_MANAGEMENT}}** / **{{ARCHITECTURE}}** / **{{BACKEND}}** / platforms: {{PLATFORMS_LIST}}. Flavors: {{FLAVORS_LIST}}. Codegen: {{CODEGEN_LIST}}.
## Usage
```
/debug <what failed — paste error output, command, or symptom>
```
**Examples:**
- `/debug flutter test fails on auth_cubit_test — paste output`
- `/debug CI analyze step — paste log excerpt`
- `/debug app crashes on cold start after last change`
---
## Phase 0 — Normalize the report (BugReport)
Emit this skeleton **before** deep analysis. If the user already pasted logs, map them into the fields instead of re-asking.
| Field | Content |
|-------|---------|
| **Summary** | One line: what broke |
| **Expected** | What should happen |
| **Actual** | What happened (symptom + error text) |
| **Repro steps** | Numbered, minimal |
| **Scope / files touched** | Paths or PR slice |
| **Environment** | OS, Flutter/Dart version if known, device vs simulator, flavor |
| **Evidence** | Pasted command output, stack trace, or screenshot notes |
---
## Phase 1 — Evidence checklist (Flutter-aware)
Gather or request **concrete** evidence. Do not guess versions or config.
1. **`flutter doctor -v`** — paste output when environment is unknown or iOS/Android toolchain errors appear.
2. **Failing command** — full invocation + **verbatim** tail of output (e.g. `flutter test …`, `dart test …`, `flutter analyze`).
3. **`flutter analyze`** — if not already in the failure log, run or ask the user to run and paste.
4. **Flavors** — this project uses: **{{FLAVORS_LIST}}**. Confirm which flavor was active if the failure is env-specific.
5. **Platforms** — **{{PLATFORMS_LIST}}**. Narrow reproduction to the platform that failed when relevant.
6. **Codegen** — tools: **{{CODEGEN_LIST}}**. When this is not `none`, remind to run `dart run build_runner build --delete-conflicting-outputs` after generated files changed, and to align with `.cursor/hooks/` / `lefthook run pre-commit` when hooks are present.
7. **Testing depth** — **{{TESTING_DEPTH}}**; E2E tool: **{{E2E_TOOL}}**. Match the failure to the right layer (unit vs widget vs integration).
---
## Phase 2 — Root cause (no code yet)
> **Invoke skill: `superpowers:systematic-debugging`**
Produce **one paragraph**: hypothesis tied to **specific lines** in the pasted evidence. Mark confidence (high / medium / low). **No code changes** in this phase.
---
## Phase 3 — Fix (only after Phase 2)
> **Invoke skill: `superpowers:systematic-debugging`** again while iterating fixes.
When proposing changes:
- Respect architecture boundaries for **{{ARCHITECTURE}}**:
{{ARCH_IMPORT_RULES}}
- Always consider: `.cursor/rules/flutter-core.mdc`, `.cursor/rules/security-standards.mdc`, `.cursor/rules/project-context.mdc`, and state-management rules for **{{STATE_MANAGEMENT}}**.
After each fix attempt, re-run the **same** failing command and paste new output.
---
## ACTION REQUIRED
If evidence is missing, **stop** and print:
1. Exact commands to run (copy-paste ready).
2. What to paste back (full error blocks, not summaries).
3. If the user cannot run commands: state assumptions explicitly and set confidence to **low**.
**Template version:** {{TEMPLATE_VERSION}}
@@ -0,0 +1,80 @@
---
name: Explain code
description: Explain what code does without editing it. Use /explain with a path or symbol; covers {{STATE_MANAGEMENT}} state, {{BACKEND}} I/O, routing {{ROUTING}}, and failure modes. Ask when facts are not in the repo.
---
# Explain — {{PROJECT_NAME}}
Explain **what the code is doing** — **do not** change production code unless the user explicitly asks for a fix.
**Stack:** **{{STATE_MANAGEMENT}}** / **{{ARCHITECTURE}}** / **{{ROUTING}}** / **{{BACKEND}}** / platforms: {{PLATFORMS_LIST}}. Package: `{{PACKAGE_ID}}`.
## Usage
```
/explain <file path, widget name, class, or symbol>
```
**Examples:**
- `/explain lib/features/cart/cart_cubit.dart`
- `/explain how checkout routes after payment`
- `/explain CartPage build method`
---
## Output format (use these headings in order)
### Purpose
One short paragraph: why this code exists in the product context (**{{PROJECT_NAME}}** — {{DESCRIPTION}}).
### Public API
Surface area: public classes/methods, constructors, and what callers are expected to pass. Note codegen involvement when **{{CODEGEN_LIST}}** is not `none`.
### Call flow
Ordered steps from entry point (e.g. widget `build`, route handler, Bloc `on<Event>`, Riverpod `build`, GetX controller lifecycle) through collaborators. Tie navigation to **{{ROUTING}}** where relevant.
### State and side effects
How state is held and updated for **{{STATE_MANAGEMENT}}** ({{STATE_MGMT_RAW}}). Mention async work, listeners, and disposal. Reference **{{ARCH_IMPORT_RULES}}** if layering is unclear.
### I/O and backends
Network, local storage, or platform channels touching **{{BACKEND}}** (and **{{AUTH}}** where auth applies: {{AUTH_RAW}}). Do **not** invent API shapes not visible in the repo.
### Failure modes
What can go wrong: null paths, error states, race conditions, missing permissions on {{PLATFORMS_LIST}}, auth edge cases.
### Suggested tests
Ideas aligned with **{{TESTING_DEPTH}}** and **{{E2E_TOOL}}**; for **{{STATE_MANAGEMENT}}**, prefer patterns like:
```
{{TEST_PATTERN}}
```
### Unknowns — questions for you
If behavior depends on runtime config, native projects, remote API contracts, or secrets not in tree: **stop** and list **specific** questions. Do not fabricate facts.
---
## References
- **Project brief:** `project-brief.yaml` — feature modules: {{FEATURES_LIST}}; special features: {{SPECIAL_FEATURES}}.
- **Scale:** {{SCALE}}; i18n locales: {{LOCALES_LIST}} (when explaining localization).
- **Design:** {{DESIGN_SOURCE}}; Figma URL: {{FIGMA_URL}}.
- **API docs format:** {{API_DOCS_FORMAT}} (path: {{API_DOCS_PATH}}).
- **Related repos (if any):**
{{GIT_REFS_BLOCK}}
- **Local paths (if any):**
{{LOCAL_PATHS_BLOCK}}
**Template version:** {{TEMPLATE_VERSION}}
@@ -0,0 +1,73 @@
---
name: Verify change
description: Post-change or pre-PR verification without the full /build lifecycle. Use /verify with optional scope; respects testing depth {{TESTING_DEPTH}} and E2E {{E2E_TOOL}}; no success claims without pasted outputs.
---
# Verify — {{PROJECT_NAME}}
Run a **focused** verification gate after a change or before a small PR — without replaying the entire `/build` document.
**Stack:** **{{STATE_MANAGEMENT}}** / **{{ARCHITECTURE}}** / **{{BACKEND}}** / {{PLATFORMS_LIST}}. **Testing depth:** {{TESTING_DEPTH}}. **E2E tool:** {{E2E_TOOL}}. **Codegen:** {{CODEGEN_LIST}}.
## Usage
```
/verify [optional scope: paths, “small PR”, or feature name]
```
**Examples:**
- `/verify lib/features/auth/`
- `/verify small PR before push`
- `/verify after dependency bump`
---
## Checklist (adapt to testing depth)
> **Invoke skill: `superpowers:verification-before-completion`**
**Do not claim success** until the user (or you, in a trusted environment) has pasted **real** output for each applicable item below.
### Always (typical Flutter repo)
- [ ] **Unit / widget tests** for touched code — e.g. `flutter test <path> --no-pub` or full suite as appropriate.
**Required in paste:** a line showing tests passed (or the exact failure to fix).
- [ ] **`flutter analyze`**
**Required in paste:** `No issues found!` or the analyzer errors to address.
### When `testing.depth` is `full` or `e2e` (this brief: **{{TESTING_DEPTH}}**)
- [ ] **Integration / E2E** — use **{{E2E_TOOL}}** patterns from `.cursor/rules/` (e.g. Patrol). Run on a device or emulator when scenarios require it; paste run output.
### When `testing.depth` is `unit_widget` only
- [ ] **Integration / device E2E** — *not* mandatory unless the change touches integration-only surfaces; if skipped, say so explicitly in the paste.
### Hooks and codegen
- [ ] **`lefthook run pre-commit`** — run when the repo has Lefthook configured (especially when **{{CODEGEN_LIST}}** is not `none`). Paste hook summary.
If hooks are not set up for this workspace, write *“skipped — hooks not configured”* instead of failing silently.
### CI alignment
- [ ] For **{{CICD_TOOL}}** ({{CICD_RAW}}): confirm the same commands the pipeline runs (analyze + tests) are green locally. Note flavor-scoped secrets for **{{FLAVORS_LIST}}**.
---
## ACTION REQUIRED — paste block
Print this table and wait for pasted output before declaring done:
```
VERIFICATION — paste real output for each line you executed:
[ ] flutter test … → (paste: pass count or failures)
[ ] flutter analyze → (paste: no issues or errors)
[ ] integration / e2e (if depth is full/e2e or change requires it) →
[ ] lefthook run pre-commit → (paste or "skipped — not configured")
```
If anything fails: switch to **`/debug`** with the failing log before guessing.
**Template version:** {{TEMPLATE_VERSION}}
@@ -0,0 +1,2 @@
*.jsonl
*.log
@@ -0,0 +1,7 @@
#!/usr/bin/env bash
# Append a JSONL usage line (local only). Requires jq when passing structured payload.
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
LOG="${ROOT}/telemetry/usage.jsonl"
mkdir -p "$(dirname "$LOG")"
echo "{\"ts\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"event\":\"${1:-note}\",\"detail\":\"${2:-}\"}" >>"$LOG"
@@ -3,109 +3,41 @@
import 'dart:io'; import 'dart:io';
import 'package:test/test.dart'; import 'package:test/test.dart';
import 'golden_briefs.dart';
import '../src/resolver.dart'; import '../src/resolver.dart';
import '../src/renderer.dart'; import '../src/renderer.dart';
import '../src/validator.dart'; import '../src/validator.dart';
import '../src/models.dart'; import '../src/models.dart';
import '../src/mcp_json.dart';
// ─── Test fixtures ─────────────────────────────────────────────────────────
final _blocCleanBrief = ProjectBrief(
projectName: 'TestApp',
packageId: 'com.test.testapp',
description: 'Test app for golden tests',
scale: 'medium',
stateManagement: 'bloc',
routing: 'gorouter',
architecture: 'clean',
backends: ['firebase'],
auth: 'firebase_auth',
platforms: ['ios', 'android'],
codegenTools: ['freezed'],
flavors: ['dev', 'prod'],
cicd: 'github_actions',
testingDepth: 'unit_widget',
e2eTool: 'patrol',
designSource: 'none',
figmaUrl: '',
apiDocsFormat: 'none',
apiDocsPath: '',
referenceRepos: [],
localPaths: [],
featureModules: ['auth', 'home', 'products'],
specialFeatures: [],
i18nEnabled: false,
locales: ['en'],
);
final _riverpodFFBrief = ProjectBrief(
projectName: 'TaskFlow',
packageId: 'com.test.taskflow',
description: 'Task management app',
scale: 'small',
stateManagement: 'riverpod',
routing: 'gorouter',
architecture: 'feature_first',
backends: ['supabase'],
auth: 'supabase_auth',
platforms: ['ios', 'android', 'web'],
codegenTools: ['freezed', 'json_serializable'],
flavors: ['dev', 'prod'],
cicd: 'github_actions',
testingDepth: 'unit_widget',
e2eTool: 'patrol',
designSource: 'none',
figmaUrl: '',
apiDocsFormat: 'none',
apiDocsPath: '',
referenceRepos: [],
localPaths: [],
featureModules: ['auth', 'tasks', 'profile'],
specialFeatures: [],
i18nEnabled: true,
locales: ['en', 'fr'],
);
final _getxMvcBrief = ProjectBrief(
projectName: 'LegacyApp',
packageId: 'com.test.legacy',
description: 'Legacy GetX app',
scale: 'medium',
stateManagement: 'getx',
routing: 'getx_nav',
architecture: 'mvc',
backends: ['rest'],
auth: 'jwt_rest',
platforms: ['ios', 'android'],
codegenTools: [],
flavors: ['dev', 'prod'],
cicd: 'codemagic',
testingDepth: 'unit_widget',
e2eTool: 'patrol',
designSource: 'none',
figmaUrl: '',
apiDocsFormat: 'openapi',
apiDocsPath: 'docs/api.yaml',
referenceRepos: [],
localPaths: [],
featureModules: ['auth', 'dashboard'],
specialFeatures: [],
i18nEnabled: false,
locales: ['en'],
);
void main() { void main() {
// ─── Resolver tests ───────────────────────────────────────────────────────── // ─── Resolver tests ─────────────────────────────────────────────────────────
group('Resolver', () { group('Resolver', () {
test('BLoC + Clean resolves correct files', () { test('BLoC + Clean resolves correct files', () {
final files = Resolver.resolve(_blocCleanBrief); final files = Resolver.resolve(kBlocCleanFirebaseBrief);
// Universal — always present // Universal — always present
expect(files.first, equals('rules/universal/rule-authoring'));
expect(files, contains('rules/universal/flutter-core')); expect(files, contains('rules/universal/flutter-core'));
expect(files, contains('rules/universal/ui-ux-standards')); expect(files, contains('rules/universal/ui-ux-standards'));
expect(files, contains('rules/universal/project-context')); expect(files, contains('rules/universal/project-context'));
// CI/CD rule when cicd set or multiple flavors
expect(files, contains('rules/cicd/cicd'));
// Feature stubs from brief
expect(files, contains('rules/features/auth'));
expect(files, contains('rules/features/home'));
expect(files, contains('rules/features/products'));
// Cursor workspace extras
expect(files, contains('root/.cursorignore'));
expect(files, contains('root/tool/cursor_audit.sh'));
expect(files, contains('onboarding/ONBOARDING'));
expect(files, contains('commands/build'));
expect(files, contains('commands/debug-issue'));
// Security — always present (Pillar 5) // Security — always present (Pillar 5)
expect(files, contains('rules/security/security-standards')); expect(files, contains('rules/security/security-standards'));
@@ -142,12 +74,15 @@ void main() {
expect(files, containsNot('rules/architecture/feature_first')); expect(files, containsNot('rules/architecture/feature_first'));
expect(files, containsNot('rules/routing/getx_nav')); expect(files, containsNot('rules/routing/getx_nav'));
expect(files, contains('commands/verify-change'));
expect(files, contains('commands/explain-code'));
expect(files, containsNot('config/mcp-json'));
expect(files, contains('root/AGENTS.md')); expect(files, contains('root/AGENTS.md'));
expect(files, contains('root/lefthook.yaml')); expect(files, contains('root/lefthook.yaml'));
}); });
test('Riverpod + Feature-First + Web resolves web platform template', () { test('Riverpod + Feature-First + Web resolves web platform template', () {
final files = Resolver.resolve(_riverpodFFBrief); final files = Resolver.resolve(kRiverpodFfSupabaseBrief);
expect(files, contains('rules/platform/platform-web')); expect(files, contains('rules/platform/platform-web'));
expect(files, contains('rules/state-management/riverpod')); expect(files, contains('rules/state-management/riverpod'));
expect(files, contains('rules/architecture/feature_first')); expect(files, contains('rules/architecture/feature_first'));
@@ -157,8 +92,177 @@ void main() {
expect(files, containsNot('agents/migration-agent')); expect(files, containsNot('agents/migration-agent'));
}); });
test('featureRuleKey sanitizes module names', () {
expect(Resolver.featureRuleKey('My Cart'), 'rules/features/my_cart');
expect(Resolver.featureRuleKey('auth'), 'rules/features/auth');
expect(Resolver.featureRuleKey(' '), isNull);
});
test('MCP template key when integrations.mcp.enabled', () {
final brief = ProjectBrief(
projectName: 'McpApp',
packageId: 'com.test.mcp',
description: '',
scale: 'small',
stateManagement: 'bloc',
routing: 'gorouter',
architecture: 'clean',
backends: ['firebase'],
auth: 'none',
platforms: ['ios'],
codegenTools: [],
flavors: ['dev'],
cicd: 'none',
testingDepth: 'unit_widget',
e2eTool: 'patrol',
designSource: 'none',
figmaUrl: '',
apiDocsFormat: 'none',
apiDocsPath: '',
referenceRepos: [],
localPaths: [],
featureModules: [],
specialFeatures: [],
i18nEnabled: false,
locales: ['en'],
mcpConfigEnabled: true,
);
expect(Resolver.resolve(brief), contains('config/mcp-json'));
});
test('Push/deeplink rule when special features request it', () {
final brief = ProjectBrief(
projectName: 'PushApp',
packageId: 'com.test.push',
description: '',
scale: 'small',
stateManagement: 'bloc',
routing: 'gorouter',
architecture: 'clean',
backends: ['rest'],
auth: 'none',
platforms: ['ios'],
codegenTools: [],
flavors: ['dev'],
cicd: 'none',
testingDepth: 'unit_widget',
e2eTool: 'patrol',
designSource: 'none',
figmaUrl: '',
apiDocsFormat: 'none',
apiDocsPath: '',
referenceRepos: [],
localPaths: [],
featureModules: [],
specialFeatures: ['push_notifications'],
i18nEnabled: false,
locales: ['en'],
);
expect(Resolver.resolve(brief), contains('rules/integrations/push-deeplink'));
});
test('Theming rule when high contrast requested', () {
final brief = ProjectBrief(
projectName: 'A11yApp',
packageId: 'com.test.a11y',
description: '',
scale: 'small',
stateManagement: 'bloc',
routing: 'gorouter',
architecture: 'clean',
backends: ['rest'],
auth: 'none',
platforms: ['ios'],
codegenTools: [],
flavors: ['dev'],
cicd: 'none',
testingDepth: 'unit_widget',
e2eTool: 'patrol',
designSource: 'none',
figmaUrl: '',
apiDocsFormat: 'none',
apiDocsPath: '',
referenceRepos: [],
localPaths: [],
featureModules: [],
specialFeatures: [],
i18nEnabled: false,
locales: ['en'],
themeVariants: const ['light', 'dark', 'high_contrast'],
);
expect(Resolver.resolve(brief), contains('rules/theming/theming'));
});
test('Telemetry helpers when telemetry_opt_in', () {
final brief = ProjectBrief(
projectName: 'TelApp',
packageId: 'com.test.tel',
description: '',
scale: 'small',
stateManagement: 'bloc',
routing: 'gorouter',
architecture: 'clean',
backends: ['rest'],
auth: 'none',
platforms: ['ios'],
codegenTools: [],
flavors: ['dev'],
cicd: 'none',
testingDepth: 'unit_widget',
e2eTool: 'patrol',
designSource: 'none',
figmaUrl: '',
apiDocsFormat: 'none',
apiDocsPath: '',
referenceRepos: [],
localPaths: [],
featureModules: [],
specialFeatures: [],
i18nEnabled: false,
locales: ['en'],
telemetryOptIn: true,
);
final files = Resolver.resolve(brief);
expect(files, contains('telemetry/gitignore'));
expect(files, contains('telemetry/log-sh'));
expect(files, contains('rules/telemetry/usage-logging'));
});
test('McpJsonBuilder minimal preset emits empty mcpServers', () {
const brief = ProjectBrief(
projectName: 'M',
packageId: 'com.m.m',
description: '',
scale: 'small',
stateManagement: 'bloc',
routing: 'gorouter',
architecture: 'clean',
backends: ['rest'],
auth: 'none',
platforms: ['ios'],
codegenTools: [],
flavors: ['dev'],
cicd: 'none',
testingDepth: 'unit_widget',
e2eTool: 'patrol',
designSource: 'none',
figmaUrl: '',
apiDocsFormat: 'none',
apiDocsPath: '',
referenceRepos: [],
localPaths: [],
featureModules: [],
specialFeatures: [],
i18nEnabled: false,
locales: ['en'],
mcpConfigEnabled: true,
mcpPreset: 'minimal',
);
expect(McpJsonBuilder.build(brief), contains('"mcpServers": {}'));
});
test('GetX + MVC includes migration-agent', () { test('GetX + MVC includes migration-agent', () {
final files = Resolver.resolve(_getxMvcBrief); final files = Resolver.resolve(kGetxMvcRestBrief);
expect(files, contains('agents/migration-agent')); expect(files, contains('agents/migration-agent'));
expect(files, contains('rules/state-management/getx')); expect(files, contains('rules/state-management/getx'));
expect(files, contains('rules/architecture/mvc')); expect(files, contains('rules/architecture/mvc'));
@@ -171,7 +275,7 @@ void main() {
}); });
test('Codegen stack includes Cursor hooks templates', () { test('Codegen stack includes Cursor hooks templates', () {
final files = Resolver.resolve(_blocCleanBrief); final files = Resolver.resolve(kBlocCleanFirebaseBrief);
expect(files, contains('hooks/hooks-json')); expect(files, contains('hooks/hooks-json'));
expect(files, contains('hooks/arch-guard')); expect(files, contains('hooks/arch-guard'));
}); });
@@ -241,17 +345,26 @@ void main() {
}); });
test('No duplicate files in resolved list', () { test('No duplicate files in resolved list', () {
final files = Resolver.resolve(_blocCleanBrief); final files = Resolver.resolve(kBlocCleanFirebaseBrief);
final unique = files.toSet(); final unique = files.toSet();
expect(files.length, equals(unique.length), expect(files.length, equals(unique.length),
reason: 'Duplicate template files detected'); reason: 'Duplicate template files detected');
}); });
test('build skill always included in resolved list', () { test('Universal slash skills always in resolved list', () {
expect(Resolver.resolve(_blocCleanBrief), contains('skills/build')); const universalSkills = [
expect(Resolver.resolve(_riverpodFFBrief), contains('skills/build')); 'skills/build',
expect(Resolver.resolve(_getxMvcBrief), contains('skills/build')); 'skills/debug-issue',
'skills/verify-change',
'skills/explain-code',
];
for (final skill in universalSkills) {
expect(Resolver.resolve(kBlocCleanFirebaseBrief), contains(skill));
expect(Resolver.resolve(kRiverpodFfSupabaseBrief), contains(skill));
expect(Resolver.resolve(kGetxMvcRestBrief), contains(skill));
}
}); });
}); });
// ─── Renderer / placeholder tests ─────────────────────────────────────────── // ─── Renderer / placeholder tests ───────────────────────────────────────────
@@ -283,8 +396,8 @@ void main() {
} }
final rendered = await Renderer.render( final rendered = await Renderer.render(
brief: _blocCleanBrief, brief: kBlocCleanFirebaseBrief,
templateFiles: Resolver.resolve(_blocCleanBrief), templateFiles: Resolver.resolve(kBlocCleanFirebaseBrief),
templateSrc: templateDir, templateSrc: templateDir,
); );
@@ -309,7 +422,7 @@ void main() {
} }
final rendered = await Renderer.render( final rendered = await Renderer.render(
brief: _blocCleanBrief, brief: kBlocCleanFirebaseBrief,
templateFiles: ['hooks/arch-guard'], templateFiles: ['hooks/arch-guard'],
templateSrc: templateDir, templateSrc: templateDir,
); );
@@ -321,13 +434,58 @@ void main() {
'Default template dir should resolve real arch-guard.ts.tmpl'); 'Default template dir should resolve real arch-guard.ts.tmpl');
expect(content, contains('arch-guard')); expect(content, contains('arch-guard'));
}); });
test('mcp.json renders from config/mcp-json key', () async {
final templateDir = _templateDir();
if (!Directory(templateDir).existsSync()) {
markTestSkipped('Template directory not found');
return;
}
final brief = ProjectBrief(
projectName: 'McpApp',
packageId: 'com.test.mcp',
description: '',
scale: 'small',
stateManagement: 'bloc',
routing: 'gorouter',
architecture: 'clean',
backends: const ['firebase'],
auth: 'none',
platforms: const ['ios'],
codegenTools: const [],
flavors: const ['dev'],
cicd: 'none',
testingDepth: 'unit_widget',
e2eTool: 'patrol',
designSource: 'none',
figmaUrl: '',
apiDocsFormat: 'none',
apiDocsPath: '',
referenceRepos: const [],
localPaths: const [],
featureModules: const [],
specialFeatures: const [],
i18nEnabled: false,
locales: const ['en'],
mcpConfigEnabled: true,
);
final rendered = await Renderer.render(
brief: brief,
templateFiles: const ['config/mcp-json'],
templateSrc: templateDir,
);
final json = rendered['mcp.json']!;
expect(json, contains('mcpServers'));
expect(json, contains('filesystem'));
expect(json, contains('firebase'));
});
}); });
// ─── Validator tests ───────────────────────────────────────────────────────── // ─── Validator tests ─────────────────────────────────────────────────────────
group('Validator', () { group('Validator', () {
test('Valid brief passes validation', () async { test('Valid brief passes validation', () async {
final result = await Validator.validate(_blocCleanBrief); final result = await Validator.validate(kBlocCleanFirebaseBrief);
expect(result.isValid, isTrue); expect(result.isValid, isTrue);
expect(result.errors, isEmpty); expect(result.errors, isEmpty);
}); });
@@ -410,8 +568,8 @@ void main() {
} }
final rendered = await Renderer.render( final rendered = await Renderer.render(
brief: _blocCleanBrief, brief: kBlocCleanFirebaseBrief,
templateFiles: Resolver.resolve(_blocCleanBrief), templateFiles: Resolver.resolve(kBlocCleanFirebaseBrief),
templateSrc: templateDir, templateSrc: templateDir,
); );
@@ -439,8 +597,8 @@ void main() {
return; return;
} }
final rendered = await Renderer.render( final rendered = await Renderer.render(
brief: _riverpodFFBrief, brief: kRiverpodFfSupabaseBrief,
templateFiles: Resolver.resolve(_riverpodFFBrief), templateFiles: Resolver.resolve(kRiverpodFfSupabaseBrief),
templateSrc: templateDir, templateSrc: templateDir,
); );
await _compareGoldens('test/golden/riverpod-ff-supabase', rendered); await _compareGoldens('test/golden/riverpod-ff-supabase', rendered);
@@ -453,8 +611,8 @@ void main() {
return; return;
} }
final rendered = await Renderer.render( final rendered = await Renderer.render(
brief: _getxMvcBrief, brief: kGetxMvcRestBrief,
templateFiles: Resolver.resolve(_getxMvcBrief), templateFiles: Resolver.resolve(kGetxMvcRestBrief),
templateSrc: templateDir, templateSrc: templateDir,
); );
await _compareGoldens('test/golden/getx-mvc-rest', rendered); await _compareGoldens('test/golden/getx-mvc-rest', rendered);
@@ -0,0 +1,20 @@
# Cursor — TestApp
## Quick start
1. Open this repo in **Cursor** so `.cursor/rules/` and `.cursor/skills/` load automatically.
2. Read **`rules/universal/rule-authoring.mdc`** — how we maintain AI rules.
3. Stack and product context live in **`project-brief.yaml`** at the repo root; regenerate `.cursor/` with `cursor_gen` after changing it.
4. **Slash skills** (primary workflows): open the Command Palette and use the project commands, or reference:
- `.cursor/skills/build/SKILL.md` — end-to-end feature implementation
- `.cursor/skills/debug-issue/SKILL.md` — structured debugging
- `.cursor/skills/verify-change/SKILL.md` — pre-PR verification
- `.cursor/skills/explain-code/SKILL.md` — explain-only walkthroughs
5. **Agents** (`.cursor/agents/*.mdc`) are reusable reviewer personas — attach when asking for code review or focused passes.
6. **Custom overrides**: files under `.cursor/custom/` and `CURSOR:CUSTOM` blocks in generated files are preserved on `cursor_gen --refresh` (see generator docs).
## Optional
- **`integrations.mcp.enabled`** in `project-brief.yaml` — generates `.cursor/mcp.json` with env-based server entries (no secrets in git).
- **`telemetry_opt_in: true`** — emits local telemetry helpers under `.cursor/telemetry/` (never commit usage logs if you enable logging).
- Run **`bash tool/cursor_audit.sh`** from the project root periodically to catch stale feature rules and overly broad `alwaysApply` usage.
@@ -0,0 +1,27 @@
# Build artefacts
build/
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
*.g.dart
*.freezed.dart
*.gr.dart
*.config.dart
# Secrets
.env
.env.*
firebase_options.dart
google-services.json
GoogleService-Info.plist
# Large binary assets
assets/fonts/
assets/videos/
*.aab
*.apk
*.ipa
# IDE
.idea/
*.iml
@@ -3,3 +3,5 @@
Repo-level notes for AI assistants. Authoritative stack and conventions are in `project-brief.yaml` and `.cursor/` (regenerate with `cursor_gen` from the project root). Repo-level notes for AI assistants. Authoritative stack and conventions are in `project-brief.yaml` and `.cursor/` (regenerate with `cursor_gen` from the project root).
- **Package:** `com.test.testapp` - **Package:** `com.test.testapp`
- **Onboarding:** `.cursor/ONBOARDING.md` — layout, slash commands → skills, MCP opt-in, audits
- **Slash skills** (see `.cursor/skills/`): `/build`, `/debug`, `/verify`, `/explain` (see `.cursor/commands/*.md`)
@@ -1,4 +1,5 @@
# TestApp — generated by cursor_gen; adjust commands to your repo # TestApp — generated by cursor_gen; adjust commands to your repo
# Optional: run `bash tool/cursor_audit.sh` after changing features.modules or rule files.
pre-commit: pre-commit:
commands: commands:
flutter-analyze: flutter-analyze:
@@ -0,0 +1,35 @@
#!/usr/bin/env bash
# TestApp — Cursor rule hygiene (generated by cursor_gen)
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
CURSOR="${ROOT}/.cursor"
RULES="${CURSOR}/rules"
echo "== cursor_audit (${ROOT}) =="
if [[ ! -d "$CURSOR" ]]; then
echo "ERROR: missing ${CURSOR}"
exit 1
fi
if [[ -d "$RULES" ]]; then
ac="$(grep -R "alwaysApply: true" "$RULES" --include='*.mdc' 2>/dev/null | wc -l | tr -d ' ')"
echo "alwaysApply: true occurrences in .cursor/rules: ${ac}"
echo " (expect a small number — meta/safety; prefer scoped globs for domain rules)"
else
echo "WARN: no ${RULES}"
fi
if [[ -d "$RULES/features" ]]; then
shopt -s nullglob
for f in "$RULES/features"/*.mdc; do
base="$(basename "$f" .mdc)"
if [[ ! -d "${ROOT}/lib/features/${base}" ]] && [[ ! -d "${ROOT}/lib/feature_${base}" ]]; then
echo "WARN: rules/features/${base}.mdc has no obvious lib/features/${base} folder — update features.modules or lib layout"
fi
done
shopt -u nullglob
fi
echo "OK — review warnings above after brief or folder renames"
@@ -0,0 +1 @@
End-to-end feature implementation (research, TDD, integration tests, verification). Follow the workflow and constraints in `@file:.cursor/skills/build/SKILL.md`. Use `project-brief.yaml` as the source of truth for stack and platforms.
@@ -0,0 +1 @@
Structured bug triage and evidence-first debugging. Follow `@file:.cursor/skills/debug-issue/SKILL.md`. Gather reproduction steps, logs, and failing commands before proposing fixes.
@@ -0,0 +1 @@
Explain-only walkthrough of code paths and stack behavior (no edits). Follow `@file:.cursor/skills/explain-code/SKILL.md`. Do not modify source files unless the user explicitly asks.
@@ -0,0 +1 @@
Pre-PR verification checklist (analyze, tests, hooks) without full /build lifecycle. Follow `@file:.cursor/skills/verify-change/SKILL.md` for the change in scope.
@@ -0,0 +1,29 @@
---
description: CI/CD, flavours, and quality gates for TestApp
globs: [".github/**", "codemagic.yaml", "Makefile", "pubspec.yaml", "fastlane/**"]
alwaysApply: false
---
# CI/CD & flavours — TestApp
## Context
Pipeline and flavour setup must stay aligned with how the app is built and released.
## Flavours
- Documented in `project-brief.yaml`: **dev, prod**
- Use `--flavor` / `-t lib/main_<flavour>.dart` (or your projects entrypoints) consistently across local, CI, and store builds
- Configuration via `--dart-define` / `--dart-define-from-file` — **never** hardcode secrets in source
## Quality gates (recommended stages)
- **Lint:** `dart format --set-exit-if-changed .` and `flutter analyze`
- **Unit + widget:** `flutter test` (with coverage if the team tracks it)
- **Goldens:** fixed device/locale; CI fails on drift unless intentionally updated
- **Smoke build:** e.g. `flutter build apk` or `flutter build ios --no-codesign` for the primary flavour
- **E2E:** when `testing.depth` includes e2e — patrol on CI devices or Firebase Test Lab
## Cursor integration
- After substantive edits: run analyze and relevant tests before PR
- For release-oriented tasks, use the **deploy** skill at `.cursor/skills/deploy/SKILL.md` when present
## CI/CD tool
- Selected in brief: **GitHub Actions** (`github_actions`)
@@ -0,0 +1,21 @@
---
description: "Feature module auth — contracts, boundaries, and ownership (fill after design)"
globs: ["lib/**/auth/**", "test/**/auth/**"]
alwaysApply: false
---
# Feature — Auth
## Context
This stub was generated from `features.modules` in `project-brief.yaml`. Use it to capture **public contracts** (routes, DTOs, events) and **dependencies** for `auth` so agents do not invent cross-feature wiring.
## Constraints
- List external dependencies (other features, packages, backend endpoints) explicitly
- Document invariants (auth required, idempotency, offline behavior) when known
- Update or delete this file when the module is removed or renamed — run `bash tool/cursor_audit.sh` to catch drift
## Patterns
- Link to key entry points: primary screen(s), state holder(s), repository interface(s)
## Anti-patterns
- Empty file left forever — either fill it or delete the module entry from the brief
@@ -0,0 +1,21 @@
---
description: "Feature module home — contracts, boundaries, and ownership (fill after design)"
globs: ["lib/**/home/**", "test/**/home/**"]
alwaysApply: false
---
# Feature — Home
## Context
This stub was generated from `features.modules` in `project-brief.yaml`. Use it to capture **public contracts** (routes, DTOs, events) and **dependencies** for `home` so agents do not invent cross-feature wiring.
## Constraints
- List external dependencies (other features, packages, backend endpoints) explicitly
- Document invariants (auth required, idempotency, offline behavior) when known
- Update or delete this file when the module is removed or renamed — run `bash tool/cursor_audit.sh` to catch drift
## Patterns
- Link to key entry points: primary screen(s), state holder(s), repository interface(s)
## Anti-patterns
- Empty file left forever — either fill it or delete the module entry from the brief
@@ -0,0 +1,21 @@
---
description: "Feature module products — contracts, boundaries, and ownership (fill after design)"
globs: ["lib/**/products/**", "test/**/products/**"]
alwaysApply: false
---
# Feature — Products
## Context
This stub was generated from `features.modules` in `project-brief.yaml`. Use it to capture **public contracts** (routes, DTOs, events) and **dependencies** for `products` so agents do not invent cross-feature wiring.
## Constraints
- List external dependencies (other features, packages, backend endpoints) explicitly
- Document invariants (auth required, idempotency, offline behavior) when known
- Update or delete this file when the module is removed or renamed — run `bash tool/cursor_audit.sh` to catch drift
## Patterns
- Link to key entry points: primary screen(s), state holder(s), repository interface(s)
## Anti-patterns
- Empty file left forever — either fill it or delete the module entry from the brief
@@ -1,6 +1,7 @@
--- ---
description: "Core Flutter conventions for TestApp — always applied" description: "Core Flutter conventions for TestApp"
alwaysApply: true globs: ["lib/**/*.dart", "test/**/*.dart", "integration_test/**/*.dart"]
alwaysApply: false
--- ---
# Flutter Core Standards — TestApp # Flutter Core Standards — TestApp
@@ -29,9 +30,9 @@ alwaysApply: true
- Private members: `_camelCase` - Private members: `_camelCase`
## Imports ## Imports
- Order: dart: → package: → relative ### Imports (strict — `conventions.strict_package_imports: true`)
- Use relative imports within a feature; absolute for cross-feature - Use `package:<your_pubspec_name>/...` imports everywhere in `lib/` and `test/` — **no** relative `../` across feature boundaries (the brief `project.package` id is often the app bundle id; prefer the **pubspec.yaml `name`** for Dart imports)
- Never import a feature's internal files from outside that feature - Barrel files (`index.dart`) at feature roots; do not wildcard re-export third-party packages
## Code quality ## Code quality
- Max function length: 40 lines. Extract widgets and helpers aggressively - Max function length: 40 lines. Extract widgets and helpers aggressively
@@ -1,6 +1,7 @@
--- ---
description: "Project context for TestApp — always applied" description: "Stack summary and product context for TestApp"
alwaysApply: true globs: ["project-brief.yaml", ".cursor/**/*.md", ".cursor/**/*.mdc", "pubspec.yaml"]
alwaysApply: false
--- ---
# Project Context — TestApp # Project Context — TestApp
@@ -0,0 +1,24 @@
---
description: How Cursor rules in this repository must be written and maintained.
globs: [".cursor/rules/**/*.mdc"]
alwaysApply: true
---
# Rule authoring — TestApp
## Context
Rules are version-controlled contracts for AI assistants. Poor rules waste context and silently steer every edit.
## Constraints
- One focused concern per file; split broad topics instead of one mega-rule
- Every rule MUST have a clear `description` in frontmatter (one sentence)
- Prefer `alwaysApply: false` with **narrow** `globs` for domain rules — reserve `alwaysApply: true` for meta and safety
- `globs` must be as specific as possible — never `["**/*"]` unless tooling requires it
- Code samples in rules MUST be valid for this project (Dart/Flutter/YAML as appropriate)
- Deprecated guidance is removed, not left commented out
- Each substantive rule includes **Context** (why), **Constraints** (must/must not), and where helpful **Patterns** / **Anti-patterns**
## Anti-patterns
- Domain rules (testing, l10n, a feature) with `alwaysApply: true` — burns context
- Rules with no concrete examples when the topic is code-facing
- Stale feature rules after modules are removed — run `tool/cursor_audit.sh` periodically
@@ -1,6 +1,7 @@
--- ---
description: "UI/UX standards for TestApp — always applied" description: "UI/UX standards for TestApp"
alwaysApply: true globs: ["lib/**/*.dart", "test/**/*.dart", "integration_test/**/*.dart"]
alwaysApply: false
--- ---
# UI / UX Standards — TestApp # UI / UX Standards — TestApp
@@ -346,6 +346,7 @@ Follow Red → Green → Refactor strictly. Show actual terminal output at each
flutter test test/features/[feature]/ --no-pub flutter test test/features/[feature]/ --no-pub
``` ```
**Paste the actual output here before proceeding.** **Paste the actual output here before proceeding.**
If failures are unclear or non-deterministic after one iteration, use **`/debug`** with full pasted output before guessing fixes.
### Step 3b — Green: Implement ### Step 3b — Green: Implement
@@ -374,6 +375,8 @@ Follow Red → Green → Refactor strictly. Show actual terminal output at each
**Paste the actual output here before proceeding.** **Paste the actual output here before proceeding.**
3. Run tests once more to confirm still green. 3. Run tests once more to confirm still green.
If output stays red or errors are ambiguous after two focused attempts, stop and use **`/debug`** with the full failing command and log before changing more code.
--- ---
## Phase 4: Integration Test Generation ## Phase 4: Integration Test Generation
@@ -506,6 +509,8 @@ VERIFICATION REQUIRED — paste real output for each:
``` ```
If any check fails: invoke `superpowers:systematic-debugging` to diagnose before retrying. If any check fails: invoke `superpowers:systematic-debugging` to diagnose before retrying.
For noisy errors or weak reproduction, run **`/debug`** and fill the BugReport skeleton with logs before retrying.
For small, isolated changes, **`/verify`** is enough to re-check tests and analyze without re-reading this whole skill.
Do not proceed to Phase 7 until all four checks are green. Do not proceed to Phase 7 until all four checks are green.
--- ---
@@ -0,0 +1,89 @@
---
name: Debug issue
description: Triage failing tests, analyze, CI, or runtime errors with an evidence-first BugReport. Use /debug and paste logs; invokes systematic-debugging before fixes. Stack BLoC / Cubit / Clean Architecture / Firebase / ios, android.
---
# Debug — TestApp
Triage failures (tests, CI, runtime, or build) **without** jumping to fixes. Stay in hypothesis-and-evidence mode until root cause is stated.
**Stack context:** **BLoC / Cubit** / **Clean Architecture** / **Firebase** / platforms: ios, android. Flavors: dev, prod. Codegen: freezed.
## Usage
```
/debug <what failed — paste error output, command, or symptom>
```
**Examples:**
- `/debug flutter test fails on auth_cubit_test — paste output`
- `/debug CI analyze step — paste log excerpt`
- `/debug app crashes on cold start after last change`
---
## Phase 0 — Normalize the report (BugReport)
Emit this skeleton **before** deep analysis. If the user already pasted logs, map them into the fields instead of re-asking.
| Field | Content |
|-------|---------|
| **Summary** | One line: what broke |
| **Expected** | What should happen |
| **Actual** | What happened (symptom + error text) |
| **Repro steps** | Numbered, minimal |
| **Scope / files touched** | Paths or PR slice |
| **Environment** | OS, Flutter/Dart version if known, device vs simulator, flavor |
| **Evidence** | Pasted command output, stack trace, or screenshot notes |
---
## Phase 1 — Evidence checklist (Flutter-aware)
Gather or request **concrete** evidence. Do not guess versions or config.
1. **`flutter doctor -v`** — paste output when environment is unknown or iOS/Android toolchain errors appear.
2. **Failing command** — full invocation + **verbatim** tail of output (e.g. `flutter test …`, `dart test …`, `flutter analyze`).
3. **`flutter analyze`** — if not already in the failure log, run or ask the user to run and paste.
4. **Flavors** — this project uses: **dev, prod**. Confirm which flavor was active if the failure is env-specific.
5. **Platforms****ios, android**. Narrow reproduction to the platform that failed when relevant.
6. **Codegen** — tools: **freezed**. When this is not `none`, remind to run `dart run build_runner build --delete-conflicting-outputs` after generated files changed, and to align with `.cursor/hooks/` / `lefthook run pre-commit` when hooks are present.
7. **Testing depth****unit_widget**; E2E tool: **patrol**. Match the failure to the right layer (unit vs widget vs integration).
---
## Phase 2 — Root cause (no code yet)
> **Invoke skill: `superpowers:systematic-debugging`**
Produce **one paragraph**: hypothesis tied to **specific lines** in the pasted evidence. Mark confidence (high / medium / low). **No code changes** in this phase.
---
## Phase 3 — Fix (only after Phase 2)
> **Invoke skill: `superpowers:systematic-debugging`** again while iterating fixes.
When proposing changes:
- Respect architecture boundaries for **Clean Architecture**:
- presentation/ MUST NOT import from data/
- domain/ MUST NOT import from data/ or presentation/
- data/ CAN import from domain/ (implements interfaces)
- Use dependency injection to invert data → domain dependency
- Always consider: `.cursor/rules/flutter-core.mdc`, `.cursor/rules/security-standards.mdc`, `.cursor/rules/project-context.mdc`, and state-management rules for **BLoC / Cubit**.
After each fix attempt, re-run the **same** failing command and paste new output.
---
## ACTION REQUIRED
If evidence is missing, **stop** and print:
1. Exact commands to run (copy-paste ready).
2. What to paste back (full error blocks, not summaries).
3. If the user cannot run commands: state assumptions explicitly and set confidence to **low**.
**Template version:** 1.0.4
@@ -0,0 +1,83 @@
---
name: Explain code
description: Explain what code does without editing it. Use /explain with a path or symbol; covers BLoC / Cubit state, Firebase I/O, routing GoRouter, and failure modes. Ask when facts are not in the repo.
---
# Explain — TestApp
Explain **what the code is doing****do not** change production code unless the user explicitly asks for a fix.
**Stack:** **BLoC / Cubit** / **Clean Architecture** / **GoRouter** / **Firebase** / platforms: ios, android. Package: `com.test.testapp`.
## Usage
```
/explain <file path, widget name, class, or symbol>
```
**Examples:**
- `/explain lib/features/cart/cart_cubit.dart`
- `/explain how checkout routes after payment`
- `/explain CartPage build method`
---
## Output format (use these headings in order)
### Purpose
One short paragraph: why this code exists in the product context (**TestApp** — Test app for golden tests).
### Public API
Surface area: public classes/methods, constructors, and what callers are expected to pass. Note codegen involvement when **freezed** is not `none`.
### Call flow
Ordered steps from entry point (e.g. widget `build`, route handler, Bloc `on<Event>`, Riverpod `build`, GetX controller lifecycle) through collaborators. Tie navigation to **GoRouter** where relevant.
### State and side effects
How state is held and updated for **BLoC / Cubit** (bloc). Mention async work, listeners, and disposal. Reference **- presentation/ MUST NOT import from data/
- domain/ MUST NOT import from data/ or presentation/
- data/ CAN import from domain/ (implements interfaces)
- Use dependency injection to invert data → domain dependency** if layering is unclear.
### I/O and backends
Network, local storage, or platform channels touching **Firebase** (and **Firebase Auth** where auth applies: firebase_auth). Do **not** invent API shapes not visible in the repo.
### Failure modes
What can go wrong: null paths, error states, race conditions, missing permissions on ios, android, auth edge cases.
### Suggested tests
Ideas aligned with **unit_widget** and **patrol**; for **BLoC / Cubit**, prefer patterns like:
```
blocTest<MyCubit, MyState>(description, build: () => MyCubit(), act: (c) => c.method(), expect: () => [MyState()])
```
### Unknowns — questions for you
If behavior depends on runtime config, native projects, remote API contracts, or secrets not in tree: **stop** and list **specific** questions. Do not fabricate facts.
---
## References
- **Project brief:** `project-brief.yaml` — feature modules: auth, home, products; special features: .
- **Scale:** medium; i18n locales: en (when explaining localization).
- **Design:** none; Figma URL: .
- **API docs format:** none (path: ).
- **Related repos (if any):**
_No Git repository URLs listed._ Add entries under `references.repos` in project-brief.yaml when other repos are part of the product context.
- **Local paths (if any):**
_No local paths listed._ Add monorepo packages or sibling folders under `references.local_paths` in project-brief.yaml when relevant.
**Template version:** 1.0.4
@@ -0,0 +1,73 @@
---
name: Verify change
description: Post-change or pre-PR verification without the full /build lifecycle. Use /verify with optional scope; respects testing depth unit_widget and E2E patrol; no success claims without pasted outputs.
---
# Verify — TestApp
Run a **focused** verification gate after a change or before a small PR — without replaying the entire `/build` document.
**Stack:** **BLoC / Cubit** / **Clean Architecture** / **Firebase** / ios, android. **Testing depth:** unit_widget. **E2E tool:** patrol. **Codegen:** freezed.
## Usage
```
/verify [optional scope: paths, “small PR”, or feature name]
```
**Examples:**
- `/verify lib/features/auth/`
- `/verify small PR before push`
- `/verify after dependency bump`
---
## Checklist (adapt to testing depth)
> **Invoke skill: `superpowers:verification-before-completion`**
**Do not claim success** until the user (or you, in a trusted environment) has pasted **real** output for each applicable item below.
### Always (typical Flutter repo)
- [ ] **Unit / widget tests** for touched code — e.g. `flutter test <path> --no-pub` or full suite as appropriate.
**Required in paste:** a line showing tests passed (or the exact failure to fix).
- [ ] **`flutter analyze`**
**Required in paste:** `No issues found!` or the analyzer errors to address.
### When `testing.depth` is `full` or `e2e` (this brief: **unit_widget**)
- [ ] **Integration / E2E** — use **patrol** patterns from `.cursor/rules/` (e.g. Patrol). Run on a device or emulator when scenarios require it; paste run output.
### When `testing.depth` is `unit_widget` only
- [ ] **Integration / device E2E***not* mandatory unless the change touches integration-only surfaces; if skipped, say so explicitly in the paste.
### Hooks and codegen
- [ ] **`lefthook run pre-commit`** — run when the repo has Lefthook configured (especially when **freezed** is not `none`). Paste hook summary.
If hooks are not set up for this workspace, write *“skipped — hooks not configured”* instead of failing silently.
### CI alignment
- [ ] For **GitHub Actions** (github_actions): confirm the same commands the pipeline runs (analyze + tests) are green locally. Note flavor-scoped secrets for **dev, prod**.
---
## ACTION REQUIRED — paste block
Print this table and wait for pasted output before declaring done:
```
VERIFICATION — paste real output for each line you executed:
[ ] flutter test … → (paste: pass count or failures)
[ ] flutter analyze → (paste: no issues or errors)
[ ] integration / e2e (if depth is full/e2e or change requires it) →
[ ] lefthook run pre-commit → (paste or "skipped — not configured")
```
If anything fails: switch to **`/debug`** with the failing log before guessing.
**Template version:** 1.0.4
@@ -0,0 +1,20 @@
# Cursor — LegacyApp
## Quick start
1. Open this repo in **Cursor** so `.cursor/rules/` and `.cursor/skills/` load automatically.
2. Read **`rules/universal/rule-authoring.mdc`** — how we maintain AI rules.
3. Stack and product context live in **`project-brief.yaml`** at the repo root; regenerate `.cursor/` with `cursor_gen` after changing it.
4. **Slash skills** (primary workflows): open the Command Palette and use the project commands, or reference:
- `.cursor/skills/build/SKILL.md` — end-to-end feature implementation
- `.cursor/skills/debug-issue/SKILL.md` — structured debugging
- `.cursor/skills/verify-change/SKILL.md` — pre-PR verification
- `.cursor/skills/explain-code/SKILL.md` — explain-only walkthroughs
5. **Agents** (`.cursor/agents/*.mdc`) are reusable reviewer personas — attach when asking for code review or focused passes.
6. **Custom overrides**: files under `.cursor/custom/` and `CURSOR:CUSTOM` blocks in generated files are preserved on `cursor_gen --refresh` (see generator docs).
## Optional
- **`integrations.mcp.enabled`** in `project-brief.yaml` — generates `.cursor/mcp.json` with env-based server entries (no secrets in git).
- **`telemetry_opt_in: true`** — emits local telemetry helpers under `.cursor/telemetry/` (never commit usage logs if you enable logging).
- Run **`bash tool/cursor_audit.sh`** from the project root periodically to catch stale feature rules and overly broad `alwaysApply` usage.
@@ -0,0 +1,27 @@
# Build artefacts
build/
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
*.g.dart
*.freezed.dart
*.gr.dart
*.config.dart
# Secrets
.env
.env.*
firebase_options.dart
google-services.json
GoogleService-Info.plist
# Large binary assets
assets/fonts/
assets/videos/
*.aab
*.apk
*.ipa
# IDE
.idea/
*.iml
@@ -3,3 +3,5 @@
Repo-level notes for AI assistants. Authoritative stack and conventions are in `project-brief.yaml` and `.cursor/` (regenerate with `cursor_gen` from the project root). Repo-level notes for AI assistants. Authoritative stack and conventions are in `project-brief.yaml` and `.cursor/` (regenerate with `cursor_gen` from the project root).
- **Package:** `com.test.legacy` - **Package:** `com.test.legacy`
- **Onboarding:** `.cursor/ONBOARDING.md` — layout, slash commands → skills, MCP opt-in, audits
- **Slash skills** (see `.cursor/skills/`): `/build`, `/debug`, `/verify`, `/explain` (see `.cursor/commands/*.md`)
@@ -1,4 +1,5 @@
# LegacyApp — generated by cursor_gen; adjust commands to your repo # LegacyApp — generated by cursor_gen; adjust commands to your repo
# Optional: run `bash tool/cursor_audit.sh` after changing features.modules or rule files.
pre-commit: pre-commit:
commands: commands:
flutter-analyze: flutter-analyze:
@@ -0,0 +1,35 @@
#!/usr/bin/env bash
# LegacyApp — Cursor rule hygiene (generated by cursor_gen)
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
CURSOR="${ROOT}/.cursor"
RULES="${CURSOR}/rules"
echo "== cursor_audit (${ROOT}) =="
if [[ ! -d "$CURSOR" ]]; then
echo "ERROR: missing ${CURSOR}"
exit 1
fi
if [[ -d "$RULES" ]]; then
ac="$(grep -R "alwaysApply: true" "$RULES" --include='*.mdc' 2>/dev/null | wc -l | tr -d ' ')"
echo "alwaysApply: true occurrences in .cursor/rules: ${ac}"
echo " (expect a small number — meta/safety; prefer scoped globs for domain rules)"
else
echo "WARN: no ${RULES}"
fi
if [[ -d "$RULES/features" ]]; then
shopt -s nullglob
for f in "$RULES/features"/*.mdc; do
base="$(basename "$f" .mdc)"
if [[ ! -d "${ROOT}/lib/features/${base}" ]] && [[ ! -d "${ROOT}/lib/feature_${base}" ]]; then
echo "WARN: rules/features/${base}.mdc has no obvious lib/features/${base} folder — update features.modules or lib layout"
fi
done
shopt -u nullglob
fi
echo "OK — review warnings above after brief or folder renames"
@@ -0,0 +1 @@
End-to-end feature implementation (research, TDD, integration tests, verification). Follow the workflow and constraints in `@file:.cursor/skills/build/SKILL.md`. Use `project-brief.yaml` as the source of truth for stack and platforms.
@@ -0,0 +1 @@
Structured bug triage and evidence-first debugging. Follow `@file:.cursor/skills/debug-issue/SKILL.md`. Gather reproduction steps, logs, and failing commands before proposing fixes.
@@ -0,0 +1 @@
Explain-only walkthrough of code paths and stack behavior (no edits). Follow `@file:.cursor/skills/explain-code/SKILL.md`. Do not modify source files unless the user explicitly asks.
@@ -0,0 +1 @@
Pre-PR verification checklist (analyze, tests, hooks) without full /build lifecycle. Follow `@file:.cursor/skills/verify-change/SKILL.md` for the change in scope.
@@ -0,0 +1,57 @@
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-filesystem",
"."
],
"description": "Read/write project files under the workspace root"
},
"flutter-docs": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-fetch"
],
"env": {
"BASE_URL": "https://api.flutter.dev/flutter"
},
"description": "Flutter API documentation lookup"
},
"dart-pub": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-fetch"
],
"env": {
"BASE_URL": "https://pub.dev/api"
},
"description": "Pub.dev package metadata and versions"
},
"github": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-github"
],
"env": {
"GITHUB_TOKEN": "${GITHUB_TOKEN}"
},
"description": "GitHub issues and PR context (requires GITHUB_TOKEN)"
},
"openapi-ref": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-fetch"
],
"env": {
"OPENAPI_SPEC_URL": "${OPENAPI_SPEC_URL}"
},
"description": "Optional fetch MCP — point OPENAPI_SPEC_URL at a hosted spec or file:// URL you expose locally"
}
}
}
@@ -0,0 +1,29 @@
---
description: CI/CD, flavours, and quality gates for LegacyApp
globs: [".github/**", "codemagic.yaml", "Makefile", "pubspec.yaml", "fastlane/**"]
alwaysApply: false
---
# CI/CD & flavours — LegacyApp
## Context
Pipeline and flavour setup must stay aligned with how the app is built and released.
## Flavours
- Documented in `project-brief.yaml`: **dev, prod**
- Use `--flavor` / `-t lib/main_<flavour>.dart` (or your projects entrypoints) consistently across local, CI, and store builds
- Configuration via `--dart-define` / `--dart-define-from-file` — **never** hardcode secrets in source
## Quality gates (recommended stages)
- **Lint:** `dart format --set-exit-if-changed .` and `flutter analyze`
- **Unit + widget:** `flutter test` (with coverage if the team tracks it)
- **Goldens:** fixed device/locale; CI fails on drift unless intentionally updated
- **Smoke build:** e.g. `flutter build apk` or `flutter build ios --no-codesign` for the primary flavour
- **E2E:** when `testing.depth` includes e2e — patrol on CI devices or Firebase Test Lab
## Cursor integration
- After substantive edits: run analyze and relevant tests before PR
- For release-oriented tasks, use the **deploy** skill at `.cursor/skills/deploy/SKILL.md` when present
## CI/CD tool
- Selected in brief: **Codemagic** (`codemagic`)
@@ -0,0 +1,21 @@
---
description: "Feature module auth — contracts, boundaries, and ownership (fill after design)"
globs: ["lib/**/auth/**", "test/**/auth/**"]
alwaysApply: false
---
# Feature — Auth
## Context
This stub was generated from `features.modules` in `project-brief.yaml`. Use it to capture **public contracts** (routes, DTOs, events) and **dependencies** for `auth` so agents do not invent cross-feature wiring.
## Constraints
- List external dependencies (other features, packages, backend endpoints) explicitly
- Document invariants (auth required, idempotency, offline behavior) when known
- Update or delete this file when the module is removed or renamed — run `bash tool/cursor_audit.sh` to catch drift
## Patterns
- Link to key entry points: primary screen(s), state holder(s), repository interface(s)
## Anti-patterns
- Empty file left forever — either fill it or delete the module entry from the brief
@@ -0,0 +1,21 @@
---
description: "Feature module dashboard — contracts, boundaries, and ownership (fill after design)"
globs: ["lib/**/dashboard/**", "test/**/dashboard/**"]
alwaysApply: false
---
# Feature — Dashboard
## Context
This stub was generated from `features.modules` in `project-brief.yaml`. Use it to capture **public contracts** (routes, DTOs, events) and **dependencies** for `dashboard` so agents do not invent cross-feature wiring.
## Constraints
- List external dependencies (other features, packages, backend endpoints) explicitly
- Document invariants (auth required, idempotency, offline behavior) when known
- Update or delete this file when the module is removed or renamed — run `bash tool/cursor_audit.sh` to catch drift
## Patterns
- Link to key entry points: primary screen(s), state holder(s), repository interface(s)
## Anti-patterns
- Empty file left forever — either fill it or delete the module entry from the brief
@@ -1,6 +1,7 @@
--- ---
description: "Core Flutter conventions for LegacyApp — always applied" description: "Core Flutter conventions for LegacyApp"
alwaysApply: true globs: ["lib/**/*.dart", "test/**/*.dart", "integration_test/**/*.dart"]
alwaysApply: false
--- ---
# Flutter Core Standards — LegacyApp # Flutter Core Standards — LegacyApp
@@ -29,9 +30,10 @@ alwaysApply: true
- Private members: `_camelCase` - Private members: `_camelCase`
## Imports ## Imports
- Order: dart: → package: → relative ### Imports (default)
- Use relative imports within a feature; absolute for cross-feature - Order: `dart:` → `package:` → relative
- Never import a feature's internal files from outside that feature - Relative imports are allowed **within** the same feature directory; use `package:` imports for cross-feature code
- Never import another feature's internals from outside that feature
## Code quality ## Code quality
- Max function length: 40 lines. Extract widgets and helpers aggressively - Max function length: 40 lines. Extract widgets and helpers aggressively
@@ -1,6 +1,7 @@
--- ---
description: "Project context for LegacyApp — always applied" description: "Stack summary and product context for LegacyApp"
alwaysApply: true globs: ["project-brief.yaml", ".cursor/**/*.md", ".cursor/**/*.mdc", "pubspec.yaml"]
alwaysApply: false
--- ---
# Project Context — LegacyApp # Project Context — LegacyApp
@@ -0,0 +1,24 @@
---
description: How Cursor rules in this repository must be written and maintained.
globs: [".cursor/rules/**/*.mdc"]
alwaysApply: true
---
# Rule authoring — LegacyApp
## Context
Rules are version-controlled contracts for AI assistants. Poor rules waste context and silently steer every edit.
## Constraints
- One focused concern per file; split broad topics instead of one mega-rule
- Every rule MUST have a clear `description` in frontmatter (one sentence)
- Prefer `alwaysApply: false` with **narrow** `globs` for domain rules — reserve `alwaysApply: true` for meta and safety
- `globs` must be as specific as possible — never `["**/*"]` unless tooling requires it
- Code samples in rules MUST be valid for this project (Dart/Flutter/YAML as appropriate)
- Deprecated guidance is removed, not left commented out
- Each substantive rule includes **Context** (why), **Constraints** (must/must not), and where helpful **Patterns** / **Anti-patterns**
## Anti-patterns
- Domain rules (testing, l10n, a feature) with `alwaysApply: true` — burns context
- Rules with no concrete examples when the topic is code-facing
- Stale feature rules after modules are removed — run `tool/cursor_audit.sh` periodically
@@ -1,6 +1,7 @@
--- ---
description: "UI/UX standards for LegacyApp — always applied" description: "UI/UX standards for LegacyApp"
alwaysApply: true globs: ["lib/**/*.dart", "test/**/*.dart", "integration_test/**/*.dart"]
alwaysApply: false
--- ---
# UI / UX Standards — LegacyApp # UI / UX Standards — LegacyApp
@@ -346,6 +346,7 @@ Follow Red → Green → Refactor strictly. Show actual terminal output at each
flutter test test/features/[feature]/ --no-pub flutter test test/features/[feature]/ --no-pub
``` ```
**Paste the actual output here before proceeding.** **Paste the actual output here before proceeding.**
If failures are unclear or non-deterministic after one iteration, use **`/debug`** with full pasted output before guessing fixes.
### Step 3b — Green: Implement ### Step 3b — Green: Implement
@@ -373,6 +374,8 @@ Follow Red → Green → Refactor strictly. Show actual terminal output at each
**Paste the actual output here before proceeding.** **Paste the actual output here before proceeding.**
3. Run tests once more to confirm still green. 3. Run tests once more to confirm still green.
If output stays red or errors are ambiguous after two focused attempts, stop and use **`/debug`** with the full failing command and log before changing more code.
--- ---
## Phase 4: Integration Test Generation ## Phase 4: Integration Test Generation
@@ -505,6 +508,8 @@ VERIFICATION REQUIRED — paste real output for each:
``` ```
If any check fails: invoke `superpowers:systematic-debugging` to diagnose before retrying. If any check fails: invoke `superpowers:systematic-debugging` to diagnose before retrying.
For noisy errors or weak reproduction, run **`/debug`** and fill the BugReport skeleton with logs before retrying.
For small, isolated changes, **`/verify`** is enough to re-check tests and analyze without re-reading this whole skill.
Do not proceed to Phase 7 until all four checks are green. Do not proceed to Phase 7 until all four checks are green.
--- ---
@@ -0,0 +1,88 @@
---
name: Debug issue
description: Triage failing tests, analyze, CI, or runtime errors with an evidence-first BugReport. Use /debug and paste logs; invokes systematic-debugging before fixes. Stack GetX / MVC / REST API / ios, android.
---
# Debug — LegacyApp
Triage failures (tests, CI, runtime, or build) **without** jumping to fixes. Stay in hypothesis-and-evidence mode until root cause is stated.
**Stack context:** **GetX** / **MVC** / **REST API** / platforms: ios, android. Flavors: dev, prod. Codegen: none.
## Usage
```
/debug <what failed — paste error output, command, or symptom>
```
**Examples:**
- `/debug flutter test fails on auth_cubit_test — paste output`
- `/debug CI analyze step — paste log excerpt`
- `/debug app crashes on cold start after last change`
---
## Phase 0 — Normalize the report (BugReport)
Emit this skeleton **before** deep analysis. If the user already pasted logs, map them into the fields instead of re-asking.
| Field | Content |
|-------|---------|
| **Summary** | One line: what broke |
| **Expected** | What should happen |
| **Actual** | What happened (symptom + error text) |
| **Repro steps** | Numbered, minimal |
| **Scope / files touched** | Paths or PR slice |
| **Environment** | OS, Flutter/Dart version if known, device vs simulator, flavor |
| **Evidence** | Pasted command output, stack trace, or screenshot notes |
---
## Phase 1 — Evidence checklist (Flutter-aware)
Gather or request **concrete** evidence. Do not guess versions or config.
1. **`flutter doctor -v`** — paste output when environment is unknown or iOS/Android toolchain errors appear.
2. **Failing command** — full invocation + **verbatim** tail of output (e.g. `flutter test …`, `dart test …`, `flutter analyze`).
3. **`flutter analyze`** — if not already in the failure log, run or ask the user to run and paste.
4. **Flavors** — this project uses: **dev, prod**. Confirm which flavor was active if the failure is env-specific.
5. **Platforms****ios, android**. Narrow reproduction to the platform that failed when relevant.
6. **Codegen** — tools: **none**. When this is not `none`, remind to run `dart run build_runner build --delete-conflicting-outputs` after generated files changed, and to align with `.cursor/hooks/` / `lefthook run pre-commit` when hooks are present.
7. **Testing depth****unit_widget**; E2E tool: **patrol**. Match the failure to the right layer (unit vs widget vs integration).
---
## Phase 2 — Root cause (no code yet)
> **Invoke skill: `superpowers:systematic-debugging`**
Produce **one paragraph**: hypothesis tied to **specific lines** in the pasted evidence. Mark confidence (high / medium / low). **No code changes** in this phase.
---
## Phase 3 — Fix (only after Phase 2)
> **Invoke skill: `superpowers:systematic-debugging`** again while iterating fixes.
When proposing changes:
- Respect architecture boundaries for **MVC**:
- View (Widget) MUST NOT contain business logic
- Controller MUST NOT import Flutter widgets directly
- Model MUST be plain Dart, no framework dependencies
- Always consider: `.cursor/rules/flutter-core.mdc`, `.cursor/rules/security-standards.mdc`, `.cursor/rules/project-context.mdc`, and state-management rules for **GetX**.
After each fix attempt, re-run the **same** failing command and paste new output.
---
## ACTION REQUIRED
If evidence is missing, **stop** and print:
1. Exact commands to run (copy-paste ready).
2. What to paste back (full error blocks, not summaries).
3. If the user cannot run commands: state assumptions explicitly and set confidence to **low**.
**Template version:** 1.0.4
@@ -0,0 +1,82 @@
---
name: Explain code
description: Explain what code does without editing it. Use /explain with a path or symbol; covers GetX state, REST API I/O, routing GetX Navigation, and failure modes. Ask when facts are not in the repo.
---
# Explain — LegacyApp
Explain **what the code is doing****do not** change production code unless the user explicitly asks for a fix.
**Stack:** **GetX** / **MVC** / **GetX Navigation** / **REST API** / platforms: ios, android. Package: `com.test.legacy`.
## Usage
```
/explain <file path, widget name, class, or symbol>
```
**Examples:**
- `/explain lib/features/cart/cart_cubit.dart`
- `/explain how checkout routes after payment`
- `/explain CartPage build method`
---
## Output format (use these headings in order)
### Purpose
One short paragraph: why this code exists in the product context (**LegacyApp** — Legacy GetX app).
### Public API
Surface area: public classes/methods, constructors, and what callers are expected to pass. Note codegen involvement when **none** is not `none`.
### Call flow
Ordered steps from entry point (e.g. widget `build`, route handler, Bloc `on<Event>`, Riverpod `build`, GetX controller lifecycle) through collaborators. Tie navigation to **GetX Navigation** where relevant.
### State and side effects
How state is held and updated for **GetX** (getx). Mention async work, listeners, and disposal. Reference **- View (Widget) MUST NOT contain business logic
- Controller MUST NOT import Flutter widgets directly
- Model MUST be plain Dart, no framework dependencies** if layering is unclear.
### I/O and backends
Network, local storage, or platform channels touching **REST API** (and **JWT / REST Auth** where auth applies: jwt_rest). Do **not** invent API shapes not visible in the repo.
### Failure modes
What can go wrong: null paths, error states, race conditions, missing permissions on ios, android, auth edge cases.
### Suggested tests
Ideas aligned with **unit_widget** and **patrol**; for **GetX**, prefer patterns like:
```
Get.put(MyController()); final ctrl = Get.find<MyController>(); expect(ctrl.value, expected);
```
### Unknowns — questions for you
If behavior depends on runtime config, native projects, remote API contracts, or secrets not in tree: **stop** and list **specific** questions. Do not fabricate facts.
---
## References
- **Project brief:** `project-brief.yaml` — feature modules: auth, dashboard; special features: .
- **Scale:** medium; i18n locales: en (when explaining localization).
- **Design:** none; Figma URL: .
- **API docs format:** openapi (path: docs/api.yaml).
- **Related repos (if any):**
_No Git repository URLs listed._ Add entries under `references.repos` in project-brief.yaml when other repos are part of the product context.
- **Local paths (if any):**
_No local paths listed._ Add monorepo packages or sibling folders under `references.local_paths` in project-brief.yaml when relevant.
**Template version:** 1.0.4
@@ -0,0 +1,73 @@
---
name: Verify change
description: Post-change or pre-PR verification without the full /build lifecycle. Use /verify with optional scope; respects testing depth unit_widget and E2E patrol; no success claims without pasted outputs.
---
# Verify — LegacyApp
Run a **focused** verification gate after a change or before a small PR — without replaying the entire `/build` document.
**Stack:** **GetX** / **MVC** / **REST API** / ios, android. **Testing depth:** unit_widget. **E2E tool:** patrol. **Codegen:** none.
## Usage
```
/verify [optional scope: paths, “small PR”, or feature name]
```
**Examples:**
- `/verify lib/features/auth/`
- `/verify small PR before push`
- `/verify after dependency bump`
---
## Checklist (adapt to testing depth)
> **Invoke skill: `superpowers:verification-before-completion`**
**Do not claim success** until the user (or you, in a trusted environment) has pasted **real** output for each applicable item below.
### Always (typical Flutter repo)
- [ ] **Unit / widget tests** for touched code — e.g. `flutter test <path> --no-pub` or full suite as appropriate.
**Required in paste:** a line showing tests passed (or the exact failure to fix).
- [ ] **`flutter analyze`**
**Required in paste:** `No issues found!` or the analyzer errors to address.
### When `testing.depth` is `full` or `e2e` (this brief: **unit_widget**)
- [ ] **Integration / E2E** — use **patrol** patterns from `.cursor/rules/` (e.g. Patrol). Run on a device or emulator when scenarios require it; paste run output.
### When `testing.depth` is `unit_widget` only
- [ ] **Integration / device E2E***not* mandatory unless the change touches integration-only surfaces; if skipped, say so explicitly in the paste.
### Hooks and codegen
- [ ] **`lefthook run pre-commit`** — run when the repo has Lefthook configured (especially when **none** is not `none`). Paste hook summary.
If hooks are not set up for this workspace, write *“skipped — hooks not configured”* instead of failing silently.
### CI alignment
- [ ] For **Codemagic** (codemagic): confirm the same commands the pipeline runs (analyze + tests) are green locally. Note flavor-scoped secrets for **dev, prod**.
---
## ACTION REQUIRED — paste block
Print this table and wait for pasted output before declaring done:
```
VERIFICATION — paste real output for each line you executed:
[ ] flutter test … → (paste: pass count or failures)
[ ] flutter analyze → (paste: no issues or errors)
[ ] integration / e2e (if depth is full/e2e or change requires it) →
[ ] lefthook run pre-commit → (paste or "skipped — not configured")
```
If anything fails: switch to **`/debug`** with the failing log before guessing.
**Template version:** 1.0.4
@@ -0,0 +1,20 @@
# Cursor — TaskFlow
## Quick start
1. Open this repo in **Cursor** so `.cursor/rules/` and `.cursor/skills/` load automatically.
2. Read **`rules/universal/rule-authoring.mdc`** — how we maintain AI rules.
3. Stack and product context live in **`project-brief.yaml`** at the repo root; regenerate `.cursor/` with `cursor_gen` after changing it.
4. **Slash skills** (primary workflows): open the Command Palette and use the project commands, or reference:
- `.cursor/skills/build/SKILL.md` — end-to-end feature implementation
- `.cursor/skills/debug-issue/SKILL.md` — structured debugging
- `.cursor/skills/verify-change/SKILL.md` — pre-PR verification
- `.cursor/skills/explain-code/SKILL.md` — explain-only walkthroughs
5. **Agents** (`.cursor/agents/*.mdc`) are reusable reviewer personas — attach when asking for code review or focused passes.
6. **Custom overrides**: files under `.cursor/custom/` and `CURSOR:CUSTOM` blocks in generated files are preserved on `cursor_gen --refresh` (see generator docs).
## Optional
- **`integrations.mcp.enabled`** in `project-brief.yaml` — generates `.cursor/mcp.json` with env-based server entries (no secrets in git).
- **`telemetry_opt_in: true`** — emits local telemetry helpers under `.cursor/telemetry/` (never commit usage logs if you enable logging).
- Run **`bash tool/cursor_audit.sh`** from the project root periodically to catch stale feature rules and overly broad `alwaysApply` usage.
@@ -0,0 +1,27 @@
# Build artefacts
build/
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
*.g.dart
*.freezed.dart
*.gr.dart
*.config.dart
# Secrets
.env
.env.*
firebase_options.dart
google-services.json
GoogleService-Info.plist
# Large binary assets
assets/fonts/
assets/videos/
*.aab
*.apk
*.ipa
# IDE
.idea/
*.iml
@@ -3,3 +3,5 @@
Repo-level notes for AI assistants. Authoritative stack and conventions are in `project-brief.yaml` and `.cursor/` (regenerate with `cursor_gen` from the project root). Repo-level notes for AI assistants. Authoritative stack and conventions are in `project-brief.yaml` and `.cursor/` (regenerate with `cursor_gen` from the project root).
- **Package:** `com.test.taskflow` - **Package:** `com.test.taskflow`
- **Onboarding:** `.cursor/ONBOARDING.md` — layout, slash commands → skills, MCP opt-in, audits
- **Slash skills** (see `.cursor/skills/`): `/build`, `/debug`, `/verify`, `/explain` (see `.cursor/commands/*.md`)
@@ -1,4 +1,5 @@
# TaskFlow — generated by cursor_gen; adjust commands to your repo # TaskFlow — generated by cursor_gen; adjust commands to your repo
# Optional: run `bash tool/cursor_audit.sh` after changing features.modules or rule files.
pre-commit: pre-commit:
commands: commands:
flutter-analyze: flutter-analyze:
@@ -0,0 +1,35 @@
#!/usr/bin/env bash
# TaskFlow — Cursor rule hygiene (generated by cursor_gen)
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
CURSOR="${ROOT}/.cursor"
RULES="${CURSOR}/rules"
echo "== cursor_audit (${ROOT}) =="
if [[ ! -d "$CURSOR" ]]; then
echo "ERROR: missing ${CURSOR}"
exit 1
fi
if [[ -d "$RULES" ]]; then
ac="$(grep -R "alwaysApply: true" "$RULES" --include='*.mdc' 2>/dev/null | wc -l | tr -d ' ')"
echo "alwaysApply: true occurrences in .cursor/rules: ${ac}"
echo " (expect a small number — meta/safety; prefer scoped globs for domain rules)"
else
echo "WARN: no ${RULES}"
fi
if [[ -d "$RULES/features" ]]; then
shopt -s nullglob
for f in "$RULES/features"/*.mdc; do
base="$(basename "$f" .mdc)"
if [[ ! -d "${ROOT}/lib/features/${base}" ]] && [[ ! -d "${ROOT}/lib/feature_${base}" ]]; then
echo "WARN: rules/features/${base}.mdc has no obvious lib/features/${base} folder — update features.modules or lib layout"
fi
done
shopt -u nullglob
fi
echo "OK — review warnings above after brief or folder renames"
@@ -0,0 +1 @@
End-to-end feature implementation (research, TDD, integration tests, verification). Follow the workflow and constraints in `@file:.cursor/skills/build/SKILL.md`. Use `project-brief.yaml` as the source of truth for stack and platforms.
@@ -0,0 +1 @@
Structured bug triage and evidence-first debugging. Follow `@file:.cursor/skills/debug-issue/SKILL.md`. Gather reproduction steps, logs, and failing commands before proposing fixes.
@@ -0,0 +1 @@
Explain-only walkthrough of code paths and stack behavior (no edits). Follow `@file:.cursor/skills/explain-code/SKILL.md`. Do not modify source files unless the user explicitly asks.
@@ -0,0 +1 @@
Pre-PR verification checklist (analyze, tests, hooks) without full /build lifecycle. Follow `@file:.cursor/skills/verify-change/SKILL.md` for the change in scope.
@@ -0,0 +1,29 @@
---
description: CI/CD, flavours, and quality gates for TaskFlow
globs: [".github/**", "codemagic.yaml", "Makefile", "pubspec.yaml", "fastlane/**"]
alwaysApply: false
---
# CI/CD & flavours — TaskFlow
## Context
Pipeline and flavour setup must stay aligned with how the app is built and released.
## Flavours
- Documented in `project-brief.yaml`: **dev, prod**
- Use `--flavor` / `-t lib/main_<flavour>.dart` (or your projects entrypoints) consistently across local, CI, and store builds
- Configuration via `--dart-define` / `--dart-define-from-file` — **never** hardcode secrets in source
## Quality gates (recommended stages)
- **Lint:** `dart format --set-exit-if-changed .` and `flutter analyze`
- **Unit + widget:** `flutter test` (with coverage if the team tracks it)
- **Goldens:** fixed device/locale; CI fails on drift unless intentionally updated
- **Smoke build:** e.g. `flutter build apk` or `flutter build ios --no-codesign` for the primary flavour
- **E2E:** when `testing.depth` includes e2e — patrol on CI devices or Firebase Test Lab
## Cursor integration
- After substantive edits: run analyze and relevant tests before PR
- For release-oriented tasks, use the **deploy** skill at `.cursor/skills/deploy/SKILL.md` when present
## CI/CD tool
- Selected in brief: **GitHub Actions** (`github_actions`)
@@ -0,0 +1,21 @@
---
description: "Feature module auth — contracts, boundaries, and ownership (fill after design)"
globs: ["lib/**/auth/**", "test/**/auth/**"]
alwaysApply: false
---
# Feature — Auth
## Context
This stub was generated from `features.modules` in `project-brief.yaml`. Use it to capture **public contracts** (routes, DTOs, events) and **dependencies** for `auth` so agents do not invent cross-feature wiring.
## Constraints
- List external dependencies (other features, packages, backend endpoints) explicitly
- Document invariants (auth required, idempotency, offline behavior) when known
- Update or delete this file when the module is removed or renamed — run `bash tool/cursor_audit.sh` to catch drift
## Patterns
- Link to key entry points: primary screen(s), state holder(s), repository interface(s)
## Anti-patterns
- Empty file left forever — either fill it or delete the module entry from the brief
@@ -0,0 +1,21 @@
---
description: "Feature module profile — contracts, boundaries, and ownership (fill after design)"
globs: ["lib/**/profile/**", "test/**/profile/**"]
alwaysApply: false
---
# Feature — Profile
## Context
This stub was generated from `features.modules` in `project-brief.yaml`. Use it to capture **public contracts** (routes, DTOs, events) and **dependencies** for `profile` so agents do not invent cross-feature wiring.
## Constraints
- List external dependencies (other features, packages, backend endpoints) explicitly
- Document invariants (auth required, idempotency, offline behavior) when known
- Update or delete this file when the module is removed or renamed — run `bash tool/cursor_audit.sh` to catch drift
## Patterns
- Link to key entry points: primary screen(s), state holder(s), repository interface(s)
## Anti-patterns
- Empty file left forever — either fill it or delete the module entry from the brief
@@ -0,0 +1,21 @@
---
description: "Feature module tasks — contracts, boundaries, and ownership (fill after design)"
globs: ["lib/**/tasks/**", "test/**/tasks/**"]
alwaysApply: false
---
# Feature — Tasks
## Context
This stub was generated from `features.modules` in `project-brief.yaml`. Use it to capture **public contracts** (routes, DTOs, events) and **dependencies** for `tasks` so agents do not invent cross-feature wiring.
## Constraints
- List external dependencies (other features, packages, backend endpoints) explicitly
- Document invariants (auth required, idempotency, offline behavior) when known
- Update or delete this file when the module is removed or renamed — run `bash tool/cursor_audit.sh` to catch drift
## Patterns
- Link to key entry points: primary screen(s), state holder(s), repository interface(s)
## Anti-patterns
- Empty file left forever — either fill it or delete the module entry from the brief
@@ -1,6 +1,7 @@
--- ---
description: "Localization / i18n conventions for TaskFlow" description: "Localization / i18n conventions for TaskFlow"
alwaysApply: true globs: ["lib/l10n/**", "lib/**/*.dart", "test/**/*.dart"]
alwaysApply: false
--- ---
# Localization Standards — TaskFlow # Localization Standards — TaskFlow
@@ -0,0 +1,23 @@
---
description: Semantic colours and theme extensions for TaskFlow
globs: ["lib/**/*.dart", "test/**/*.dart"]
alwaysApply: false
---
# Theming — TaskFlow
## Context
Theme variants from brief: **light, dark, high contrast**.
- **High contrast:** validate contrast, borders, and focus in the high-contrast theme alongside light/dark (WCAG).
## Constraints
- Prefer **`ThemeExtension`** (or design-system equivalents) for app-specific colours and radii — avoid raw `Color(0xFF...)` in feature code except in central token definitions
- Use **`Theme.of(context).colorScheme`** / `TextTheme` for Material-aligned roles where appropriate
- **High contrast:** when supported, verify WCAG contrast for text and controls; never rely on colour alone for state (pair with icon or label)
- **High contrast theme:** validate loading, empty, and error states; never rely on color alone for meaning (use icons/text/semantics).
- Touch targets: respect platform minimums (e.g. ~48 logical pixels) for interactive widgets
## Anti-patterns
- Hard-coded `Colors.*` scattered across feature widgets instead of theme tokens
- Ignoring `MediaQuery.of(context).platformBrightness` / high-contrast modes when the product claims support
@@ -1,6 +1,7 @@
--- ---
description: "Core Flutter conventions for TaskFlow — always applied" description: "Core Flutter conventions for TaskFlow"
alwaysApply: true globs: ["lib/**/*.dart", "test/**/*.dart", "integration_test/**/*.dart"]
alwaysApply: false
--- ---
# Flutter Core Standards — TaskFlow # Flutter Core Standards — TaskFlow
@@ -29,9 +30,10 @@ alwaysApply: true
- Private members: `_camelCase` - Private members: `_camelCase`
## Imports ## Imports
- Order: dart: → package: → relative ### Imports (default)
- Use relative imports within a feature; absolute for cross-feature - Order: `dart:` → `package:` → relative
- Never import a feature's internal files from outside that feature - Relative imports are allowed **within** the same feature directory; use `package:` imports for cross-feature code
- Never import another feature's internals from outside that feature
## Code quality ## Code quality
- Max function length: 40 lines. Extract widgets and helpers aggressively - Max function length: 40 lines. Extract widgets and helpers aggressively
@@ -1,6 +1,7 @@
--- ---
description: "Project context for TaskFlow — always applied" description: "Stack summary and product context for TaskFlow"
alwaysApply: true globs: ["project-brief.yaml", ".cursor/**/*.md", ".cursor/**/*.mdc", "pubspec.yaml"]
alwaysApply: false
--- ---
# Project Context — TaskFlow # Project Context — TaskFlow
@@ -42,10 +43,12 @@ _No Git repository URLs listed._ Add entries under `references.repos` in project
_No local paths listed._ Add monorepo packages or sibling folders under `references.local_paths` in project-brief.yaml when relevant. _No local paths listed._ Add monorepo packages or sibling folders under `references.local_paths` in project-brief.yaml when relevant.
## Product UX / themes & roles ## Product UX / themes & roles
- **Theme variants:** light, dark - **Theme variants:** light, dark, high contrast
- **Roles:** Not enabled (`app_context.roles_enabled: false`). - **Roles:** Not enabled (`app_context.roles_enabled: false`).
- **High contrast:** validate contrast, borders, and focus in the high-contrast theme alongside light/dark (WCAG).
## Reviews — which rule owns what ## Reviews — which rule owns what
- **Theme, colors, typography, spacing/radius tokens** → `ui-ux-standards.mdc` (widgets read `Theme.of(context)` only) - **Theme, colors, typography, spacing/radius tokens** → `ui-ux-standards.mdc` (widgets read `Theme.of(context)` only)
- **User-visible copy & locales** → `localization.mdc` (ARB / `AppLocalizations`; no UI string literals) - **User-visible copy & locales** → `localization.mdc` (ARB / `AppLocalizations`; no UI string literals)
@@ -0,0 +1,24 @@
---
description: How Cursor rules in this repository must be written and maintained.
globs: [".cursor/rules/**/*.mdc"]
alwaysApply: true
---
# Rule authoring — TaskFlow
## Context
Rules are version-controlled contracts for AI assistants. Poor rules waste context and silently steer every edit.
## Constraints
- One focused concern per file; split broad topics instead of one mega-rule
- Every rule MUST have a clear `description` in frontmatter (one sentence)
- Prefer `alwaysApply: false` with **narrow** `globs` for domain rules — reserve `alwaysApply: true` for meta and safety
- `globs` must be as specific as possible — never `["**/*"]` unless tooling requires it
- Code samples in rules MUST be valid for this project (Dart/Flutter/YAML as appropriate)
- Deprecated guidance is removed, not left commented out
- Each substantive rule includes **Context** (why), **Constraints** (must/must not), and where helpful **Patterns** / **Anti-patterns**
## Anti-patterns
- Domain rules (testing, l10n, a feature) with `alwaysApply: true` — burns context
- Rules with no concrete examples when the topic is code-facing
- Stale feature rules after modules are removed — run `tool/cursor_audit.sh` periodically
@@ -1,6 +1,7 @@
--- ---
description: "UI/UX standards for TaskFlow — always applied" description: "UI/UX standards for TaskFlow"
alwaysApply: true globs: ["lib/**/*.dart", "test/**/*.dart", "integration_test/**/*.dart"]
alwaysApply: false
--- ---
# UI / UX Standards — TaskFlow # UI / UX Standards — TaskFlow
@@ -46,3 +47,4 @@ alwaysApply: true
- Minimum contrast ratio: 4.5:1 (WCAG AA) - Minimum contrast ratio: 4.5:1 (WCAG AA)
- Test with TalkBack / VoiceOver before each release - Test with TalkBack / VoiceOver before each release
- **High contrast theme:** validate loading, empty, and error states; never rely on color alone for meaning (use icons/text/semantics).
@@ -346,6 +346,7 @@ Follow Red → Green → Refactor strictly. Show actual terminal output at each
flutter test test/features/[feature]/ --no-pub flutter test test/features/[feature]/ --no-pub
``` ```
**Paste the actual output here before proceeding.** **Paste the actual output here before proceeding.**
If failures are unclear or non-deterministic after one iteration, use **`/debug`** with full pasted output before guessing fixes.
### Step 3b — Green: Implement ### Step 3b — Green: Implement
@@ -373,6 +374,8 @@ Follow Red → Green → Refactor strictly. Show actual terminal output at each
**Paste the actual output here before proceeding.** **Paste the actual output here before proceeding.**
3. Run tests once more to confirm still green. 3. Run tests once more to confirm still green.
If output stays red or errors are ambiguous after two focused attempts, stop and use **`/debug`** with the full failing command and log before changing more code.
--- ---
## Phase 4: Integration Test Generation ## Phase 4: Integration Test Generation
@@ -505,6 +508,8 @@ VERIFICATION REQUIRED — paste real output for each:
``` ```
If any check fails: invoke `superpowers:systematic-debugging` to diagnose before retrying. If any check fails: invoke `superpowers:systematic-debugging` to diagnose before retrying.
For noisy errors or weak reproduction, run **`/debug`** and fill the BugReport skeleton with logs before retrying.
For small, isolated changes, **`/verify`** is enough to re-check tests and analyze without re-reading this whole skill.
Do not proceed to Phase 7 until all four checks are green. Do not proceed to Phase 7 until all four checks are green.
--- ---
@@ -0,0 +1,88 @@
---
name: Debug issue
description: Triage failing tests, analyze, CI, or runtime errors with an evidence-first BugReport. Use /debug and paste logs; invokes systematic-debugging before fixes. Stack Riverpod / Feature-First / Supabase / ios, android, web.
---
# Debug — TaskFlow
Triage failures (tests, CI, runtime, or build) **without** jumping to fixes. Stay in hypothesis-and-evidence mode until root cause is stated.
**Stack context:** **Riverpod** / **Feature-First** / **Supabase** / platforms: ios, android, web. Flavors: dev, prod. Codegen: freezed, json_serializable.
## Usage
```
/debug <what failed — paste error output, command, or symptom>
```
**Examples:**
- `/debug flutter test fails on auth_cubit_test — paste output`
- `/debug CI analyze step — paste log excerpt`
- `/debug app crashes on cold start after last change`
---
## Phase 0 — Normalize the report (BugReport)
Emit this skeleton **before** deep analysis. If the user already pasted logs, map them into the fields instead of re-asking.
| Field | Content |
|-------|---------|
| **Summary** | One line: what broke |
| **Expected** | What should happen |
| **Actual** | What happened (symptom + error text) |
| **Repro steps** | Numbered, minimal |
| **Scope / files touched** | Paths or PR slice |
| **Environment** | OS, Flutter/Dart version if known, device vs simulator, flavor |
| **Evidence** | Pasted command output, stack trace, or screenshot notes |
---
## Phase 1 — Evidence checklist (Flutter-aware)
Gather or request **concrete** evidence. Do not guess versions or config.
1. **`flutter doctor -v`** — paste output when environment is unknown or iOS/Android toolchain errors appear.
2. **Failing command** — full invocation + **verbatim** tail of output (e.g. `flutter test …`, `dart test …`, `flutter analyze`).
3. **`flutter analyze`** — if not already in the failure log, run or ask the user to run and paste.
4. **Flavors** — this project uses: **dev, prod**. Confirm which flavor was active if the failure is env-specific.
5. **Platforms****ios, android, web**. Narrow reproduction to the platform that failed when relevant.
6. **Codegen** — tools: **freezed, json_serializable**. When this is not `none`, remind to run `dart run build_runner build --delete-conflicting-outputs` after generated files changed, and to align with `.cursor/hooks/` / `lefthook run pre-commit` when hooks are present.
7. **Testing depth****unit_widget**; E2E tool: **patrol**. Match the failure to the right layer (unit vs widget vs integration).
---
## Phase 2 — Root cause (no code yet)
> **Invoke skill: `superpowers:systematic-debugging`**
Produce **one paragraph**: hypothesis tied to **specific lines** in the pasted evidence. Mark confidence (high / medium / low). **No code changes** in this phase.
---
## Phase 3 — Fix (only after Phase 2)
> **Invoke skill: `superpowers:systematic-debugging`** again while iterating fixes.
When proposing changes:
- Respect architecture boundaries for **Feature-First**:
- features/auth/ MUST NOT import from features/home/ etc.
- Shared code lives in core/ or shared/ only
- Each feature is self-contained: screen + provider + model + repo
- Always consider: `.cursor/rules/flutter-core.mdc`, `.cursor/rules/security-standards.mdc`, `.cursor/rules/project-context.mdc`, and state-management rules for **Riverpod**.
After each fix attempt, re-run the **same** failing command and paste new output.
---
## ACTION REQUIRED
If evidence is missing, **stop** and print:
1. Exact commands to run (copy-paste ready).
2. What to paste back (full error blocks, not summaries).
3. If the user cannot run commands: state assumptions explicitly and set confidence to **low**.
**Template version:** 1.0.4
@@ -0,0 +1,82 @@
---
name: Explain code
description: Explain what code does without editing it. Use /explain with a path or symbol; covers Riverpod state, Supabase I/O, routing GoRouter, and failure modes. Ask when facts are not in the repo.
---
# Explain — TaskFlow
Explain **what the code is doing****do not** change production code unless the user explicitly asks for a fix.
**Stack:** **Riverpod** / **Feature-First** / **GoRouter** / **Supabase** / platforms: ios, android, web. Package: `com.test.taskflow`.
## Usage
```
/explain <file path, widget name, class, or symbol>
```
**Examples:**
- `/explain lib/features/cart/cart_cubit.dart`
- `/explain how checkout routes after payment`
- `/explain CartPage build method`
---
## Output format (use these headings in order)
### Purpose
One short paragraph: why this code exists in the product context (**TaskFlow** — Task management app).
### Public API
Surface area: public classes/methods, constructors, and what callers are expected to pass. Note codegen involvement when **freezed, json_serializable** is not `none`.
### Call flow
Ordered steps from entry point (e.g. widget `build`, route handler, Bloc `on<Event>`, Riverpod `build`, GetX controller lifecycle) through collaborators. Tie navigation to **GoRouter** where relevant.
### State and side effects
How state is held and updated for **Riverpod** (riverpod). Mention async work, listeners, and disposal. Reference **- features/auth/ MUST NOT import from features/home/ etc.
- Shared code lives in core/ or shared/ only
- Each feature is self-contained: screen + provider + model + repo** if layering is unclear.
### I/O and backends
Network, local storage, or platform channels touching **Supabase** (and **Supabase Auth** where auth applies: supabase_auth). Do **not** invent API shapes not visible in the repo.
### Failure modes
What can go wrong: null paths, error states, race conditions, missing permissions on ios, android, web, auth edge cases.
### Suggested tests
Ideas aligned with **unit_widget** and **patrol**; for **Riverpod**, prefer patterns like:
```
final container = ProviderContainer(overrides: [myProvider.overrideWithValue(mockValue)]); addTearDown(container.dispose);
```
### Unknowns — questions for you
If behavior depends on runtime config, native projects, remote API contracts, or secrets not in tree: **stop** and list **specific** questions. Do not fabricate facts.
---
## References
- **Project brief:** `project-brief.yaml` — feature modules: auth, tasks, profile; special features: .
- **Scale:** small; i18n locales: en, fr (when explaining localization).
- **Design:** none; Figma URL: .
- **API docs format:** none (path: ).
- **Related repos (if any):**
_No Git repository URLs listed._ Add entries under `references.repos` in project-brief.yaml when other repos are part of the product context.
- **Local paths (if any):**
_No local paths listed._ Add monorepo packages or sibling folders under `references.local_paths` in project-brief.yaml when relevant.
**Template version:** 1.0.4

Some files were not shown because too many files have changed in this diff Show More