@obsfx/trekker 1.1.0 → 1.2.2

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 +21 -6
  2. package/dist/index.js +574 -745
  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/@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,729 +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
1263
-
1264
- ---
1265
-
1266
- ## Critical Rules for AI Agents
1267
-
1268
- These rules are **non-negotiable**. Following them ensures continuity across sessions and prevents lost context.
1269
-
1270
- ### 1. ALWAYS Update Task Status
1271
-
1272
- **Never leave a task in the wrong status.** Status reflects reality and helps you (and future sessions) understand what's happening.
1273
-
1274
- - Set to \`in_progress\` **immediately** when you start working on a task
1275
- - Set to \`completed\` **only after** you've verified the work is done
1276
- - Set to \`wont_fix\` or \`archived\` if the task is no longer relevant
1277
- - **Before ending any session**: ensure all task statuses are accurate
1278
-
1279
- \`\`\`bash
1280
- # Starting work
1281
- trekker task update TREK-1 -s in_progress
1282
-
1283
- # Finishing work
1284
- trekker task update TREK-1 -s completed
1285
- \`\`\`
1286
-
1287
- **Why this matters:** If you forget to update status, the next session (or another agent) won't know what's actually done. They'll either duplicate work or miss incomplete tasks.
1288
-
1289
- ### 2. ALWAYS Add a Summary Comment Before Moving On
1290
-
1291
- **Before moving to a new task, document what you did on the current one.** This is your handoff to your future self.
1292
-
1293
- Every task completion should include a comment with:
1294
- - **What was implemented** (files changed, functions added)
1295
- - **Key decisions made** and why
1296
- - **Any gotchas or important notes** for future reference
1297
- - **Testing done** or verification steps taken
1298
-
1299
- \`\`\`bash
1300
- # Before marking complete, add a summary
1301
- trekker comment add TREK-1 -a "agent" -c "Completed: Implemented JWT auth endpoint.
1302
- - Added POST /api/auth/login in routes/auth.ts (lines 45-80)
1303
- - Created validateCredentials() in services/auth.ts
1304
- - Chose bcrypt for hashing (better library support than argon2)
1305
- - Tested: valid login returns token, invalid returns 401
1306
- - Note: Token expires in 24h, configurable via JWT_EXPIRY env var"
1307
-
1308
- # Then mark complete
1309
- trekker task update TREK-1 -s completed
1310
- \`\`\`
1311
-
1312
- **Why this matters:** Comments are your external memory. Without a summary, you lose all the context and reasoning from your work session.
1313
-
1314
- ### 3. Use Epic Descriptions for Implementation Plans
1315
-
1316
- **Epic descriptions are your design documents.** Don't put brief one-liners\u2014use them to capture the full implementation plan.
1317
-
1318
- An epic description should include:
1319
- - **Goal and scope** - what problem are we solving?
1320
- - **Architecture overview** - how will components fit together?
1321
- - **Implementation phases** - what order should tasks follow?
1322
- - **Technical decisions** - chosen approaches and rationale
1323
- - **API contracts** - endpoints, request/response formats
1324
- - **Data models** - schema changes, new entities
1325
- - **Edge cases and constraints** - what to watch out for
1326
-
1327
- \`\`\`bash
1328
- trekker epic create -t "User Authentication System" -d "## Goal
1329
- Implement secure user authentication with JWT tokens.
1330
-
1331
- ## Architecture
1332
- - Auth service handles token generation/validation
1333
- - Middleware intercepts requests and verifies tokens
1334
- - Redis stores refresh tokens for revocation support
1335
-
1336
- ## Implementation Phases
1337
- 1. Basic JWT auth (login, token generation)
1338
- 2. Refresh token mechanism
1339
- 3. Password reset flow
1340
- 4. Rate limiting on auth endpoints
1341
-
1342
- ## API Endpoints
1343
- - POST /api/auth/login - {email, password} -> {accessToken, refreshToken}
1344
- - POST /api/auth/refresh - {refreshToken} -> {accessToken}
1345
- - POST /api/auth/logout - invalidates refresh token
1346
- - POST /api/auth/forgot-password - sends reset email
1347
-
1348
- ## Data Model
1349
- - users table: id, email, passwordHash, createdAt, updatedAt
1350
- - refresh_tokens table: id, userId, token, expiresAt, revokedAt
1351
-
1352
- ## Security Considerations
1353
- - Passwords hashed with bcrypt (cost factor 12)
1354
- - Access tokens expire in 15 minutes
1355
- - Refresh tokens expire in 7 days
1356
- - Rate limit: 5 login attempts per minute per IP"
1357
- \`\`\`
1358
-
1359
- **Why this matters:** When you start a new session, the epic description tells you the full plan. You won't have to re-derive the architecture or remember why certain decisions were made.
1360
-
1361
- ### 4. Archive Completed Work
1362
-
1363
- **When a feature is fully done, move everything to \`archived\` status.** This keeps the active task list clean and signals that work is truly finished.
1364
-
1365
- - Archive tasks after they've been completed and verified
1366
- - Archive the epic once all its tasks are done
1367
- - Archived items stay in the database for reference but don't clutter active views
1368
-
1369
- \`\`\`bash
1370
- # After all tasks in an epic are completed
1371
- trekker task update TREK-1 -s archived
1372
- trekker task update TREK-2 -s archived
1373
- trekker task update TREK-3 -s archived
1374
- trekker epic update EPIC-1 -s archived
1375
- \`\`\`
1376
-
1377
- **Why this matters:** A clean task list helps you focus on what's actually in progress. Archived items preserve history without noise.
1378
-
1379
- ---
1380
-
1381
- ## Best Practices for AI Agents
1382
-
1383
- ### Creating Epics
1384
- - Create one epic per **feature or major goal**
1385
- - Use clear, descriptive titles that explain the outcome (e.g., "User Authentication System" not "Auth")
1386
- - Write descriptions that capture the **why** and **scope** of the work
1387
- - Example: \`trekker epic create -t "REST API for User Management" -d "Build CRUD endpoints for users with JWT auth, input validation, and rate limiting"\`
1388
-
1389
- ### Creating Atomic Tasks
1390
- - Each task should be **completable in one focused session**
1391
- - Tasks should have a **single, clear objective**
1392
- - If a task feels too big, break it into subtasks
1393
- - Use action verbs: "Implement", "Add", "Fix", "Refactor", "Update"
1394
- - Bad: \`"Work on authentication"\`
1395
- - Good: \`"Implement JWT token generation endpoint"\`
1396
-
1397
- ### Writing Good Descriptions
1398
- - Explain **what** needs to be done and **how** to verify it's complete
1399
- - Include relevant technical details (endpoints, file paths, function names)
1400
- - Mention acceptance criteria when applicable
1401
- - Example: \`-d "Create POST /api/auth/login that accepts {email, password}, validates credentials against DB, returns JWT token. Should return 401 for invalid credentials."\`
1402
-
1403
- ### Using Tags Effectively
1404
- - Use tags to categorize work: \`--tags "api,auth,security"\`
1405
- - Common tag categories: component (\`api\`, \`ui\`, \`db\`), type (\`bug\`, \`feature\`, \`refactor\`), area (\`auth\`, \`payments\`)
1406
-
1407
- ### Managing Dependencies
1408
- - Always define dependencies when task order matters
1409
- - A task should not start until its dependencies are complete
1410
- - Use \`trekker dep list <task-id>\` to check what's blocking a task
1411
-
1412
- ### Adding Comments (Critical for Context Management)
1413
-
1414
- Comments are your **external memory**. Use them extensively to:
1415
-
1416
- **Save Your Thought Process:**
1417
- - Document your reasoning and analysis as you work
1418
- - Record hypotheses before investigating them
1419
- - Note alternatives you considered and why you chose/rejected them
1420
-
1421
- **Offload Context:**
1422
- - When your context window is filling up, dump your current state into comments
1423
- - Record what you've tried, what worked, what didn't
1424
- - Save partial progress so you can resume after a context reset
1425
-
1426
- **Preserve Investigation Results:**
1427
- - Store findings from code exploration
1428
- - Document file locations and relevant code snippets
1429
- - Record error messages and stack traces you're debugging
1430
-
1431
- **Track Decision History:**
1432
- - Log architectural decisions with rationale
1433
- - Document trade-offs you evaluated
1434
- - Record blockers and how you resolved them
1435
-
1436
- **Examples:**
1437
- \`\`\`bash
1438
- # Starting a task - record your initial analysis
1439
- 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."
1440
-
1441
- # During investigation - save what you found
1442
- 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."
1731
+ var QUICKSTART_TEXT = `# Trekker Quickstart
1443
1732
 
