mirror of
https://github.com/kuhyx/todo-app.git
synced 2026-11-10 19:07:02 +01:00
Some checks are pending
dependency freshness / dependencies on newest stable (push) Waiting to run
file-length / file length <= 250 lines (push) Waiting to run
md-naming / markdown naming convention (push) Waiting to run
release-apk / release (APK) (push) Waiting to run
repo-contract / repo contract (push) Waiting to run
New optional note field created_by (kuhy|claude), stored like by/repo and omitted when unset so older builds stay wire-compatible. App UI stamps kuhy; MCP add_task and the CLI stamp claude. list/get/search output includes it. Human notes sort above Claude's; Claude notes are hidden behind a per-device eye toggle. CLI --backfill-created-by [--dry-run] classifies old notes by the node that wrote created_at, writing only the created_by field. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
65 lines
2.0 KiB
Dart
65 lines
2.0 KiB
Dart
import 'package:todo/cli/cli_args.dart';
|
|
import 'package:todo/data/note.dart';
|
|
|
|
const _minPrefix = 8;
|
|
|
|
/// No note, or more than one, matches an id argument; [message] says which.
|
|
class NoteLookupError extends FormatException {
|
|
NoteLookupError._(super.message);
|
|
|
|
@override
|
|
String toString() => message;
|
|
}
|
|
|
|
/// The note with id [ref], else the only one whose id starts with [ref].
|
|
Note findNote(Iterable<Note> notes, String ref) {
|
|
final exact = notes.where((n) => n.id == ref).firstOrNull;
|
|
if (exact != null) return exact;
|
|
if (ref.length < _minPrefix) {
|
|
throw NoteLookupError._(
|
|
'no live note with id $ref (an id prefix needs at least '
|
|
'$_minPrefix characters)',
|
|
);
|
|
}
|
|
final matches = notes.where((n) => n.id.startsWith(ref)).toList();
|
|
if (matches.isEmpty) throw NoteLookupError._('no live note with id $ref');
|
|
if (matches.length > 1) {
|
|
throw NoteLookupError._(
|
|
'id prefix $ref matches ${matches.length} notes; give more of the id:\n'
|
|
'${matches.map((n) => ' ${n.id} ${_title(n)}').join('\n')}',
|
|
);
|
|
}
|
|
return matches.single;
|
|
}
|
|
|
|
/// `<8-char id>\t<status>\t<priority>\t<repo or ->\t<title>\t<created_by>`.
|
|
String listLine(Note note) => [
|
|
if (note.id.length <= _minPrefix)
|
|
note.id
|
|
else
|
|
note.id.substring(0, _minPrefix),
|
|
CliArgs.statusName(note.status),
|
|
note.priority.name,
|
|
note.repo ?? '-',
|
|
_title(note),
|
|
note.createdBy ?? 'kuhy',
|
|
].join('\t');
|
|
|
|
String _title(Note note) => note.text.split('\n').first;
|
|
|
|
/// The note's metadata lines, `---`, then its text.
|
|
String noteDetail(Note note) => [
|
|
'id: ${note.id}',
|
|
'priority: ${note.priority.name}',
|
|
'status: ${CliArgs.statusName(note.status)}',
|
|
'created: ${note.createdAt.toIso8601String()}',
|
|
'updated: ${note.updatedAt.toIso8601String()}',
|
|
if (note.repo case final v?) 'repo: $v',
|
|
if (note.doneCheck case final v?) 'done_check: $v',
|
|
if (note.by case final v?) 'by: $v',
|
|
if (note.evidence case final v?) 'evidence: $v',
|
|
'created_by: ${note.createdBy ?? 'kuhy'}',
|
|
'---',
|
|
note.text,
|
|
].join('\n');
|