parsePlanContent static method

List<Issue> parsePlanContent(
  1. String content, {
  2. Map<String, String> statesById = const <String, String>{},
})

Parses <task> XML blocks from PLAN.md content into normalized issues.

Visible for testing.

Implementation

static List<Issue> parsePlanContent(
  String content, {
  Map<String, String> statesById = const <String, String>{},
}) {
  final taskRegex = RegExp(r'<task[^>]*>.*?</task>', dotAll: true);
  final matches = taskRegex.allMatches(content);
  final issues = <Issue>[];
  for (final match in matches) {
    final block = match.group(0)!;
    final XmlElement element;
    try {
      element = XmlDocument.parse(block).rootElement;
    } catch (_) {
      continue;
    }

    final id = element.getAttribute('id') ?? '';
    if (id.isEmpty) continue;

    final type = element.getAttribute('type') ?? 'implement';

    final nameElements = element.findElements('n');
    final title = nameElements.isNotEmpty
        ? nameElements.first.innerText.trim()
        : 'Task $id';

    final files = element
        .findElements('files')
        .expand((f) => f.findElements('file'))
        .map((f) => f.innerText.trim())
        .toList();

    final objective = _firstChildText(element, 'objective');
    final verification = _firstChildText(element, 'verification');
    final acceptance = _firstChildText(element, 'acceptance');

    final descriptionBuffer = StringBuffer();
    if (objective != null) {
      descriptionBuffer
        ..writeln('## Objective')
        ..writeln(objective);
    }
    if (verification != null) {
      descriptionBuffer
        ..writeln()
        ..writeln('## Verification')
        ..writeln(verification);
    }
    if (acceptance != null) {
      descriptionBuffer
        ..writeln()
        ..writeln('## Acceptance')
        ..writeln(acceptance);
    }
    if (files.isNotEmpty) {
      descriptionBuffer
        ..writeln()
        ..writeln('## Files')
        ..writeln(files.map((f) => '- $f').join('\n'));
    }

    final state = statesById[id] ?? 'Todo';

    issues.add(
      Issue(
        id: id,
        identifier: 'PLAN-$id',
        title: title,
        description: descriptionBuffer.isEmpty
            ? null
            : descriptionBuffer.toString().trimRight(),
        priority: _safeParseInt(id),
        state: state,
        labels: <String>[type.toLowerCase()],
      ),
    );
  }
  return issues;
}