1444
- # Recording a decision
1445
- 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."
1733
+ Issue tracker for AI agents. Data stored in \`.trekker/trekker.db\`.
1446
1734
 
1447
- # Saving progress before context reset
1448
- 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."
1735
+ ## Setup
1736
+ trekker init # Initialize
1737
+ trekker wipe -y # Remove all data
1449
1738
 
1450
- # After hitting an issue
1451
- 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."
1452
- \`\`\`
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
1453
1743
 
1454
- ---
1744
+ ## Commands
1455
1745
 
1456
- ## Initialization
1457
-
1458
- Before using Trekker, initialize it in your project directory:
1459
-
1460
- \`\`\`bash
1461
- trekker init # Creates .trekker/ directory with database
1462
- \`\`\`
1463
-
1464
- To remove all Trekker data:
1465
-
1466
- \`\`\`bash
1467
- trekker wipe # Prompts for confirmation
1468
- trekker wipe -y # Skip confirmation
1469
- \`\`\`
1470
-
1471
- ## Global Options
1472
-
1473
- | Option | Description |
1474
- |-----------|--------------------------------------------|
1475
- | \`--json\` | Output in JSON format (recommended for agents) |
1476
- | \`--help\` | Show help for any command |
1477
-
1478
- **Example:**
1479
- \`\`\`bash
1480
- trekker --json task list # Returns tasks as JSON array
1481
- \`\`\`
1482
-
1483
- ---
1484
-
1485
- ## Epics
1486
-
1487
- > High-level features or milestones
1488
-
1489
- ### Create Epic
1490
-
1491
- \`\`\`bash
1492
- trekker epic create -t <title> [-d <description>] [-p <0-5>] [-s <status>]
1493
- \`\`\`
1494
-
1495
- | Option | Required | Description |
1496
- |--------|----------|-------------|
1497
- | \`-t, --title\` | Yes | Epic title |
1498
- | \`-d, --description\` | No | Detailed description |
1499
- | \`-p, --priority\` | No | 0=critical, 1=high, 2=medium (default), 3=low, 4=backlog, 5=someday |
1500
- | \`-s, --status\` | No | todo (default), in_progress, completed, archived |
1501
-
1502
- **Example:**
1503
- \`\`\`bash
1504
- trekker epic create -t "User Authentication" -d "Implement OAuth2 login" -p 1
1505
- \`\`\`
1506
-
1507
- ### List Epics
1508
-
1509
- \`\`\`bash
1746
+ ### Epics (features/milestones)
1747
+ trekker epic create -t "Title" [-d "desc"] [-p 0-5] [-e EPIC-1]
1510
1748
  trekker epic list [--status <status>]
1511
- \`\`\`
1512
-
1513
- ### Show Epic
1514
-
1515
- \`\`\`bash
1516
- trekker epic show <epic-id>
1517
- \`\`\`
1518
-
1519
- ### Update Epic
1520
-
1521
- \`\`\`bash
1522
- trekker epic update <epic-id> [-t <title>] [-d <desc>] [-p <priority>] [-s <status>]
1523
- \`\`\`
1524
-
1525
- ### Delete Epic
1526
-
1527
- \`\`\`bash
1528
- trekker epic delete <epic-id>
1529
- \`\`\`
1530
-
1531
- ---
1532
-
1533
- ## Tasks
1534
-
1535
- > Work items that can be assigned to epics
1536
-
1537
- ### Create Task
1538
-
1539
- \`\`\`bash
1540
- trekker task create -t <title> [-d <description>] [-p <0-5>] [-s <status>] [--tags <tags>] [-e <epic-id>]
1541
- \`\`\`
1542
-
1543
- | Option | Required | Description |
1544
- |--------|----------|-------------|
1545
- | \`-t, --title\` | Yes | Task title |
1546
- | \`-d, --description\` | No | Detailed description |
1547
- | \`-p, --priority\` | No | 0-5, default: 2 |
1548
- | \`-s, --status\` | No | todo (default), in_progress, completed, wont_fix, archived |
1549
- | \`--tags\` | No | Comma-separated tags, e.g., "api,auth,backend" |
1550
- | \`-e, --epic\` | No | Epic ID to assign task to |
1551
-
1552
- **Example:**
1553
- \`\`\`bash
1554
- trekker task create -t "Implement login API" -d "POST /api/login endpoint" -e EPIC-1 --tags "api,auth"
1555
- \`\`\`
1556
-
1557
- ### List Tasks
1558
-
1559
- \`\`\`bash
1560
- trekker task list [--status <status>] [--epic <epic-id>]
1561
- \`\`\`
1562
-
1563
- **Examples:**
1564
- \`\`\`bash
1565
- trekker task list # All top-level tasks
1566
- trekker task list --status todo # Filter by status
1567
- trekker task list --epic EPIC-1 # Filter by epic
1568
- \`\`\`
1569
-
1570
- ### Show Task
1571
-
1572
- \`\`\`bash
1573
- trekker task show <task-id>
1574
- trekker --json task show TREK-1 # Get full task details as JSON
1575
- \`\`\`
1576
-
1577
- ### Update Task
1578
-
1579
- \`\`\`bash
1580
- trekker task update <task-id> [-t <title>] [-d <desc>] [-p <priority>] [-s <status>] [--tags <tags>] [-e <epic-id>] [--no-epic]
1581
- \`\`\`
1582
-
1583
- **Examples:**
1584
- \`\`\`bash
1585
- trekker task update TREK-1 -s in_progress
1586
- trekker task update TREK-1 --no-epic # Remove from epic
1587
- \`\`\`
1588
-
1589
- ### Delete Task
1590
-
1591
- \`\`\`bash
1592
- trekker task delete <task-id>
1593
- \`\`\`
1594
-
1595
- ---
1596
-
1597
- ## Subtasks
1598
-
1599
- > Child tasks that belong to a parent task. They inherit the epic from their parent.
1600
-
1601
- ### Create Subtask
1602
-
1603
- \`\`\`bash
1604
- trekker subtask create <parent-task-id> -t <title> [-d <description>] [-p <0-5>] [-s <status>]
1605
- \`\`\`
1606
-
1607
- **Example:**
1608
- \`\`\`bash
1609
- trekker subtask create TREK-1 -t "Add input validation"
1610
- \`\`\`
1611
-
1612
- ### List Subtasks
1613
-
1614
- \`\`\`bash
1615
- trekker subtask list <parent-task-id>
1616
- \`\`\`
1617
-
1618
- ### Update Subtask
1619
-
1620
- \`\`\`bash
1621
- trekker subtask update <subtask-id> [-t <title>] [-d <desc>] [-p <priority>] [-s <status>]
1622
- \`\`\`
1623
-
1624
- ### Delete Subtask
1625
-
1626
- \`\`\`bash
1627
- trekker subtask delete <subtask-id>
1628
- \`\`\`
1629
-
1630
- ---
1631
-
1632
- ## Comments
1633
-
1634
- > Your external memory - use extensively to preserve context and reasoning
1635
-
1636
- Comments are **critical for AI agents**. They persist beyond your context window and session boundaries. Use them to:
1637
- - Store your analysis and reasoning
1638
- - Save investigation results
1639
- - Record decisions with rationale
1640
- - Checkpoint progress before context resets
1641
- - Document blockers and solutions
1642
-
1643
- ### Add Comment
1644
-
1645
- \`\`\`bash
1646
- trekker comment add <task-id> -a <author> -c <content>
1647
- \`\`\`
1648
-
1649
- | Option | Required | Description |
1650
- |--------|----------|-------------|
1651
- | \`-a, --author\` | Yes | Comment author name (use "agent" for AI) |
1652
- | \`-c, --content\` | Yes | Comment text (can be multi-line) |
1653
-
1654
- **Examples:**
1655
- \`\`\`bash
1656
- # Record analysis
1657
- trekker comment add TREK-1 -a "agent" -c "Analyzed codebase: auth logic in src/auth/, uses JWT stored in httpOnly cookies"
1658
-
1659
- # Save progress checkpoint
1660
- trekker comment add TREK-1 -a "agent" -c "Checkpoint: Completed steps 1-3. Next: implement validation. Files modified: auth.ts, routes.ts"
1661
-
1662
- # Document a blocker
1663
- trekker comment add TREK-1 -a "agent" -c "BLOCKED: Need clarification on password requirements. Asked user, waiting for response."
1664
- \`\`\`
1665
-
1666
- ### List Comments
1667
-
1668
- \`\`\`bash
1669
- trekker comment list <task-id>
1670
- trekker --json comment list <task-id> # Get as JSON for parsing
1671
- \`\`\`
1672
-
1673
- **Pro tip:** Always read comments when resuming work on a task - they contain your previous context!
1674
-
1675
- ### Update Comment
1676
-
1677
- \`\`\`bash
1678
- trekker comment update <comment-id> -c <new-content>
1679
- \`\`\`
1680
-
1681
- ### Delete Comment
1682
-
1683
- \`\`\`bash
1684
- trekker comment delete <comment-id>
1685
- \`\`\`
1686
-
1687
- ---
1688
-
1689
- ## Dependencies
1690
-
1691
- > Task relationships - which tasks must be completed before others can start.
1692
- > Trekker automatically detects and prevents circular dependencies.
1693
-
1694
- ### Add Dependency
1695
-
1696
- \`\`\`bash
1697
- trekker dep add <task-id> <depends-on-id>
1698
- \`\`\`
1699
-
1700
- This means:
1701
- - \`<task-id>\` depends on \`<depends-on-id>\`
1702
- - \`<task-id>\` cannot start until \`<depends-on-id>\` is done
1703
-
1704
- **Example:**
1705
- \`\`\`bash
1706
- trekker dep add TREK-2 TREK-1 # TREK-2 depends on TREK-1
1707
- \`\`\`
1708
-
1709
- ### Remove Dependency
1710
-
1711
- \`\`\`bash
1712
- trekker dep remove <task-id> <depends-on-id>
1713
- \`\`\`
1714
-
1715
- ### List Dependencies
1716
-
1717
- \`\`\`bash
1718
- trekker dep list <task-id>
1719
- \`\`\`
1720
-
1721
- Shows both:
1722
- - What this task depends on (blockers)
1723
- - What tasks this task blocks
1724
-
1725
- ---
1726
-
1727
- ## Web Interface
1728
-
1729
- Trekker includes a web interface for visual task management.
1730
-
1731
- ### Start the Web Interface
1732
-
1733
- \`\`\`bash
1734
- trekker serve # Start on port 3000
1735
- trekker serve -p 8080 # Start on custom port
1736
- \`\`\`
1737
-
1738
- The web interface provides:
1739
- - Kanban board with tasks grouped by status (TODO, In Progress, Completed)
1740
- - Epic filter to focus on specific features
1741
- - Task details including dependencies, subtasks, and tags
1742
- - Auto-refresh every 5 seconds to reflect CLI changes
1743
-
1744
- ---
1745
-
1746
- ## Reference
1747
-
1748
- ### ID Formats
1749
-
1750
- | Type | Format | Example |
1751
- |------|--------|---------|
1752
- | Epic | \`EPIC-n\` | EPIC-1, EPIC-2 |
1753
- | Task/Subtask | \`TREK-n\` | TREK-1, TREK-42 |
1754
- | Comment | \`CMT-n\` | CMT-1, CMT-5 |
1755
-
1756
- IDs are auto-generated and sequential within each type.
1757
-
1758
- ### Task Status Values
1759
-
1760
- | Status | Description |
1761
- |--------|-------------|
1762
- | \`todo\` | Not started (default) |
1763
- | \`in_progress\` | Currently being worked on |
1764
- | \`completed\` | Finished successfully |
1765
- | \`wont_fix\` | Decided not to implement |
1766
- | \`archived\` | No longer relevant |
1767
-
1768
- ### Epic Status Values
1769
-
1770
- | Status | Description |
1771
- |--------|-------------|
1772
- | \`todo\` | Not started (default) |
1773
- | \`in_progress\` | Work has begun |
1774
- | \`completed\` | All tasks done |
1775
- | \`archived\` | No longer relevant |
1776
-
1777
- ### Priority Scale
1778
-
1779
- | Value | Meaning |
1780
- |-------|---------|
1781
- | 0 | Critical (drop everything) |
1782
- | 1 | High (do soon) |
1783
- | 2 | Medium (default) |
1784
- | 3 | Low (when time permits) |
1785
- | 4 | Backlog (future consideration) |
1786
- | 5 | Someday (nice to have) |
1787
-
1788
- ---
1789
-
1790
- ## Recommended Workflow for AI Agents
1791
-
1792
- 1. **Initialize** (once per project):
1793
- \`\`\`bash
1794
- trekker init
1795
- \`\`\`
1796
-
1797
- 2. **Check existing state** (every session start):
1798
- \`\`\`bash
1799
- trekker --json task list --status in_progress # See what's active
1800
- trekker --json comment list TREK-1 # Read your previous context
1801
- \`\`\`
1802
-
1803
- 3. **Create epic** for the feature you're working on:
1804
- \`\`\`bash
1805
- trekker epic create -t "Feature Name" -d "Description"
1806
- \`\`\`
1807
-
1808
- 4. **Create tasks** for each piece of work:
1809
- \`\`\`bash
1810
- trekker task create -t "Task name" -e EPIC-1
1811
- \`\`\`
1812
-
1813
- 5. **Add dependencies** if tasks have ordering requirements:
1814
- \`\`\`bash
1815
- trekker dep add TREK-2 TREK-1
1816
- \`\`\`
1817
-
1818
- 6. **Document your initial analysis** before starting work:
1819
- \`\`\`bash
1820
- trekker comment add TREK-1 -a "agent" -c "Analysis: Need to modify X, Y, Z. Approach: ..."
1821
- \`\`\`
1822
-
1823
- 7. **Update status** as you work:
1824
- \`\`\`bash
1825
- trekker task update TREK-1 -s in_progress
1826
- \`\`\`
1827
-
1828
- 8. **Add comments frequently** - this is your external memory:
1829
- \`\`\`bash
1830
- # After investigating
1831
- trekker comment add TREK-1 -a "agent" -c "Found: auth logic in src/auth.ts:45-80"
1832
-
1833
- # After making a decision
1834
- trekker comment add TREK-1 -a "agent" -c "Decision: Using approach X because Y"
1835
-
1836
- # Before context might reset
1837
- trekker comment add TREK-1 -a "agent" -c "Checkpoint: Completed A, B. Next: C, D"
1838
- \`\`\`
1839
-
1840
- 9. **Mark complete** when done:
1841
- \`\`\`bash
1842
- trekker task update TREK-1 -s completed
1843
- trekker comment add TREK-1 -a "agent" -c "Completed. Summary: Implemented X in files A, B, C."
1844
- \`\`\`
1845
-
1846
- 10. **Use JSON output** for parsing:
1847
- \`\`\`bash
1848
- trekker --json task list
1849
- trekker --json task show TREK-1
1850
- \`\`\`
1851
-
1852
- ---
1853
-
1854
- ## Context Management Strategies
1855
-
1856
- AI agents have limited context windows. Use Trekker to extend your effective memory:
1857
-
1858
- ### Starting a New Session
1859
-
1860
- Always begin by reading your previous state:
1861
- \`\`\`bash
1862
- # What was I working on?
1863
- trekker --json task list --status in_progress
1864
-
1865
- # What did I learn/decide?
1866
- trekker --json comment list TREK-1
1867
- \`\`\`
1868
-
1869
- ### During Long Tasks
1870
-
1871
- Periodically checkpoint your progress:
1872
- \`\`\`bash
1873
- trekker comment add TREK-1 -a "agent" -c "Progress: Steps 1-3 done. Current state: X. Next: Y. Blockers: Z."
1874
- \`\`\`
1875
-
1876
- ### Before Context Window Fills Up
1877
-
1878
- When you notice context getting large, dump your current mental state:
1879
- \`\`\`bash
1880
- trekker comment add TREK-1 -a "agent" -c "Context dump:
1881
- - Working on: implementing login validation
1882
- - Files involved: auth.ts (modified lines 45-80), routes.ts (new endpoint at line 120)
1883
- - Current approach: using existing validateEmail() from utils
1884
- - Remaining work: add rate limiting, write tests
1885
- - Open questions: unclear if we need refresh tokens"
1886
- \`\`\`
1887
-
1888
- ### After Solving a Problem
1889
-
1890
- Document the solution so future-you doesn't repeat the investigation:
1891
- \`\`\`bash
1892
- 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."
1893
- \`\`\`
1894
-
1895
- ### When Blocked
1896
-
1897
- Record what you're waiting for:
1898
- \`\`\`bash
1899
- 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."
1900
- \`\`\`
1901
-
1902
- ---
1903
-
1904
- ## Common Scenarios
1905
-
1906
- ### Start a new feature
1907
-
1908
- \`\`\`bash
1909
- trekker epic create -t "API Rate Limiting" -d "Implement rate limiting for all endpoints" -p 1
1910
- trekker task create -t "Design rate limit algorithm" -e EPIC-1
1911
- trekker task create -t "Implement Redis counter" -e EPIC-1
1912
- trekker task create -t "Add middleware" -e EPIC-1
1913
- trekker dep add TREK-3 TREK-2 # Middleware depends on Redis counter
1914
- trekker dep add TREK-2 TREK-1 # Redis counter depends on design
1915
- \`\`\`
1916
-
1917
- ### Check what's ready to work on
1918
-
1919
- \`\`\`bash
1920
- trekker --json task list --status todo
1921
- \`\`\`
1922
-
1923
- ### Get all details about a task
1924
-
1925
- \`\`\`bash
1926
- trekker --json task show TREK-1
1927
- \`\`\`
1928
-
1929
- ### Log progress
1930
-
1931
- \`\`\`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
1932
1791
  trekker task update TREK-1 -s in_progress
1933
- 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"
1934
1795
  trekker task update TREK-1 -s completed
1935
- \`\`\`
1936
-
1937
- ### Break down a complex task
1938
-
1939
- \`\`\`bash
1940
- trekker subtask create TREK-2 -t "Set up Redis connection"
1941
- trekker subtask create TREK-2 -t "Implement increment logic"
1942
- trekker subtask create TREK-2 -t "Add TTL handling"
1943
- \`\`\`
1944
-
1945
- ### Resume work after context reset
1946
-
1947
- \`\`\`bash
1948
- # 1. Find what you were working on
1949
- trekker --json task list --status in_progress
1950
-
1951
- # 2. Read your previous comments to restore context
1952
- trekker --json comment list TREK-1
1953
-
1954
- # 3. Read task details for full picture
1955
- trekker --json task show TREK-1
1956
-
1957
- # 4. Continue work with restored context
1958
- \`\`\`
1959
-
1960
- ### Save context before stopping
1961
1796
 
1962
- \`\`\`bash
1963
- # Dump everything you know before session ends
1964
- trekker comment add TREK-1 -a "agent" -c "Session end checkpoint:
1965
- - Completed: login endpoint, validation
1966
- - In progress: rate limiting (50% done, see middleware.ts:30)
1967
- - Files modified: auth.ts, routes.ts, middleware.ts
1968
- - Next steps: finish rate limiter, add tests
1969
- - Notes: user prefers 100 req/min limit"
1970
- \`\`\`
1797
+ ### Before Context Reset
1798
+ trekker comment add TREK-1 -a "agent" -c "Checkpoint: done A,B. Next: C. Files: x.ts, y.ts"
1971
1799
  `;
