@obsfx/trekker 1.0.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +15 -0
  2. package/dist/index.js +574 -628
  3. package/package.json +2 -1
package/dist/index.js CHANGED
@@ -310,17 +310,498 @@ function wipeProject(cwd = process.cwd()) {
310
310
  deleteDb(cwd);
311
311
  }
312
312
 
313
+ // node_modules/.pnpm/@toon-format+toon@2.1.0/node_modules/@toon-format/toon/dist/index.mjs
314
+ var LIST_ITEM_MARKER = "-";
315
+ var LIST_ITEM_PREFIX = "- ";
316
+ var COMMA = ",";
317
+ var PIPE = "|";
318
+ var DOT = ".";
319
+ var NULL_LITERAL = "null";
320
+ var TRUE_LITERAL = "true";
321
+ var FALSE_LITERAL = "false";
322
+ var BACKSLASH = "\\";
323
+ var DOUBLE_QUOTE = '"';
324
+ var TAB = "\t";
325
+ var DELIMITERS = {
326
+ comma: COMMA,
327
+ tab: TAB,
328
+ pipe: PIPE
329
+ };
330
+ var DEFAULT_DELIMITER = DELIMITERS.comma;
331
+ function escapeString(value) {
332
+ return value.replace(/\\/g, `${BACKSLASH}${BACKSLASH}`).replace(/"/g, `${BACKSLASH}${DOUBLE_QUOTE}`).replace(/\n/g, `${BACKSLASH}n`).replace(/\r/g, `${BACKSLASH}r`).replace(/\t/g, `${BACKSLASH}t`);
333
+ }
334
+ function isBooleanOrNullLiteral(token) {
335
+ return token === TRUE_LITERAL || token === FALSE_LITERAL || token === NULL_LITERAL;
336
+ }
337
+ function normalizeValue(value) {
338
+ if (value === null)
339
+ return null;
340
+ if (typeof value === "object" && value !== null && "toJSON" in value && typeof value.toJSON === "function") {
341
+ const next = value.toJSON();
342
+ if (next !== value)
343
+ return normalizeValue(next);
344
+ }
345
+ if (typeof value === "string" || typeof value === "boolean")
346
+ return value;
347
+ if (typeof value === "number") {
348
+ if (Object.is(value, -0))
349
+ return 0;
350
+ if (!Number.isFinite(value))
351
+ return null;
352
+ return value;
353
+ }
354
+ if (typeof value === "bigint") {
355
+ if (value >= Number.MIN_SAFE_INTEGER && value <= Number.MAX_SAFE_INTEGER)
356
+ return Number(value);
357
+ return value.toString();
358
+ }
359
+ if (value instanceof Date)
360
+ return value.toISOString();
361
+ if (Array.isArray(value))
362
+ return value.map(normalizeValue);
363
+ if (value instanceof Set)
364
+ return Array.from(value).map(normalizeValue);
365
+ if (value instanceof Map)
366
+ return Object.fromEntries(Array.from(value, ([k, v]) => [String(k), normalizeValue(v)]));
367
+ if (isPlainObject(value)) {
368
+ const normalized = {};
369
+ for (const key in value)
370
+ if (Object.prototype.hasOwnProperty.call(value, key))
371
+ normalized[key] = normalizeValue(value[key]);
372
+ return normalized;
373
+ }
374
+ return null;
375
+ }
376
+ function isJsonPrimitive(value) {
377
+ return value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean";
378
+ }
379
+ function isJsonArray(value) {
380
+ return Array.isArray(value);
381
+ }
382
+ function isJsonObject(value) {
383
+ return value !== null && typeof value === "object" && !Array.isArray(value);
384
+ }
385
+ function isEmptyObject(value) {
386
+ return Object.keys(value).length === 0;
387
+ }
388
+ function isPlainObject(value) {
389
+ if (value === null || typeof value !== "object")
390
+ return false;
391
+ const prototype = Object.getPrototypeOf(value);
392
+ return prototype === null || prototype === Object.prototype;
393
+ }
394
+ function isArrayOfPrimitives(value) {
395
+ return value.length === 0 || value.every((item) => isJsonPrimitive(item));
396
+ }
397
+ function isArrayOfArrays(value) {
398
+ return value.length === 0 || value.every((item) => isJsonArray(item));
399
+ }
400
+ function isArrayOfObjects(value) {
401
+ return value.length === 0 || value.every((item) => isJsonObject(item));
402
+ }
403
+ function isValidUnquotedKey(key) {
404
+ return /^[A-Z_][\w.]*$/i.test(key);
405
+ }
406
+ function isIdentifierSegment(key) {
407
+ return /^[A-Z_]\w*$/i.test(key);
408
+ }
409
+ function isSafeUnquoted(value, delimiter = DEFAULT_DELIMITER) {
410
+ if (!value)
411
+ return false;
412
+ if (value !== value.trim())
413
+ return false;
414
+ if (isBooleanOrNullLiteral(value) || isNumericLike(value))
415
+ return false;
416
+ if (value.includes(":"))
417
+ return false;
418
+ if (value.includes('"') || value.includes("\\"))
419
+ return false;
420
+ if (/[[\]{}]/.test(value))
421
+ return false;
422
+ if (/[\n\r\t]/.test(value))
423
+ return false;
424
+ if (value.includes(delimiter))
425
+ return false;
426
+ if (value.startsWith(LIST_ITEM_MARKER))
427
+ return false;
428
+ return true;
429
+ }
430
+ function isNumericLike(value) {
431
+ return /^-?\d+(?:\.\d+)?(?:e[+-]?\d+)?$/i.test(value) || /^0\d+$/.test(value);
432
+ }
433
+ var QUOTED_KEY_MARKER = Symbol("quotedKey");
434
+ function tryFoldKeyChain(key, value, siblings, options, rootLiteralKeys, pathPrefix, flattenDepth) {
435
+ if (options.keyFolding !== "safe")
436
+ return;
437
+ if (!isJsonObject(value))
438
+ return;
439
+ const { segments, tail, leafValue } = collectSingleKeyChain(key, value, flattenDepth ?? options.flattenDepth);
440
+ if (segments.length < 2)
441
+ return;
442
+ if (!segments.every((seg) => isIdentifierSegment(seg)))
443
+ return;
444
+ const foldedKey = buildFoldedKey(segments);
445
+ const absolutePath = pathPrefix ? `${pathPrefix}${DOT}${foldedKey}` : foldedKey;
446
+ if (siblings.includes(foldedKey))
447
+ return;
448
+ if (rootLiteralKeys && rootLiteralKeys.has(absolutePath))
449
+ return;
450
+ return {
451
+ foldedKey,
452
+ remainder: tail,
453
+ leafValue,
454
+ segmentCount: segments.length
455
+ };
456
+ }
457
+ function collectSingleKeyChain(startKey, startValue, maxDepth) {
458
+ const segments = [startKey];
459
+ let currentValue = startValue;
460
+ while (segments.length < maxDepth) {
461
+ if (!isJsonObject(currentValue))
462
+ break;
463
+ const keys = Object.keys(currentValue);
464
+ if (keys.length !== 1)
465
+ break;
466
+ const nextKey = keys[0];
467
+ const nextValue = currentValue[nextKey];
468
+ segments.push(nextKey);
469
+ currentValue = nextValue;
470
+ }
471
+ if (!isJsonObject(currentValue) || isEmptyObject(currentValue))
472
+ return {
473
+ segments,
474
+ tail: undefined,
475
+ leafValue: currentValue
476
+ };
477
+ return {
478
+ segments,
479
+ tail: currentValue,
480
+ leafValue: currentValue
481
+ };
482
+ }
483
+ function buildFoldedKey(segments) {
484
+ return segments.join(DOT);
485
+ }
486
+ function encodePrimitive(value, delimiter) {
487
+ if (value === null)
488
+ return NULL_LITERAL;
489
+ if (typeof value === "boolean")
490
+ return String(value);
491
+ if (typeof value === "number")
492
+ return String(value);
493
+ return encodeStringLiteral(value, delimiter);
494
+ }
495
+ function encodeStringLiteral(value, delimiter = DEFAULT_DELIMITER) {
496
+ if (isSafeUnquoted(value, delimiter))
497
+ return value;
498
+ return `${DOUBLE_QUOTE}${escapeString(value)}${DOUBLE_QUOTE}`;
499
+ }
500
+ function encodeKey(key) {
501
+ if (isValidUnquotedKey(key))
502
+ return key;
503
+ return `${DOUBLE_QUOTE}${escapeString(key)}${DOUBLE_QUOTE}`;
504
+ }
505
+ function encodeAndJoinPrimitives(values, delimiter = DEFAULT_DELIMITER) {
506
+ return values.map((v) => encodePrimitive(v, delimiter)).join(delimiter);
507
+ }
508
+ function formatHeader(length, options) {
509
+ const key = options?.key;
510
+ const fields = options?.fields;
511
+ const delimiter = options?.delimiter ?? COMMA;
512
+ let header = "";
513
+ if (key)
514
+ header += encodeKey(key);
515
+ header += `[${length}${delimiter !== DEFAULT_DELIMITER ? delimiter : ""}]`;
516
+ if (fields) {
517
+ const quotedFields = fields.map((f) => encodeKey(f));
518
+ header += `{${quotedFields.join(delimiter)}}`;
519
+ }
520
+ header += ":";
521
+ return header;
522
+ }
523
+ function* encodeJsonValue(value, options, depth) {
524
+ if (isJsonPrimitive(value)) {
525
+ const encodedPrimitive = encodePrimitive(value, options.delimiter);
526
+ if (encodedPrimitive !== "")
527
+ yield encodedPrimitive;
528
+ return;
529
+ }
530
+ if (isJsonArray(value))
531
+ yield* encodeArrayLines(undefined, value, depth, options);
532
+ else if (isJsonObject(value))
533
+ yield* encodeObjectLines(value, depth, options);
534
+ }
535
+ function* encodeObjectLines(value, depth, options, rootLiteralKeys, pathPrefix, remainingDepth) {
536
+ const keys = Object.keys(value);
537
+ if (depth === 0 && !rootLiteralKeys)
538
+ rootLiteralKeys = new Set(keys.filter((k) => k.includes(".")));
539
+ const effectiveFlattenDepth = remainingDepth ?? options.flattenDepth;
540
+ for (const [key, val] of Object.entries(value))
541
+ yield* encodeKeyValuePairLines(key, val, depth, options, keys, rootLiteralKeys, pathPrefix, effectiveFlattenDepth);
542
+ }
543
+ function* encodeKeyValuePairLines(key, value, depth, options, siblings, rootLiteralKeys, pathPrefix, flattenDepth) {
544
+ const currentPath = pathPrefix ? `${pathPrefix}${DOT}${key}` : key;
545
+ const effectiveFlattenDepth = flattenDepth ?? options.flattenDepth;
546
+ if (options.keyFolding === "safe" && siblings) {
547
+ const foldResult = tryFoldKeyChain(key, value, siblings, options, rootLiteralKeys, pathPrefix, effectiveFlattenDepth);
548
+ if (foldResult) {
549
+ const { foldedKey, remainder, leafValue, segmentCount } = foldResult;
550
+ const encodedFoldedKey = encodeKey(foldedKey);
551
+ if (remainder === undefined) {
552
+ if (isJsonPrimitive(leafValue)) {
553
+ yield indentedLine(depth, `${encodedFoldedKey}: ${encodePrimitive(leafValue, options.delimiter)}`, options.indent);
554
+ return;
555
+ } else if (isJsonArray(leafValue)) {
556
+ yield* encodeArrayLines(foldedKey, leafValue, depth, options);
557
+ return;
558
+ } else if (isJsonObject(leafValue) && isEmptyObject(leafValue)) {
559
+ yield indentedLine(depth, `${encodedFoldedKey}:`, options.indent);
560
+ return;
561
+ }
562
+ }
563
+ if (isJsonObject(remainder)) {
564
+ yield indentedLine(depth, `${encodedFoldedKey}:`, options.indent);
565
+ const remainingDepth = effectiveFlattenDepth - segmentCount;
566
+ const foldedPath = pathPrefix ? `${pathPrefix}${DOT}${foldedKey}` : foldedKey;
567
+ yield* encodeObjectLines(remainder, depth + 1, options, rootLiteralKeys, foldedPath, remainingDepth);
568
+ return;
569
+ }
570
+ }
571
+ }
572
+ const encodedKey = encodeKey(key);
573
+ if (isJsonPrimitive(value))
574
+ yield indentedLine(depth, `${encodedKey}: ${encodePrimitive(value, options.delimiter)}`, options.indent);
575
+ else if (isJsonArray(value))
576
+ yield* encodeArrayLines(key, value, depth, options);
577
+ else if (isJsonObject(value)) {
578
+ yield indentedLine(depth, `${encodedKey}:`, options.indent);
579
+ if (!isEmptyObject(value))
580
+ yield* encodeObjectLines(value, depth + 1, options, rootLiteralKeys, currentPath, effectiveFlattenDepth);
581
+ }
582
+ }
583
+ function* encodeArrayLines(key, value, depth, options) {
584
+ if (value.length === 0) {
585
+ yield indentedLine(depth, formatHeader(0, {
586
+ key,
587
+ delimiter: options.delimiter
588
+ }), options.indent);
589
+ return;
590
+ }
591
+ if (isArrayOfPrimitives(value)) {
592
+ yield indentedLine(depth, encodeInlineArrayLine(value, options.delimiter, key), options.indent);
593
+ return;
594
+ }
595
+ if (isArrayOfArrays(value)) {
596
+ if (value.every((arr) => isArrayOfPrimitives(arr))) {
597
+ yield* encodeArrayOfArraysAsListItemsLines(key, value, depth, options);
598
+ return;
599
+ }
600
+ }
601
+ if (isArrayOfObjects(value)) {
602
+ const header = extractTabularHeader(value);
603
+ if (header)
604
+ yield* encodeArrayOfObjectsAsTabularLines(key, value, header, depth, options);
605
+ else
606
+ yield* encodeMixedArrayAsListItemsLines(key, value, depth, options);
607
+ return;
608
+ }
609
+ yield* encodeMixedArrayAsListItemsLines(key, value, depth, options);
610
+ }
611
+ function* encodeArrayOfArraysAsListItemsLines(prefix, values, depth, options) {
612
+ yield indentedLine(depth, formatHeader(values.length, {
613
+ key: prefix,
614
+ delimiter: options.delimiter
615
+ }), options.indent);
616
+ for (const arr of values)
617
+ if (isArrayOfPrimitives(arr)) {
618
+ const arrayLine = encodeInlineArrayLine(arr, options.delimiter);
619
+ yield indentedListItem(depth + 1, arrayLine, options.indent);
620
+ }
621
+ }
622
+ function encodeInlineArrayLine(values, delimiter, prefix) {
623
+ const header = formatHeader(values.length, {
624
+ key: prefix,
625
+ delimiter
626
+ });
627
+ const joinedValue = encodeAndJoinPrimitives(values, delimiter);
628
+ if (values.length === 0)
629
+ return header;
630
+ return `${header} ${joinedValue}`;
631
+ }
632
+ function* encodeArrayOfObjectsAsTabularLines(prefix, rows, header, depth, options) {
633
+ yield indentedLine(depth, formatHeader(rows.length, {
634
+ key: prefix,
635
+ fields: header,
636
+ delimiter: options.delimiter
637
+ }), options.indent);
638
+ yield* writeTabularRowsLines(rows, header, depth + 1, options);
639
+ }
640
+ function extractTabularHeader(rows) {
641
+ if (rows.length === 0)
642
+ return;
643
+ const firstRow = rows[0];
644
+ const firstKeys = Object.keys(firstRow);
645
+ if (firstKeys.length === 0)
646
+ return;
647
+ if (isTabularArray(rows, firstKeys))
648
+ return firstKeys;
649
+ }
650
+ function isTabularArray(rows, header) {
651
+ for (const row of rows) {
652
+ if (Object.keys(row).length !== header.length)
653
+ return false;
654
+ for (const key of header) {
655
+ if (!(key in row))
656
+ return false;
657
+ if (!isJsonPrimitive(row[key]))
658
+ return false;
659
+ }
660
+ }
661
+ return true;
662
+ }
663
+ function* writeTabularRowsLines(rows, header, depth, options) {
664
+ for (const row of rows)
665
+ yield indentedLine(depth, encodeAndJoinPrimitives(header.map((key) => row[key]), options.delimiter), options.indent);
666
+ }
667
+ function* encodeMixedArrayAsListItemsLines(prefix, items, depth, options) {
668
+ yield indentedLine(depth, formatHeader(items.length, {
669
+ key: prefix,
670
+ delimiter: options.delimiter
671
+ }), options.indent);
672
+ for (const item of items)
673
+ yield* encodeListItemValueLines(item, depth + 1, options);
674
+ }
675
+ function* encodeObjectAsListItemLines(obj, depth, options) {
676
+ if (isEmptyObject(obj)) {
677
+ yield indentedLine(depth, LIST_ITEM_MARKER, options.indent);
678
+ return;
679
+ }
680
+ const entries = Object.entries(obj);
681
+ const [firstKey, firstValue] = entries[0];
682
+ const restEntries = entries.slice(1);
683
+ if (isJsonArray(firstValue) && isArrayOfObjects(firstValue)) {
684
+ const header = extractTabularHeader(firstValue);
685
+ if (header) {
686
+ yield indentedListItem(depth, formatHeader(firstValue.length, {
687
+ key: firstKey,
688
+ fields: header,
689
+ delimiter: options.delimiter
690
+ }), options.indent);
691
+ yield* writeTabularRowsLines(firstValue, header, depth + 2, options);
692
+ if (restEntries.length > 0)
693
+ yield* encodeObjectLines(Object.fromEntries(restEntries), depth + 1, options);
694
+ return;
695
+ }
696
+ }
697
+ const encodedKey = encodeKey(firstKey);
698
+ if (isJsonPrimitive(firstValue))
699
+ yield indentedListItem(depth, `${encodedKey}: ${encodePrimitive(firstValue, options.delimiter)}`, options.indent);
700
+ else if (isJsonArray(firstValue))
701
+ if (firstValue.length === 0)
702
+ yield indentedListItem(depth, `${encodedKey}${formatHeader(0, { delimiter: options.delimiter })}`, options.indent);
703
+ else if (isArrayOfPrimitives(firstValue))
704
+ yield indentedListItem(depth, `${encodedKey}${encodeInlineArrayLine(firstValue, options.delimiter)}`, options.indent);
705
+ else {
706
+ yield indentedListItem(depth, `${encodedKey}${formatHeader(firstValue.length, { delimiter: options.delimiter })}`, options.indent);
707
+ for (const item of firstValue)
708
+ yield* encodeListItemValueLines(item, depth + 2, options);
709
+ }
710
+ else if (isJsonObject(firstValue)) {
711
+ yield indentedListItem(depth, `${encodedKey}:`, options.indent);
712
+ if (!isEmptyObject(firstValue))
713
+ yield* encodeObjectLines(firstValue, depth + 2, options);
714
+ }
715
+ if (restEntries.length > 0)
716
+ yield* encodeObjectLines(Object.fromEntries(restEntries), depth + 1, options);
717
+ }
718
+ function* encodeListItemValueLines(value, depth, options) {
719
+ if (isJsonPrimitive(value))
720
+ yield indentedListItem(depth, encodePrimitive(value, options.delimiter), options.indent);
721
+ else if (isJsonArray(value))
722
+ if (isArrayOfPrimitives(value))
723
+ yield indentedListItem(depth, encodeInlineArrayLine(value, options.delimiter), options.indent);
724
+ else {
725
+ yield indentedListItem(depth, formatHeader(value.length, { delimiter: options.delimiter }), options.indent);
726
+ for (const item of value)
727
+ yield* encodeListItemValueLines(item, depth + 1, options);
728
+ }
729
+ else if (isJsonObject(value))
730
+ yield* encodeObjectAsListItemLines(value, depth, options);
731
+ }
732
+ function indentedLine(depth, content, indentSize) {
733
+ return " ".repeat(indentSize * depth) + content;
734
+ }
735
+ function indentedListItem(depth, content, indentSize) {
736
+ return indentedLine(depth, LIST_ITEM_PREFIX + content, indentSize);
737
+ }
738
+ function applyReplacer(root, replacer) {
739
+ const replacedRoot = replacer("", root, []);
740
+ if (replacedRoot === undefined)
741
+ return transformChildren(root, replacer, []);
742
+ return transformChildren(normalizeValue(replacedRoot), replacer, []);
743
+ }
744
+ function transformChildren(value, replacer, path) {
745
+ if (isJsonObject(value))
746
+ return transformObject(value, replacer, path);
747
+ if (isJsonArray(value))
748
+ return transformArray(value, replacer, path);
749
+ return value;
750
+ }
751
+ function transformObject(obj, replacer, path) {
752
+ const result = {};
753
+ for (const [key, value] of Object.entries(obj)) {
754
+ const childPath = [...path, key];
755
+ const replacedValue = replacer(key, value, childPath);
756
+ if (replacedValue === undefined)
757
+ continue;
758
+ result[key] = transformChildren(normalizeValue(replacedValue), replacer, childPath);
759
+ }
760
+ return result;
761
+ }
762
+ function transformArray(arr, replacer, path) {
763
+ const result = [];
764
+ for (let i = 0;i < arr.length; i++) {
765
+ const value = arr[i];
766
+ const childPath = [...path, i];
767
+ const replacedValue = replacer(String(i), value, childPath);
768
+ if (replacedValue === undefined)
769
+ continue;
770
+ const normalizedValue = normalizeValue(replacedValue);
771
+ result.push(transformChildren(normalizedValue, replacer, childPath));
772
+ }
773
+ return result;
774
+ }
775
+ function encode(input, options) {
776
+ return Array.from(encodeLines(input, options)).join(`
777
+ `);
778
+ }
779
+ function encodeLines(input, options) {
780
+ const normalizedValue = normalizeValue(input);
781
+ const resolvedOptions = resolveOptions(options);
782
+ return encodeJsonValue(resolvedOptions.replacer ? applyReplacer(normalizedValue, resolvedOptions.replacer) : normalizedValue, resolvedOptions, 0);
783
+ }
784
+ function resolveOptions(options) {
785
+ return {
786
+ indent: options?.indent ?? 2,
787
+ delimiter: options?.delimiter ?? DEFAULT_DELIMITER,
788
+ keyFolding: options?.keyFolding ?? "off",
789
+ flattenDepth: options?.flattenDepth ?? Number.POSITIVE_INFINITY,
790
+ replacer: options?.replacer
791
+ };
792
+ }
793
+
313
794
  // src/utils/output.ts
314
- var jsonMode = false;
315
- function setJsonMode(enabled) {
316
- jsonMode = enabled;
795
+ var toonMode = false;
796
+ function setToonMode(enabled) {
797
+ toonMode = enabled;
317
798
  }
318
- function isJsonMode() {
319
- return jsonMode;
799
+ function isToonMode() {
800
+ return toonMode;
320
801
  }
321
802
  function output(data) {
322
- if (jsonMode) {
323
- console.log(JSON.stringify(data, null, 2));
803
+ if (toonMode) {
804
+ console.log(encode(data));
324
805
  } else if (typeof data === "string") {
325
806
  console.log(data);
326
807
  } else {
@@ -328,8 +809,8 @@ function output(data) {
328
809
  }
329
810
  }
330
811
  function success(message, data) {
331
- if (jsonMode) {
332
- console.log(JSON.stringify({ success: true, message, data }, null, 2));
812
+ if (toonMode) {
813
+ console.log(encode({ success: true, message, data }));
333
814
  } else {
334
815
  console.log(`\u2713 ${message}`);
335
816
  if (data) {
@@ -338,8 +819,8 @@ function success(message, data) {
338
819
  }
339
820
  }
340
821
  function error(message, details) {
341
- if (jsonMode) {
342
- console.error(JSON.stringify({ success: false, error: message, details }, null, 2));
822
+ if (toonMode) {
823
+ console.error(encode({ success: false, error: message, details }));
343
824
  } else {
344
825
  console.error(`\u2717 Error: ${message}`);
345
826
  if (details) {
@@ -348,7 +829,7 @@ function error(message, details) {
348
829
  }
349
830
  }
350
831
  function info(message) {
351
- if (!jsonMode) {
832
+ if (!toonMode) {
352
833
  console.log(message);
353
834
  }
354
835
  }
@@ -613,7 +1094,7 @@ epicCommand.command("create").description("Create a new epic").requiredOption("-
613
1094
  priority: parsePriority(options.priority),
614
1095
  status: parseStatus(options.status, "epic")
615
1096
  });
616
- if (isJsonMode()) {
1097
+ if (isToonMode()) {
617
1098
  output(epic);
618
1099
  } else {
619
1100
  success(`Epic created: ${epic.id}`);
@@ -628,7 +1109,7 @@ epicCommand.command("list").description("List all epics").option("-s, --status <
628
1109
  try {
629
1110
  const status = parseStatus(options.status, "epic");
630
1111
  const epics2 = listEpics(status);
631
- if (isJsonMode()) {
1112
+ if (isToonMode()) {
632
1113
  output(epics2);
633
1114
  } else {
634
1115
  console.log(formatEpicList(epics2));
@@ -645,7 +1126,7 @@ epicCommand.command("show <epic-id>").description("Show epic details").action((e
645
1126
  error(`Epic not found: ${epicId}`);
646
1127
  process.exit(1);
647
1128
  }
648
- if (isJsonMode()) {
1129
+ if (isToonMode()) {
649
1130
  output(epic);
650
1131
  } else {
651
1132
  console.log(formatEpic(epic));
@@ -663,7 +1144,7 @@ epicCommand.command("update <epic-id>").description("Update an epic").option("-t
663
1144
  priority: parsePriority(options.priority),
664
1145
  status: parseStatus(options.status, "epic")
665
1146
  });
666
- if (isJsonMode()) {
1147
+ if (isToonMode()) {
667
1148
  output(epic);
668
1149
  } else {
669
1150
  success(`Epic updated: ${epic.id}`);
@@ -805,7 +1286,7 @@ taskCommand.command("create").description("Create a new task").requiredOption("-
805
1286
  tags: options.tags,
806
1287
  epicId: options.epic
807
1288
  });
808
- if (isJsonMode()) {
1289
+ if (isToonMode()) {
809
1290
  output(task);
810
1291
  } else {
811
1292
  success(`Task created: ${task.id}`);
@@ -824,7 +1305,7 @@ taskCommand.command("list").description("List all tasks").option("-s, --status <
824
1305
  epicId: options.epic,
825
1306
  parentTaskId: null
826
1307
  });
827
- if (isJsonMode()) {
1308
+ if (isToonMode()) {
828
1309
  output(tasks2);
829
1310
  } else {
830
1311
  console.log(formatTaskList(tasks2));
@@ -841,7 +1322,7 @@ taskCommand.command("show <task-id>").description("Show task details").action((t
841
1322
  error(`Task not found: ${taskId}`);
842
1323
  process.exit(1);
843
1324
  }
844
- if (isJsonMode()) {
1325
+ if (isToonMode()) {
845
1326
  output(task);
846
1327
  } else {
847
1328
  console.log(formatTask(task));
@@ -871,7 +1352,7 @@ taskCommand.command("update <task-id>").description("Update a task").option("-t,
871
1352
  updateInput.epicId = options.epic;
872
1353
  }
873
1354
  const task = updateTask(taskId, updateInput);
874
- if (isJsonMode()) {
1355
+ if (isToonMode()) {
875
1356
  output(task);
876
1357
  } else {
877
1358
  success(`Task updated: ${task.id}`);
@@ -911,7 +1392,7 @@ subtaskCommand.command("create <parent-task-id>").description("Create a new subt
911
1392
  parentTaskId,
912
1393
  epicId: parent.epicId ?? undefined
913
1394
  });
914
- if (isJsonMode()) {
1395
+ if (isToonMode()) {
915
1396
  output(subtask);
916
1397
  } else {
917
1398
  success(`Subtask created: ${subtask.id}`);
@@ -930,7 +1411,7 @@ subtaskCommand.command("list <parent-task-id>").description("List all subtasks o
930
1411
  process.exit(1);
931
1412
  }
932
1413
  const subtasks = listSubtasks(parentTaskId);
933
- if (isJsonMode()) {
1414
+ if (isToonMode()) {
934
1415
  output(subtasks);
935
1416
  } else {
936
1417
  if (subtasks.length === 0) {
@@ -967,7 +1448,7 @@ subtaskCommand.command("update <subtask-id>").description("Update a subtask").op
967
1448
  updateInput.status = parseStatus(options.status, "task");
968
1449
  }
969
1450
  const updated = updateTask(subtaskId, updateInput);
970
- if (isJsonMode()) {
1451
+ if (isToonMode()) {
971
1452
  output(updated);
972
1453
  } else {
973
1454
  success(`Subtask updated: ${updated.id}`);
@@ -1066,7 +1547,7 @@ commentCommand.command("add <task-id>").description("Add a comment to a task").r
1066
1547
  author: options.author,
1067
1548
  content: options.content
1068
1549
  });
1069
- if (isJsonMode()) {
1550
+ if (isToonMode()) {
1070
1551
  output(comment);
1071
1552
  } else {
1072
1553
  success(`Comment added: ${comment.id}`);
@@ -1080,7 +1561,7 @@ commentCommand.command("add <task-id>").description("Add a comment to a task").r
1080
1561
  commentCommand.command("list <task-id>").description("List all comments on a task").action((taskId) => {
1081
1562
  try {
1082
1563
  const comments2 = listComments(taskId);
1083
- if (isJsonMode()) {
1564
+ if (isToonMode()) {
1084
1565
  output(comments2);
1085
1566
  } else {
1086
1567
  if (comments2.length === 0) {
@@ -1101,7 +1582,7 @@ commentCommand.command("update <comment-id>").description("Update a comment").re
1101
1582
  const comment = updateComment(commentId, {
1102
1583
  content: options.content
1103
1584
  });
1104
- if (isJsonMode()) {
1585
+ if (isToonMode()) {
1105
1586
  output(comment);
1106
1587
  } else {
1107
1588
  success(`Comment updated: ${comment.id}`);
@@ -1206,7 +1687,7 @@ var depCommand = new Command7("dep").description("Manage task dependencies");
1206
1687
  depCommand.command("add <task-id> <depends-on-id>").description("Add a dependency (task-id depends on depends-on-id)").action((taskId, dependsOnId) => {
1207
1688
  try {
1208
1689
  const dependency = addDependency(taskId, dependsOnId);
1209
- if (isJsonMode()) {
1690
+ if (isToonMode()) {
1210
1691
  output(dependency);
1211
1692
  } else {
1212
1693
  success(`Dependency added: ${taskId} \u2192 depends on ${dependsOnId}`);
@@ -1228,7 +1709,7 @@ depCommand.command("remove <task-id> <depends-on-id>").description("Remove a dep
1228
1709
  depCommand.command("list <task-id>").description("List dependencies for a task").action((taskId) => {
1229
1710
  try {
1230
1711
  const { dependsOn, blocks } = getDependencies(taskId);
1231
- if (isJsonMode()) {
1712
+ if (isToonMode()) {
1232
1713
  output({ taskId, dependsOn, blocks });
1233
1714
  } else {
1234
1715
  console.log(`Dependencies for ${taskId}:`);
@@ -1247,612 +1728,76 @@ Blocks:`);
1247
1728
 
1248
1729
  // src/commands/quickstart.ts
1249
1730
  import { Command as Command8 } from "commander";
1250
- var QUICKSTART_TEXT = `# Trekker - AI Agent Quickstart Guide
1251
-
1252
- Trekker is a CLI-based issue tracker designed for coding agents. It stores all data in a local SQLite database at \`.trekker/trekker.db\` in your project root.
1253
-
1254
- ## Why Use Trekker?
1255
-
1256
- As an AI agent, you should use Trekker to:
1257
- - **Track your work** across multiple sessions and context windows
1258
- - **Break down complex tasks** into manageable, atomic pieces
1259
- - **Document decisions** and progress for future reference
1260
- - **Manage dependencies** between tasks to ensure correct execution order
1261
- - **Offload context** - store your reasoning, findings, and state externally so you can recover after context resets
1262
- - **Persist memory** - comments survive context window limits and session boundaries
1731
+ var QUICKSTART_TEXT = `# Trekker Quickstart
1263
1732
 
1264
- ## Best Practices for AI Agents
1733
+ Issue tracker for AI agents. Data stored in \`.trekker/trekker.db\`.
1265
1734
 
1266
- ### Creating Epics
1267
- - Create one epic per **feature or major goal**
1268
- - Use clear, descriptive titles that explain the outcome (e.g., "User Authentication System" not "Auth")
1269
- - Write descriptions that capture the **why** and **scope** of the work
1270
- - Example: \`trekker epic create -t "REST API for User Management" -d "Build CRUD endpoints for users with JWT auth, input validation, and rate limiting"\`
1735
+ ## Setup
1736
+ trekker init # Initialize
1737
+ trekker wipe -y # Remove all data
1271
1738
 
1272
- ### Creating Atomic Tasks
1273
- - Each task should be **completable in one focused session**
1274
- - Tasks should have a **single, clear objective**
1275
- - If a task feels too big, break it into subtasks
1276
- - Use action verbs: "Implement", "Add", "Fix", "Refactor", "Update"
1277
- - Bad: \`"Work on authentication"\`
1278
- - Good: \`"Implement JWT token generation endpoint"\`
1739
+ ## Core Rules
1740
+ 1. Set status to \`in_progress\` when starting, \`completed\` when done
1741
+ 2. Add summary comment before marking task complete
1742
+ 3. Use \`--toon\` flag for token-efficient output
1279
1743
 
1280
- ### Writing Good Descriptions
1281
- - Explain **what** needs to be done and **how** to verify it's complete
1282
- - Include relevant technical details (endpoints, file paths, function names)
1283
- - Mention acceptance criteria when applicable
1284
- - Example: \`-d "Create POST /api/auth/login that accepts {email, password}, validates credentials against DB, returns JWT token. Should return 401 for invalid credentials."\`
1744
+ ## Commands
1285
1745
 
1286
- ### Using Tags Effectively
1287
- - Use tags to categorize work: \`--tags "api,auth,security"\`
1288
- - Common tag categories: component (\`api\`, \`ui\`, \`db\`), type (\`bug\`, \`feature\`, \`refactor\`), area (\`auth\`, \`payments\`)
1289
-
1290
- ### Managing Dependencies
1291
- - Always define dependencies when task order matters
1292
- - A task should not start until its dependencies are complete
1293
- - Use \`trekker dep list <task-id>\` to check what's blocking a task
1294
-
1295
- ### Adding Comments (Critical for Context Management)
1296
-
1297
- Comments are your **external memory**. Use them extensively to:
1298
-
1299
- **Save Your Thought Process:**
1300
- - Document your reasoning and analysis as you work
1301
- - Record hypotheses before investigating them
1302
- - Note alternatives you considered and why you chose/rejected them
1303
-
1304
- **Offload Context:**
1305
- - When your context window is filling up, dump your current state into comments
1306
- - Record what you've tried, what worked, what didn't
1307
- - Save partial progress so you can resume after a context reset
1308
-
1309
- **Preserve Investigation Results:**
1310
- - Store findings from code exploration
1311
- - Document file locations and relevant code snippets
1312
- - Record error messages and stack traces you're debugging
1313
-
1314
- **Track Decision History:**
1315
- - Log architectural decisions with rationale
1316
- - Document trade-offs you evaluated
1317
- - Record blockers and how you resolved them
1318
-
1319
- **Examples:**
1320
- \`\`\`bash
1321
- # Starting a task - record your initial analysis
1322
- trekker comment add TREK-1 -a "agent" -c "Initial analysis: Need to modify auth.ts (line 45-80) and add new endpoint in routes/api.ts. Dependencies: bcrypt, jsonwebtoken already installed."
1323
-
1324
- # During investigation - save what you found
1325
- trekker comment add TREK-1 -a "agent" -c "Found existing validation in utils/validate.ts:23. Can reuse validateEmail() and validatePassword(). Token generation should follow pattern in auth/jwt.ts."
1326
-
1327
- # Recording a decision
1328
- trekker comment add TREK-1 -a "agent" -c "Decision: Using bcrypt over argon2 for password hashing. Rationale: better library support, existing team familiarity, sufficient security for this use case."
1329
-
1330
- # Saving progress before context reset
1331
- trekker comment add TREK-1 -a "agent" -c "Progress checkpoint: Implemented login endpoint (auth.ts:45-120). TODO: Add rate limiting, write tests. Blocked by: Need to clarify password reset flow with user."
1332
-
1333
- # After hitting an issue
1334
- trekker comment add TREK-1 -a "agent" -c "Issue: JWT verification failing. Tried: 1) Checked secret key - correct, 2) Verified token format - valid, 3) Found issue - clock skew on server. Solution: Added 30s leeway to verification."
1335
- \`\`\`
1336
-
1337
- ---
1338
-
1339
- ## Initialization
1340
-
1341
- Before using Trekker, initialize it in your project directory:
1342
-
1343
- \`\`\`bash
1344
- trekker init # Creates .trekker/ directory with database
1345
- \`\`\`
1346
-
1347
- To remove all Trekker data:
1348
-
1349
- \`\`\`bash
1350
- trekker wipe # Prompts for confirmation
1351
- trekker wipe -y # Skip confirmation
1352
- \`\`\`
1353
-
1354
- ## Global Options
1355
-
1356
- | Option | Description |
1357
- |-----------|--------------------------------------------|
1358
- | \`--json\` | Output in JSON format (recommended for agents) |
1359
- | \`--help\` | Show help for any command |
1360
-
1361
- **Example:**
1362
- \`\`\`bash
1363
- trekker --json task list # Returns tasks as JSON array
1364
- \`\`\`
1365
-
1366
- ---
1367
-
1368
- ## Epics
1369
-
1370
- > High-level features or milestones
1371
-
1372
- ### Create Epic
1373
-
1374
- \`\`\`bash
1375
- trekker epic create -t <title> [-d <description>] [-p <0-5>] [-s <status>]
1376
- \`\`\`
1377
-
1378
- | Option | Required | Description |
1379
- |--------|----------|-------------|
1380
- | \`-t, --title\` | Yes | Epic title |
1381
- | \`-d, --description\` | No | Detailed description |
1382
- | \`-p, --priority\` | No | 0=critical, 1=high, 2=medium (default), 3=low, 4=backlog, 5=someday |
1383
- | \`-s, --status\` | No | todo (default), in_progress, completed, archived |
1384
-
1385
- **Example:**
1386
- \`\`\`bash
1387
- trekker epic create -t "User Authentication" -d "Implement OAuth2 login" -p 1
1388
- \`\`\`
1389
-
1390
- ### List Epics
1391
-
1392
- \`\`\`bash
1746
+ ### Epics (features/milestones)
1747
+ trekker epic create -t "Title" [-d "desc"] [-p 0-5] [-e EPIC-1]
1393
1748
  trekker epic list [--status <status>]
1394
- \`\`\`
1395
-
1396
- ### Show Epic
1397
-
1398
- \`\`\`bash
1399
- trekker epic show <epic-id>
1400
- \`\`\`
1401
-
1402
- ### Update Epic
1403
-
1404
- \`\`\`bash
1405
- trekker epic update <epic-id> [-t <title>] [-d <desc>] [-p <priority>] [-s <status>]
1406
- \`\`\`
1407
-
1408
- ### Delete Epic
1409
-
1410
- \`\`\`bash
1411
- trekker epic delete <epic-id>
1412
- \`\`\`
1413
-
1414
- ---
1415
-
1416
- ## Tasks
1417
-
1418
- > Work items that can be assigned to epics
1419
-
1420
- ### Create Task
1421
-
1422
- \`\`\`bash
1423
- trekker task create -t <title> [-d <description>] [-p <0-5>] [-s <status>] [--tags <tags>] [-e <epic-id>]
1424
- \`\`\`
1425
-
1426
- | Option | Required | Description |
1427
- |--------|----------|-------------|
1428
- | \`-t, --title\` | Yes | Task title |
1429
- | \`-d, --description\` | No | Detailed description |
1430
- | \`-p, --priority\` | No | 0-5, default: 2 |
1431
- | \`-s, --status\` | No | todo (default), in_progress, completed, wont_fix, archived |
1432
- | \`--tags\` | No | Comma-separated tags, e.g., "api,auth,backend" |
1433
- | \`-e, --epic\` | No | Epic ID to assign task to |
1434
-
1435
- **Example:**
1436
- \`\`\`bash
1437
- trekker task create -t "Implement login API" -d "POST /api/login endpoint" -e EPIC-1 --tags "api,auth"
1438
- \`\`\`
1439
-
1440
- ### List Tasks
1441
-
1442
- \`\`\`bash
1443
- trekker task list [--status <status>] [--epic <epic-id>]
1444
- \`\`\`
1445
-
1446
- **Examples:**
1447
- \`\`\`bash
1448
- trekker task list # All top-level tasks
1449
- trekker task list --status todo # Filter by status
1450
- trekker task list --epic EPIC-1 # Filter by epic
1451
- \`\`\`
1452
-
1453
- ### Show Task
1454
-
1455
- \`\`\`bash
1456
- trekker task show <task-id>
1457
- trekker --json task show TREK-1 # Get full task details as JSON
1458
- \`\`\`
1459
-
1460
- ### Update Task
1461
-
1462
- \`\`\`bash
1463
- trekker task update <task-id> [-t <title>] [-d <desc>] [-p <priority>] [-s <status>] [--tags <tags>] [-e <epic-id>] [--no-epic]
1464
- \`\`\`
1465
-
1466
- **Examples:**
1467
- \`\`\`bash
1749
+ trekker epic show EPIC-1
1750
+ trekker epic update EPIC-1 [-t "Title"] [-d "desc"] [-p 0-5] [-s <status>]
1751
+ trekker epic delete EPIC-1
1752
+
1753
+ ### Tasks
1754
+ trekker task create -t "Title" [-d "desc"] [-p 0-5] [-e EPIC-1] [--tags "a,b"]
1755
+ trekker task list [--status <status>] [--epic EPIC-1]
1756
+ trekker task show TREK-1
1757
+ trekker task update TREK-1 [-t "Title"] [-d "desc"] [-p 0-5] [-s <status>] [--tags "a,b"] [-e EPIC-1] [--no-epic]
1758
+ trekker task delete TREK-1
1759
+
1760
+ ### Subtasks
1761
+ trekker subtask create TREK-1 -t "Title" [-d "desc"] [-p 0-5]
1762
+ trekker subtask list TREK-1
1763
+ trekker subtask update TREK-2 [-t "Title"] [-d "desc"] [-p 0-5] [-s <status>]
1764
+ trekker subtask delete TREK-2
1765
+
1766
+ ### Comments (external memory)
1767
+ trekker comment add TREK-1 -a "agent" -c "content"
1768
+ trekker comment list TREK-1
1769
+ trekker comment update CMT-1 -c "new content"
1770
+ trekker comment delete CMT-1
1771
+
1772
+ ### Dependencies
1773
+ trekker dep add TREK-2 TREK-1 # TREK-2 depends on TREK-1
1774
+ trekker dep remove TREK-2 TREK-1
1775
+ trekker dep list TREK-1
1776
+
1777
+ ## Status Values
1778
+ Tasks: todo, in_progress, completed, wont_fix, archived
1779
+ Epics: todo, in_progress, completed, archived
1780
+
1781
+ ## Priority Scale
1782
+ 0=critical, 1=high, 2=medium (default), 3=low, 4=backlog, 5=someday
1783
+
1784
+ ## Workflow
1785
+
1786
+ ### Session Start
1787
+ trekker --toon task list --status in_progress
1788
+ trekker --toon comment list TREK-1
1789
+
1790
+ ### Working
1468
1791
  trekker task update TREK-1 -s in_progress
1469
- trekker task update TREK-1 --no-epic # Remove from epic
1470
- \`\`\`
1471
-
1472
- ### Delete Task
1473
-
1474
- \`\`\`bash
1475
- trekker task delete <task-id>
1476
- \`\`\`
1477
-
1478
- ---
1479
-
1480
- ## Subtasks
1481
-
1482
- > Child tasks that belong to a parent task. They inherit the epic from their parent.
1483
-
1484
- ### Create Subtask
1485
-
1486
- \`\`\`bash
1487
- trekker subtask create <parent-task-id> -t <title> [-d <description>] [-p <0-5>] [-s <status>]
1488
- \`\`\`
1489
-
1490
- **Example:**
1491
- \`\`\`bash
1492
- trekker subtask create TREK-1 -t "Add input validation"
1493
- \`\`\`
1494
-
1495
- ### List Subtasks
1496
-
1497
- \`\`\`bash
1498
- trekker subtask list <parent-task-id>
1499
- \`\`\`
1500
-
1501
- ### Update Subtask
1502
-
1503
- \`\`\`bash
1504
- trekker subtask update <subtask-id> [-t <title>] [-d <desc>] [-p <priority>] [-s <status>]
1505
- \`\`\`
1506
-
1507
- ### Delete Subtask
1508
-
1509
- \`\`\`bash
1510
- trekker subtask delete <subtask-id>
1511
- \`\`\`
1512
-
1513
- ---
1514
-
1515
- ## Comments
1516
-
1517
- > Your external memory - use extensively to preserve context and reasoning
1518
-
1519
- Comments are **critical for AI agents**. They persist beyond your context window and session boundaries. Use them to:
1520
- - Store your analysis and reasoning
1521
- - Save investigation results
1522
- - Record decisions with rationale
1523
- - Checkpoint progress before context resets
1524
- - Document blockers and solutions
1525
-
1526
- ### Add Comment
1527
-
1528
- \`\`\`bash
1529
- trekker comment add <task-id> -a <author> -c <content>
1530
- \`\`\`
1531
-
1532
- | Option | Required | Description |
1533
- |--------|----------|-------------|
1534
- | \`-a, --author\` | Yes | Comment author name (use "agent" for AI) |
1535
- | \`-c, --content\` | Yes | Comment text (can be multi-line) |
1536
-
1537
- **Examples:**
1538
- \`\`\`bash
1539
- # Record analysis
1540
- trekker comment add TREK-1 -a "agent" -c "Analyzed codebase: auth logic in src/auth/, uses JWT stored in httpOnly cookies"
1541
-
1542
- # Save progress checkpoint
1543
- trekker comment add TREK-1 -a "agent" -c "Checkpoint: Completed steps 1-3. Next: implement validation. Files modified: auth.ts, routes.ts"
1544
-
1545
- # Document a blocker
1546
- trekker comment add TREK-1 -a "agent" -c "BLOCKED: Need clarification on password requirements. Asked user, waiting for response."
1547
- \`\`\`
1548
-
1549
- ### List Comments
1550
-
1551
- \`\`\`bash
1552
- trekker comment list <task-id>
1553
- trekker --json comment list <task-id> # Get as JSON for parsing
1554
- \`\`\`
1555
-
1556
- **Pro tip:** Always read comments when resuming work on a task - they contain your previous context!
1557
-
1558
- ### Update Comment
1559
-
1560
- \`\`\`bash
1561
- trekker comment update <comment-id> -c <new-content>
1562
- \`\`\`
1563
-
1564
- ### Delete Comment
1565
-
1566
- \`\`\`bash
1567
- trekker comment delete <comment-id>
1568
- \`\`\`
1569
-
1570
- ---
1571
-
1572
- ## Dependencies
1573
-
1574
- > Task relationships - which tasks must be completed before others can start.
1575
- > Trekker automatically detects and prevents circular dependencies.
1576
-
1577
- ### Add Dependency
1578
-
1579
- \`\`\`bash
1580
- trekker dep add <task-id> <depends-on-id>
1581
- \`\`\`
1582
-
1583
- This means:
1584
- - \`<task-id>\` depends on \`<depends-on-id>\`
1585
- - \`<task-id>\` cannot start until \`<depends-on-id>\` is done
1586
-
1587
- **Example:**
1588
- \`\`\`bash
1589
- trekker dep add TREK-2 TREK-1 # TREK-2 depends on TREK-1
1590
- \`\`\`
1591
-
1592
- ### Remove Dependency
1593
-
1594
- \`\`\`bash
1595
- trekker dep remove <task-id> <depends-on-id>
1596
- \`\`\`
1597
-
1598
- ### List Dependencies
1599
-
1600
- \`\`\`bash
1601
- trekker dep list <task-id>
1602
- \`\`\`
1603
-
1604
- Shows both:
1605
- - What this task depends on (blockers)
1606
- - What tasks this task blocks
1607
-
1608
- ---
1609
-
1610
- ## Web Interface
1611
-
1612
- Trekker includes a web interface for visual task management.
1613
-
1614
- ### Start the Web Interface
1615
-
1616
- \`\`\`bash
1617
- trekker serve # Start on port 3000
1618
- trekker serve -p 8080 # Start on custom port
1619
- \`\`\`
1620
-
1621
- The web interface provides:
1622
- - Kanban board with tasks grouped by status (TODO, In Progress, Completed)
1623
- - Epic filter to focus on specific features
1624
- - Task details including dependencies, subtasks, and tags
1625
- - Auto-refresh every 5 seconds to reflect CLI changes
1626
-
1627
- ---
1628
-
1629
- ## Reference
1630
-
1631
- ### ID Formats
1632
-
1633
- | Type | Format | Example |
1634
- |------|--------|---------|
1635
- | Epic | \`EPIC-n\` | EPIC-1, EPIC-2 |
1636
- | Task/Subtask | \`TREK-n\` | TREK-1, TREK-42 |
1637
- | Comment | \`CMT-n\` | CMT-1, CMT-5 |
1638
-
1639
- IDs are auto-generated and sequential within each type.
1640
-
1641
- ### Task Status Values
1642
-
1643
- | Status | Description |
1644
- |--------|-------------|
1645
- | \`todo\` | Not started (default) |
1646
- | \`in_progress\` | Currently being worked on |
1647
- | \`completed\` | Finished successfully |
1648
- | \`wont_fix\` | Decided not to implement |
1649
- | \`archived\` | No longer relevant |
1650
-
1651
- ### Epic Status Values
1652
-
1653
- | Status | Description |
1654
- |--------|-------------|
1655
- | \`todo\` | Not started (default) |
1656
- | \`in_progress\` | Work has begun |
1657
- | \`completed\` | All tasks done |
1658
- | \`archived\` | No longer relevant |
1659
-
1660
- ### Priority Scale
1661
-
1662
- | Value | Meaning |
1663
- |-------|---------|
1664
- | 0 | Critical (drop everything) |
1665
- | 1 | High (do soon) |
1666
- | 2 | Medium (default) |
1667
- | 3 | Low (when time permits) |
1668
- | 4 | Backlog (future consideration) |
1669
- | 5 | Someday (nice to have) |
1670
-
1671
- ---
1672
-
1673
- ## Recommended Workflow for AI Agents
1674
-
1675
- 1. **Initialize** (once per project):
1676
- \`\`\`bash
1677
- trekker init
1678
- \`\`\`
1679
-
1680
- 2. **Check existing state** (every session start):
1681
- \`\`\`bash
1682
- trekker --json task list --status in_progress # See what's active
1683
- trekker --json comment list TREK-1 # Read your previous context
1684
- \`\`\`
1685
-
1686
- 3. **Create epic** for the feature you're working on:
1687
- \`\`\`bash
1688
- trekker epic create -t "Feature Name" -d "Description"
1689
- \`\`\`
1690
-
1691
- 4. **Create tasks** for each piece of work:
1692
- \`\`\`bash
1693
- trekker task create -t "Task name" -e EPIC-1
1694
- \`\`\`
1695
-
1696
- 5. **Add dependencies** if tasks have ordering requirements:
1697
- \`\`\`bash
1698
- trekker dep add TREK-2 TREK-1
1699
- \`\`\`
1700
-
1701
- 6. **Document your initial analysis** before starting work:
1702
- \`\`\`bash
1703
- trekker comment add TREK-1 -a "agent" -c "Analysis: Need to modify X, Y, Z. Approach: ..."
1704
- \`\`\`
1705
-
1706
- 7. **Update status** as you work:
1707
- \`\`\`bash
1708
- trekker task update TREK-1 -s in_progress
1709
- \`\`\`
1710
-
1711
- 8. **Add comments frequently** - this is your external memory:
1712
- \`\`\`bash
1713
- # After investigating
1714
- trekker comment add TREK-1 -a "agent" -c "Found: auth logic in src/auth.ts:45-80"
1715
-
1716
- # After making a decision
1717
- trekker comment add TREK-1 -a "agent" -c "Decision: Using approach X because Y"
1718
-
1719
- # Before context might reset
1720
- trekker comment add TREK-1 -a "agent" -c "Checkpoint: Completed A, B. Next: C, D"
1721
- \`\`\`
1722
-
1723
- 9. **Mark complete** when done:
1724
- \`\`\`bash
1725
- trekker task update TREK-1 -s completed
1726
- trekker comment add TREK-1 -a "agent" -c "Completed. Summary: Implemented X in files A, B, C."
1727
- \`\`\`
1728
-
1729
- 10. **Use JSON output** for parsing:
1730
- \`\`\`bash
1731
- trekker --json task list
1732
- trekker --json task show TREK-1
1733
- \`\`\`
1734
-
1735
- ---
1736
-
1737
- ## Context Management Strategies
1738
-
1739
- AI agents have limited context windows. Use Trekker to extend your effective memory:
1740
-
1741
- ### Starting a New Session
1742
-
1743
- Always begin by reading your previous state:
1744
- \`\`\`bash
1745
- # What was I working on?
1746
- trekker --json task list --status in_progress
1747
-
1748
- # What did I learn/decide?
1749
- trekker --json comment list TREK-1
1750
- \`\`\`
1751
-
1752
- ### During Long Tasks
1753
-
1754
- Periodically checkpoint your progress:
1755
- \`\`\`bash
1756
- trekker comment add TREK-1 -a "agent" -c "Progress: Steps 1-3 done. Current state: X. Next: Y. Blockers: Z."
1757
- \`\`\`
1758
-
1759
- ### Before Context Window Fills Up
1760
-
1761
- When you notice context getting large, dump your current mental state:
1762
- \`\`\`bash
1763
- trekker comment add TREK-1 -a "agent" -c "Context dump:
1764
- - Working on: implementing login validation
1765
- - Files involved: auth.ts (modified lines 45-80), routes.ts (new endpoint at line 120)
1766
- - Current approach: using existing validateEmail() from utils
1767
- - Remaining work: add rate limiting, write tests
1768
- - Open questions: unclear if we need refresh tokens"
1769
- \`\`\`
1770
-
1771
- ### After Solving a Problem
1772
-
1773
- Document the solution so future-you doesn't repeat the investigation:
1774
- \`\`\`bash
1775
- trekker comment add TREK-1 -a "agent" -c "Solved: JWT verification failing. Root cause: clock skew. Fix: added 30s leeway in verify(). See auth.ts:67."
1776
- \`\`\`
1777
-
1778
- ### When Blocked
1779
-
1780
- Record what you're waiting for:
1781
- \`\`\`bash
1782
- trekker comment add TREK-1 -a "agent" -c "BLOCKED: Need user input on password complexity requirements. Asked in conversation. Current assumption: min 8 chars, 1 number."
1783
- \`\`\`
1784
-
1785
- ---
1786
-
1787
- ## Common Scenarios
1788
-
1789
- ### Start a new feature
1790
-
1791
- \`\`\`bash
1792
- trekker epic create -t "API Rate Limiting" -d "Implement rate limiting for all endpoints" -p 1
1793
- trekker task create -t "Design rate limit algorithm" -e EPIC-1
1794
- trekker task create -t "Implement Redis counter" -e EPIC-1
1795
- trekker task create -t "Add middleware" -e EPIC-1
1796
- trekker dep add TREK-3 TREK-2 # Middleware depends on Redis counter
1797
- trekker dep add TREK-2 TREK-1 # Redis counter depends on design
1798
- \`\`\`
1799
-
1800
- ### Check what's ready to work on
1801
-
1802
- \`\`\`bash
1803
- trekker --json task list --status todo
1804
- \`\`\`
1805
-
1806
- ### Get all details about a task
1807
-
1808
- \`\`\`bash
1809
- trekker --json task show TREK-1
1810
- \`\`\`
1811
-
1812
- ### Log progress
1813
-
1814
- \`\`\`bash
1815
- trekker task update TREK-1 -s in_progress
1816
- trekker comment add TREK-1 -a "agent" -c "Chose token bucket algorithm"
1792
+ trekker comment add TREK-1 -a "agent" -c "Analysis: ..."
1793
+ # ... do work ...
1794
+ trekker comment add TREK-1 -a "agent" -c "Summary: implemented X in files A, B"
1817
1795
  trekker task update TREK-1 -s completed
1818
- \`\`\`
1819
-
1820
- ### Break down a complex task
1821
-
1822
- \`\`\`bash
1823
- trekker subtask create TREK-2 -t "Set up Redis connection"
1824
- trekker subtask create TREK-2 -t "Implement increment logic"
1825
- trekker subtask create TREK-2 -t "Add TTL handling"
1826
- \`\`\`
1827
-
1828
- ### Resume work after context reset
1829
-
1830
- \`\`\`bash
1831
- # 1. Find what you were working on
1832
- trekker --json task list --status in_progress
1833
-
1834
- # 2. Read your previous comments to restore context
1835
- trekker --json comment list TREK-1
1836
-
1837
- # 3. Read task details for full picture
1838
- trekker --json task show TREK-1
1839
-
1840
- # 4. Continue work with restored context
1841
- \`\`\`
1842
-
1843
- ### Save context before stopping
1844
1796
 
1845
- \`\`\`bash
1846
- # Dump everything you know before session ends
1847
- trekker comment add TREK-1 -a "agent" -c "Session end checkpoint:
1848
- - Completed: login endpoint, validation
1849
- - In progress: rate limiting (50% done, see middleware.ts:30)
1850
- - Files modified: auth.ts, routes.ts, middleware.ts
1851
- - Next steps: finish rate limiter, add tests
1852
- - Notes: user prefers 100 req/min limit"
1853
- \`\`\`
1797
+ ### Before Context Reset
1798
+ trekker comment add TREK-1 -a "agent" -c "Checkpoint: done A,B. Next: C. Files: x.ts, y.ts"
1854
1799
  `;
1855
- var quickstartCommand = new Command8("quickstart").description("Show comprehensive guide for AI agents").action(() => {
1800
+ var quickstartCommand = new Command8("quickstart").description("Show quick reference for AI agents").action(() => {
1856
1801
  console.log(QUICKSTART_TEXT);
1857
1802
  });
1858
1803
 
@@ -2128,7 +2073,7 @@ Seed complete! Created ${epicIds.length} epics, ${taskIds.length} tasks, ${SAMPL
2128
2073
  // package.json
2129
2074
  var package_default = {
2130
2075
  name: "@obsfx/trekker",
2131
- version: "1.0.0",
2076
+ version: "1.2.0",
2132
2077
  description: "A CLI-based issue tracker built for AI coding agents. Stores tasks, epics, and dependencies in a local SQLite database.",
2133
2078
  type: "module",
2134
2079
  main: "dist/index.js",
@@ -2175,6 +2120,7 @@ var package_default = {
2175
2120
  bun: ">=1.0.0"
2176
2121
  },
2177
2122
  dependencies: {
2123
+ "@toon-format/toon": "^2.1.0",
2178
2124
  commander: "^13.1.0",
2179
2125
  "drizzle-orm": "^0.38.4"
2180
2126
  },
@@ -2187,10 +2133,10 @@ var package_default = {
2187
2133
 
2188
2134
  // src/index.ts
2189
2135
  var program = new Command10;
2190
- program.name("trekker").description("CLI-based issue tracker for coding agents").version(package_default.version).option("--json", "Output in JSON format").hook("preAction", (thisCommand) => {
2136
+ program.name("trekker").description("CLI-based issue tracker for coding agents").version(package_default.version).option("--toon", "Output in TOON format").hook("preAction", (thisCommand) => {
2191
2137
  const opts = thisCommand.opts();
2192
- if (opts.json) {
2193
- setJsonMode(true);
2138
+ if (opts.toon) {
2139
+ setToonMode(true);
2194
2140
  }
2195
2141
  });
2196
2142
  program.addCommand(initCommand);