@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.
- package/README.md +21 -6
- package/dist/index.js +574 -745
- 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
|
|
315
|
-
function
|
|
316
|
-
|
|
795
|
+
var toonMode = false;
|
|
796
|
+
function setToonMode(enabled) {
|
|
797
|
+
toonMode = enabled;
|
|
317
798
|
}
|
|
318
|
-
function
|
|
319
|
-
return
|
|
799
|
+
function isToonMode() {
|
|
800
|
+
return toonMode;
|
|
320
801
|
}
|
|
321
802
|
function output(data) {
|
|
322
|
-
if (
|
|
323
|
-
console.log(
|
|
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 (
|
|
332
|
-
console.log(
|
|
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 (
|
|
342
|
-
console.error(
|
|
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 (!
|
|
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 (
|
|
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 (
|
|
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 (
|
|
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 (
|
|
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 (
|
|
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 (
|
|
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 (
|
|
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 (
|
|
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 (
|
|
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 (
|
|
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 (
|
|
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 (
|
|
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 (
|
|
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 (
|
|
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 (
|
|
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 (
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
1448
|
-
trekker
|
|
1735
|
+
## Setup
|
|
1736
|
+
trekker init # Initialize
|
|
1737
|
+
trekker wipe -y # Remove all data
|
|
1449
1738
|
|
|
1450
|
-
|
|
1451
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
trekker
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
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 "
|
|
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
|
-
|
|
1963
|
-
|
|
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
|
|
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.
|
|
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("--
|
|
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.
|
|
2310
|
-
|
|
2138
|
+
if (opts.toon) {
|
|
2139
|
+
setToonMode(true);
|
|
2311
2140
|
}
|
|
2312
2141
|
});
|
|
2313
2142
|
program.addCommand(initCommand);
|