1972
- 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(() => {
1973
1801
  console.log(QUICKSTART_TEXT);
1974
1802
  });
1975
1803
 
@@ -2245,7 +2073,7 @@ Seed complete! Created ${epicIds.length} epics, ${taskIds.length} tasks, ${SAMPL
2245
2073
  // package.json
2246
2074
  var package_default = {
2247
2075
  name: "@obsfx/trekker",
2248
- version: "1.1.0",
2076
+ version: "1.2.2",
2249
2077
  description: "A CLI-based issue tracker built for AI coding agents. Stores tasks, epics, and dependencies in a local SQLite database.",
2250
2078
  type: "module",
2251
2079
  main: "dist/index.js",
@@ -2292,6 +2120,7 @@ var package_default = {
2292
2120
  bun: ">=1.0.0"
2293
2121
  },
2294
2122
  dependencies: {
2123
+ "@toon-format/toon": "^2.1.0",
2295
2124
  commander: "^13.1.0",
2296
2125
  "drizzle-orm": "^0.38.4"
2297
2126
  },
@@ -2304,10 +2133,10 @@ var package_default = {
2304
2133
 
2305
2134
  // src/index.ts
2306
2135
  var program = new Command10;
2307
- 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) => {
2308
2137
  const opts = thisCommand.opts();
2309
- if (opts.json) {
2310
- setJsonMode(true);
2138
+ if (opts.toon) {
2139
+ setToonMode(true);
2311
2140
  }
2312
2141
  });
2313
2142
  program.addCommand(initCommand);