run method
Runs this command.
The return value is wrapped in a Future if necessary and returned by
CommandRunner.runCommand.
Implementation
@override
Future<void> run() async {
if (!ContextService.isInitialized()) {
print('ContextForge has not been initialized.\nRun: contextforge init');
return;
}
ContextService.ensureDataFilesExist();
// Prompt for task details
stdout.write('Task ID: ');
var id = stdin.readLineSync() ?? '';
id = id.trim();
if (id.isEmpty) {
print('Task ID cannot be empty.');
return;
}
stdout.write('Task title: ');
final title = stdin.readLineSync() ?? '';
if (title.isEmpty) {
print('Task title cannot be empty.');
return;
}
stdout.write('Short description (one line): ');
final description = stdin.readLineSync() ?? '';
stdout.write('Status (todo/in-progress/completed) [todo]: ');
final statusInput = stdin.readLineSync() ?? '';
final status = _validateStatus(statusInput.isEmpty ? 'todo' : statusInput);
if (status == null) {
print('Invalid status. Use: todo, in-progress, or completed');
return;
}
// Check active + archive for duplicate IDs
final features = ContextService.loadAllFeatures();
if (features.any((f) => f.id == id)) {
print('Task with ID "$id" already exists.');
return;
}
final detailFile = 'tasks/$id.md';
final taskMarkdownPath = '.contextforge/data/tasks/$id.md';
// Ensure tasks directory exists
try {
Directory('.contextforge/data/tasks').createSync(recursive: true);
} catch (e) {
print('Error creating tasks directory: $e');
return;
}
// Create markdown file with template
final markdownContent = _createMarkdownTemplate(title, description, status);
try {
File(taskMarkdownPath).writeAsStringSync(markdownContent);
} catch (e) {
print('Error creating task file: $e');
return;
}
// Create feature and add to features.yaml
final feature = Feature(
id: id,
title: title,
status: status,
description: description,
files: [],
detailFile: detailFile,
);
// Add and save
features.add(feature);
ContextService.saveFeatures(features);
print('✔ Task "$title" added successfully (ID: $id)');
}