chore: bump cursor_templates_version to 1.0.4 and update related files
- Updated cursor_templates_version in project-brief.yaml to 1.0.4 for reproducibility. - Enhanced CHANGELOG with fixes and features for version 1.0.4, including improved template resolution for global installs. - Updated pubspec.yaml and VERSION file to reflect the new version. - Added new templates for AGENTS.md and lefthook.yaml to the generator. - Adjusted tests to include new root-level files in the output. This release addresses template resolution issues and introduces new repo-level configuration files.
This commit is contained in:
@@ -70,7 +70,7 @@ class Renderer {
|
||||
'ARCH_IMPORT_RULES': _archImportRules(brief.architecture),
|
||||
'TEST_PATTERN': _testPattern(brief.stateManagement),
|
||||
'LOCALES_LIST': brief.locales.join(', '),
|
||||
'TEMPLATE_VERSION': '1.0.1',
|
||||
'TEMPLATE_VERSION': '1.0.4',
|
||||
};
|
||||
}
|
||||
|
||||
@@ -93,6 +93,9 @@ class Renderer {
|
||||
}
|
||||
|
||||
static String _templatePath(String templateSrc, String key) {
|
||||
if (key.startsWith('root/')) {
|
||||
return p.join(templateSrc, '$key.tmpl');
|
||||
}
|
||||
if (key.startsWith('skills/')) {
|
||||
final skillName = p.basename(key);
|
||||
return p.join(templateSrc, 'skills', skillName, 'SKILL.md.tmpl');
|
||||
@@ -108,6 +111,9 @@ class Renderer {
|
||||
}
|
||||
|
||||
static String _outputPath(String key) {
|
||||
if (key.startsWith('root/')) {
|
||||
return '__root__/${key.substring(5)}';
|
||||
}
|
||||
if (key.startsWith('skills/')) {
|
||||
final name = p.basename(key);
|
||||
return 'skills/$name/SKILL.md';
|
||||
|
||||
@@ -94,6 +94,8 @@ class Resolver {
|
||||
files.add('agents/migration-agent');
|
||||
}
|
||||
|
||||
files.addAll(['root/AGENTS.md', 'root/lefthook.yaml']);
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
@@ -127,6 +129,7 @@ class Resolver {
|
||||
if (key == 'skills/build') return 'Always included — universal TDD-first feature implementation command';
|
||||
if (key.contains('api-client')) return 'api_docs.format is set';
|
||||
if (key.contains('realtime')) return 'features.special contains realtime';
|
||||
if (key.startsWith('root/')) return 'Repo-level companion files';
|
||||
return 'Included';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,17 +8,19 @@ import 'models.dart';
|
||||
import 'logger.dart';
|
||||
|
||||
const _lockFileName = '.cursor-gen-lock.json';
|
||||
const _currentVersion = '1.0.1';
|
||||
|
||||
/// Current flutter-cursor-templates bundle version (lock file, wizard, --check-updates).
|
||||
const kCursorTemplatesVersion = '1.0.4';
|
||||
|
||||
class VersionManager {
|
||||
/// Check if the project's locked version differs from the current template version
|
||||
static Future<VersionStatus> check({required ProjectBrief brief}) async {
|
||||
final locked = brief.cursorTemplatesVersion ?? 'unset';
|
||||
final hasUpdate = locked != _currentVersion && locked != 'unset';
|
||||
final hasUpdate = locked != kCursorTemplatesVersion && locked != 'unset';
|
||||
return VersionStatus(
|
||||
hasUpdate: hasUpdate,
|
||||
currentVersion: locked,
|
||||
latestVersion: _currentVersion,
|
||||
latestVersion: kCursorTemplatesVersion,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -30,7 +32,7 @@ class VersionManager {
|
||||
}) async {
|
||||
final briefHash = await _fileHash(briefPath);
|
||||
final lock = {
|
||||
'templateVersion': _currentVersion,
|
||||
'templateVersion': kCursorTemplatesVersion,
|
||||
'generatedAt': DateTime.now().toIso8601String(),
|
||||
'briefHash': briefHash,
|
||||
'projectName': brief.projectName,
|
||||
@@ -72,15 +74,15 @@ class VersionManager {
|
||||
final lockedVersion = lock['templateVersion'] as String? ?? 'unknown';
|
||||
Logger.info('Template version check:');
|
||||
Logger.info(' Locked: $lockedVersion');
|
||||
Logger.info(' Latest: $_currentVersion');
|
||||
Logger.info(' Latest: $kCursorTemplatesVersion');
|
||||
|
||||
if (lockedVersion == _currentVersion) {
|
||||
if (lockedVersion == kCursorTemplatesVersion) {
|
||||
Logger.success(' ✔ You are on the latest template version.');
|
||||
} else {
|
||||
Logger.warn(' ⚠ Update available!');
|
||||
Logger.info('\nTo update:');
|
||||
Logger.info(
|
||||
' 1. Update cursor_templates_version in project-brief.yaml to "$_currentVersion"');
|
||||
' 1. Update cursor_templates_version in project-brief.yaml to "$kCursorTemplatesVersion"');
|
||||
Logger.info(' 2. Run: cursor_gen --diff (preview changes)');
|
||||
Logger.info(' 3. Run: cursor_gen --refresh (apply updates)');
|
||||
Logger.info(
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import 'dart:io';
|
||||
import 'logger.dart';
|
||||
import 'version_manager.dart';
|
||||
|
||||
class Wizard {
|
||||
static Future<void> run({required String outputPath}) async {
|
||||
@@ -80,6 +81,22 @@ class Wizard {
|
||||
|
||||
Logger.info('');
|
||||
|
||||
// Features
|
||||
final modulesRaw = _ask(
|
||||
'Feature modules (comma-separated, optional)', hint: '');
|
||||
answers['feature_modules'] = _splitComma(modulesRaw);
|
||||
answers['special_features'] = _askMultiChoice(
|
||||
'Special capabilities (optional)',
|
||||
[
|
||||
'realtime',
|
||||
'push_notifications',
|
||||
'deep_linking',
|
||||
'offline_first',
|
||||
],
|
||||
defaults: []);
|
||||
|
||||
Logger.info('');
|
||||
|
||||
// Environments
|
||||
final flavorsInput =
|
||||
_ask('Build flavors (comma-separated)', hint: 'dev,staging,prod');
|
||||
@@ -92,10 +109,60 @@ class Wizard {
|
||||
answers['testing_depth'] = _askChoice(
|
||||
'Testing depth', ['unit_widget', 'integration', 'e2e', 'full'],
|
||||
defaultIdx: 0);
|
||||
final testingDepth = answers['testing_depth'] as String;
|
||||
if (testingDepth == 'e2e' || testingDepth == 'full') {
|
||||
answers['e2e_tool'] = _askChoice(
|
||||
'E2E tool', ['patrol', 'maestro'], defaultIdx: 0);
|
||||
} else {
|
||||
answers['e2e_tool'] = 'patrol';
|
||||
}
|
||||
|
||||
Logger.info('');
|
||||
|
||||
// Design (validator-aligned sources)
|
||||
answers['design_source'] = _askChoice('Design source', [
|
||||
'figma_mcp',
|
||||
'figma_manual',
|
||||
'native_ref',
|
||||
'html_ref',
|
||||
'none',
|
||||
], defaultIdx: 4);
|
||||
final designSource = answers['design_source'] as String;
|
||||
if (designSource == 'figma_mcp' || designSource == 'figma_manual') {
|
||||
answers['figma_url'] =
|
||||
_ask('Figma file URL', hint: 'https://www.figma.com/design/...');
|
||||
} else {
|
||||
answers['figma_url'] = '';
|
||||
}
|
||||
|
||||
Logger.info('');
|
||||
|
||||
// API docs
|
||||
answers['api_docs_format'] = _askChoice(
|
||||
'API docs format', ['openapi', 'postman', 'markdown', 'none'],
|
||||
defaultIdx: 3);
|
||||
final apiFmt = answers['api_docs_format'] as String;
|
||||
if (apiFmt != 'none') {
|
||||
answers['api_docs_path'] =
|
||||
_ask('Path to API spec (repo-relative)', hint: 'docs/api.yaml');
|
||||
} else {
|
||||
answers['api_docs_path'] = '';
|
||||
}
|
||||
|
||||
Logger.info('');
|
||||
|
||||
// Localization
|
||||
final l10n = _askBool('Enable localization / i18n?', defaultYes: false);
|
||||
answers['i18n'] = l10n;
|
||||
if (l10n) {
|
||||
final locRaw =
|
||||
_ask('Locale codes (comma-separated)', hint: 'en');
|
||||
var locales = _splitComma(locRaw);
|
||||
if (locales.isEmpty) locales = ['en'];
|
||||
answers['locales'] = locales;
|
||||
} else {
|
||||
answers['locales'] = <String>['en'];
|
||||
}
|
||||
|
||||
// Telemetry opt-in (Pillar 6)
|
||||
final telemetry = _askBool(
|
||||
@@ -183,13 +250,18 @@ class Wizard {
|
||||
final refLocals = List<String>.from(a['local_paths'] as List);
|
||||
final themes = List<String>.from(a['theme_variants'] as List);
|
||||
final roles = List<String>.from(a['role_names'] as List);
|
||||
final featureMods = List<String>.from(a['feature_modules'] as List);
|
||||
final specialFeats = List<String>.from(a['special_features'] as List);
|
||||
final locales = List<String>.from(a['locales'] as List);
|
||||
final figmaUrl = a['figma_url'] as String;
|
||||
final apiPath = a['api_docs_path'] as String;
|
||||
return '''# project-brief.yaml — cursor_gen configuration
|
||||
# Generated by cursor_gen --wizard
|
||||
# Run: cursor_gen to generate .cursor/
|
||||
# Run: cursor_gen --refresh to update after changes
|
||||
|
||||
# Pillar 1: Pin to template version for reproducibility
|
||||
cursor_templates_version: "1.0.1"
|
||||
cursor_templates_version: "$kCursorTemplatesVersion"
|
||||
|
||||
project:
|
||||
name: "${a['name']}"
|
||||
@@ -216,15 +288,15 @@ environments:
|
||||
|
||||
testing:
|
||||
depth: "${a['testing_depth']}"
|
||||
e2e_tool: "patrol"
|
||||
e2e_tool: "${a['e2e_tool']}"
|
||||
|
||||
design:
|
||||
source: "none"
|
||||
figma_url: ""
|
||||
source: "${a['design_source']}"
|
||||
figma_url: "${_yamlEsc(figmaUrl)}"
|
||||
|
||||
api_docs:
|
||||
format: "none"
|
||||
path: ""
|
||||
format: "${a['api_docs_format']}"
|
||||
path: "${_yamlEsc(apiPath)}"
|
||||
|
||||
references:
|
||||
repos: [${_yamlQStringList(refRepos)}]
|
||||
@@ -236,12 +308,12 @@ app_context:
|
||||
role_names: [${_yamlQStringList(roles)}]
|
||||
|
||||
features:
|
||||
modules: []
|
||||
special: []
|
||||
modules: [${_yamlQStringList(featureMods)}]
|
||||
special: [${_yamlQStringList(specialFeats)}]
|
||||
|
||||
localization:
|
||||
enabled: ${a['i18n']}
|
||||
locales: ["en"]
|
||||
locales: [${_yamlQStringList(locales)}]
|
||||
|
||||
# Pillar 6: Opt-in local telemetry (logs rule trigger frequency locally)
|
||||
telemetry_opt_in: ${a['telemetry']}
|
||||
|
||||
Reference in New Issue
Block a user