trekoon 0.1.8 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/skills/trekoon/SKILL.md +117 -2
- package/README.md +158 -18
- package/package.json +1 -1
- package/src/commands/arg-parser.ts +164 -0
- package/src/commands/epic.ts +256 -3
- package/src/commands/help.ts +45 -4
- package/src/commands/subtask.ts +209 -3
- package/src/commands/sync.ts +130 -52
- package/src/commands/task.ts +257 -3
- package/src/domain/mutation-service.ts +242 -1
- package/src/domain/tracker-domain.ts +171 -0
- package/src/domain/types.ts +27 -0
- package/src/index.ts +1 -1
- package/src/io/output.ts +98 -5
- package/src/runtime/cli-shell.ts +159 -22
- package/src/runtime/command-types.ts +18 -0
- package/src/storage/path.ts +58 -1
- package/src/sync/event-writes.ts +21 -1
package/src/commands/epic.ts
CHANGED
|
@@ -1,16 +1,21 @@
|
|
|
1
1
|
import {
|
|
2
|
+
SEARCH_REPLACE_FIELDS,
|
|
3
|
+
findUnknownOption,
|
|
2
4
|
hasFlag,
|
|
3
5
|
parseArgs,
|
|
6
|
+
parseCsvEnumOption,
|
|
4
7
|
parseStrictNonNegativeInt,
|
|
5
8
|
parseStrictPositiveInt,
|
|
6
9
|
readEnumOption,
|
|
7
10
|
readMissingOptionValue,
|
|
8
11
|
readOption,
|
|
12
|
+
resolvePreviewApplyMode,
|
|
13
|
+
suggestOptions,
|
|
9
14
|
} from "./arg-parser";
|
|
10
15
|
|
|
11
16
|
import { MutationService } from "../domain/mutation-service";
|
|
12
17
|
import { TrackerDomain } from "../domain/tracker-domain";
|
|
13
|
-
import { DomainError, type EpicRecord } from "../domain/types";
|
|
18
|
+
import { DomainError, type EpicRecord, type SearchEntityMatch } from "../domain/types";
|
|
14
19
|
import { formatHumanTable } from "../io/human-table";
|
|
15
20
|
import { failResult, okResult } from "../io/output";
|
|
16
21
|
import { type CliContext, type CliResult } from "../runtime/command-types";
|
|
@@ -24,6 +29,8 @@ const VIEW_MODES = ["table", "compact", "tree", "detail"] as const;
|
|
|
24
29
|
const LIST_VIEW_MODES = ["table", "compact"] as const;
|
|
25
30
|
const DEFAULT_LIST_LIMIT = 10;
|
|
26
31
|
const DEFAULT_OPEN_STATUSES = ["in_progress", "in-progress", "todo"] as const;
|
|
32
|
+
const SEARCH_OPTIONS = ["fields", "preview"] as const;
|
|
33
|
+
const REPLACE_OPTIONS = ["search", "replace", "fields", "preview", "apply"] as const;
|
|
27
34
|
|
|
28
35
|
function parseStatusCsv(rawStatuses: string | undefined): string[] | undefined {
|
|
29
36
|
if (rawStatuses === undefined) {
|
|
@@ -36,6 +43,55 @@ function parseStatusCsv(rawStatuses: string | undefined): string[] | undefined {
|
|
|
36
43
|
.filter((value) => value.length > 0);
|
|
37
44
|
}
|
|
38
45
|
|
|
46
|
+
function prefixedOptions(options: readonly string[]): string[] {
|
|
47
|
+
return options.map((option) => `--${option}`);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function unknownOption(command: string, option: string, allowedOptions: readonly string[]): CliResult {
|
|
51
|
+
const suggestions = suggestOptions(option, allowedOptions).map((suggestion) => `--${suggestion}`);
|
|
52
|
+
const suggestionMessage = suggestions.length > 0 ? ` Did you mean ${suggestions.join(" or ")}?` : "";
|
|
53
|
+
return failResult({
|
|
54
|
+
command,
|
|
55
|
+
human: `Unknown option --${option}.${suggestionMessage}`,
|
|
56
|
+
data: {
|
|
57
|
+
option: `--${option}`,
|
|
58
|
+
allowedOptions: prefixedOptions(allowedOptions),
|
|
59
|
+
suggestions,
|
|
60
|
+
},
|
|
61
|
+
error: {
|
|
62
|
+
code: "unknown_option",
|
|
63
|
+
message: `Unknown option --${option}`,
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function invalidSearchInput(command: string, human: string, message: string, data: Record<string, unknown>): CliResult {
|
|
69
|
+
return failResult({
|
|
70
|
+
command,
|
|
71
|
+
human,
|
|
72
|
+
data,
|
|
73
|
+
error: {
|
|
74
|
+
code: "invalid_input",
|
|
75
|
+
message,
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function formatSearchHuman(matches: readonly SearchEntityMatch[], emptyMessage: string): string {
|
|
81
|
+
if (matches.length === 0) {
|
|
82
|
+
return emptyMessage;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return matches
|
|
86
|
+
.map(
|
|
87
|
+
(match) =>
|
|
88
|
+
`${match.kind} ${match.id}: ${match.fields
|
|
89
|
+
.map((field) => `${field.field}(${field.count}) "${field.snippet}"`)
|
|
90
|
+
.join(", ")}`,
|
|
91
|
+
)
|
|
92
|
+
.join("\n");
|
|
93
|
+
}
|
|
94
|
+
|
|
39
95
|
function getStatusPriority(status: string): number {
|
|
40
96
|
if (status === "in_progress" || status === "in-progress") {
|
|
41
97
|
return 0;
|
|
@@ -396,7 +452,28 @@ export async function runEpic(context: CliContext): Promise<CliResult> {
|
|
|
396
452
|
command: "epic.list",
|
|
397
453
|
human,
|
|
398
454
|
data: { epics },
|
|
399
|
-
...(context.mode === "human"
|
|
455
|
+
...(context.mode === "human"
|
|
456
|
+
? {}
|
|
457
|
+
: {
|
|
458
|
+
meta: {
|
|
459
|
+
pagination: listed.pagination,
|
|
460
|
+
defaults: {
|
|
461
|
+
statuses: !includeAll && statuses === undefined ? [...DEFAULT_OPEN_STATUSES] : null,
|
|
462
|
+
limit: !includeAll && limit === undefined ? DEFAULT_LIST_LIMIT : null,
|
|
463
|
+
cursor: rawCursor === undefined ? 0 : null,
|
|
464
|
+
view: view === undefined ? "table" : null,
|
|
465
|
+
},
|
|
466
|
+
filters: {
|
|
467
|
+
statuses: includeAll ? null : (statuses ?? [...DEFAULT_OPEN_STATUSES]),
|
|
468
|
+
includeAll,
|
|
469
|
+
},
|
|
470
|
+
truncation: {
|
|
471
|
+
applied: listed.pagination.hasMore,
|
|
472
|
+
returned: epics.length,
|
|
473
|
+
limit: includeAll ? null : (limit ?? DEFAULT_LIST_LIMIT),
|
|
474
|
+
},
|
|
475
|
+
},
|
|
476
|
+
}),
|
|
400
477
|
});
|
|
401
478
|
}
|
|
402
479
|
case "show": {
|
|
@@ -430,6 +507,22 @@ export async function runEpic(context: CliContext): Promise<CliResult> {
|
|
|
430
507
|
command: "epic.show",
|
|
431
508
|
human: formatEpic(epic),
|
|
432
509
|
data: { epic, includeAll: false },
|
|
510
|
+
...(context.mode === "human"
|
|
511
|
+
? {}
|
|
512
|
+
: {
|
|
513
|
+
meta: {
|
|
514
|
+
defaults: {
|
|
515
|
+
view: view === undefined ? effectiveView : null,
|
|
516
|
+
},
|
|
517
|
+
filters: {
|
|
518
|
+
includeAll: false,
|
|
519
|
+
},
|
|
520
|
+
truncation: {
|
|
521
|
+
applied: true,
|
|
522
|
+
scope: "compact",
|
|
523
|
+
},
|
|
524
|
+
},
|
|
525
|
+
}),
|
|
433
526
|
});
|
|
434
527
|
}
|
|
435
528
|
|
|
@@ -440,6 +533,22 @@ export async function runEpic(context: CliContext): Promise<CliResult> {
|
|
|
440
533
|
command: "epic.show",
|
|
441
534
|
human: formatEpicShowCompact(tree),
|
|
442
535
|
data: { tree, includeAll: false },
|
|
536
|
+
...(context.mode === "human"
|
|
537
|
+
? {}
|
|
538
|
+
: {
|
|
539
|
+
meta: {
|
|
540
|
+
defaults: {
|
|
541
|
+
view: view === undefined ? effectiveView : null,
|
|
542
|
+
},
|
|
543
|
+
filters: {
|
|
544
|
+
includeAll: false,
|
|
545
|
+
},
|
|
546
|
+
truncation: {
|
|
547
|
+
applied: true,
|
|
548
|
+
scope: "tree",
|
|
549
|
+
},
|
|
550
|
+
},
|
|
551
|
+
}),
|
|
443
552
|
});
|
|
444
553
|
}
|
|
445
554
|
|
|
@@ -449,6 +558,150 @@ export async function runEpic(context: CliContext): Promise<CliResult> {
|
|
|
449
558
|
command: "epic.show",
|
|
450
559
|
human: effectiveView === "table" ? formatEpicShowTable(tree) : formatEpicShowDetailed(tree),
|
|
451
560
|
data: { tree, includeAll: true },
|
|
561
|
+
...(context.mode === "human"
|
|
562
|
+
? {}
|
|
563
|
+
: {
|
|
564
|
+
meta: {
|
|
565
|
+
defaults: {
|
|
566
|
+
view: view === undefined ? effectiveView : null,
|
|
567
|
+
},
|
|
568
|
+
filters: {
|
|
569
|
+
includeAll: true,
|
|
570
|
+
},
|
|
571
|
+
truncation: {
|
|
572
|
+
applied: false,
|
|
573
|
+
scope: "full",
|
|
574
|
+
},
|
|
575
|
+
},
|
|
576
|
+
}),
|
|
577
|
+
});
|
|
578
|
+
}
|
|
579
|
+
case "search": {
|
|
580
|
+
const searchUnknownOption = findUnknownOption(parsed, SEARCH_OPTIONS);
|
|
581
|
+
if (searchUnknownOption !== undefined) {
|
|
582
|
+
return unknownOption("epic.search", searchUnknownOption, SEARCH_OPTIONS);
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
const missingSearchOption = readMissingOptionValue(parsed.missingOptionValues, "fields");
|
|
586
|
+
if (missingSearchOption !== undefined) {
|
|
587
|
+
return failMissingOptionValue("epic.search", missingSearchOption);
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
const epicId: string = parsed.positional[1] ?? "";
|
|
591
|
+
const searchText: string = parsed.positional[2] ?? "";
|
|
592
|
+
if (epicId.length === 0 || searchText.trim().length === 0) {
|
|
593
|
+
return invalidSearchInput(
|
|
594
|
+
"epic.search",
|
|
595
|
+
"Usage: trekoon epic search <epic-id> \"search text\" [--fields <csv>] [--preview]",
|
|
596
|
+
"Missing search target",
|
|
597
|
+
{
|
|
598
|
+
epicId,
|
|
599
|
+
},
|
|
600
|
+
);
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
const parsedFields = parseCsvEnumOption(readOption(parsed.options, "fields"), SEARCH_REPLACE_FIELDS);
|
|
604
|
+
if (parsedFields.empty || parsedFields.invalidValues.length > 0) {
|
|
605
|
+
return invalidSearchInput("epic.search", "Invalid --fields value. Use title, description, or title,description.", "Invalid --fields value", {
|
|
606
|
+
fields: readOption(parsed.options, "fields"),
|
|
607
|
+
invalidFields: parsedFields.invalidValues,
|
|
608
|
+
allowedFields: [...SEARCH_REPLACE_FIELDS],
|
|
609
|
+
});
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
const { matches, summary } = domain.searchEpicScope(epicId, searchText, parsedFields.values);
|
|
613
|
+
|
|
614
|
+
return okResult({
|
|
615
|
+
command: "epic.search",
|
|
616
|
+
human: formatSearchHuman(matches, "No matches found."),
|
|
617
|
+
data: {
|
|
618
|
+
scope: {
|
|
619
|
+
kind: "epic",
|
|
620
|
+
id: epicId,
|
|
621
|
+
},
|
|
622
|
+
query: {
|
|
623
|
+
search: searchText,
|
|
624
|
+
fields: parsedFields.values,
|
|
625
|
+
mode: "preview",
|
|
626
|
+
},
|
|
627
|
+
summary,
|
|
628
|
+
matches,
|
|
629
|
+
},
|
|
630
|
+
});
|
|
631
|
+
}
|
|
632
|
+
case "replace": {
|
|
633
|
+
const replaceUnknownOption = findUnknownOption(parsed, REPLACE_OPTIONS);
|
|
634
|
+
if (replaceUnknownOption !== undefined) {
|
|
635
|
+
return unknownOption("epic.replace", replaceUnknownOption, REPLACE_OPTIONS);
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
const missingReplaceOption =
|
|
639
|
+
readMissingOptionValue(parsed.missingOptionValues, "search") ??
|
|
640
|
+
readMissingOptionValue(parsed.missingOptionValues, "replace") ??
|
|
641
|
+
readMissingOptionValue(parsed.missingOptionValues, "fields");
|
|
642
|
+
if (missingReplaceOption !== undefined) {
|
|
643
|
+
return failMissingOptionValue("epic.replace", missingReplaceOption);
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
const epicId: string = parsed.positional[1] ?? "";
|
|
647
|
+
const searchText = readOption(parsed.options, "search") ?? "";
|
|
648
|
+
const replacementText = readOption(parsed.options, "replace") ?? "";
|
|
649
|
+
if (epicId.length === 0 || searchText.trim().length === 0) {
|
|
650
|
+
return invalidSearchInput(
|
|
651
|
+
"epic.replace",
|
|
652
|
+
"Usage: trekoon epic replace <epic-id> --search \"text\" --replace \"text\" [--fields <csv>] [--preview|--apply]",
|
|
653
|
+
"Missing replace target",
|
|
654
|
+
{
|
|
655
|
+
epicId,
|
|
656
|
+
search: searchText,
|
|
657
|
+
},
|
|
658
|
+
);
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
const rawFields = readOption(parsed.options, "fields");
|
|
662
|
+
const parsedFields = parseCsvEnumOption(rawFields, SEARCH_REPLACE_FIELDS);
|
|
663
|
+
if (parsedFields.empty || parsedFields.invalidValues.length > 0) {
|
|
664
|
+
return invalidSearchInput("epic.replace", "Invalid --fields value. Use title, description, or title,description.", "Invalid --fields value", {
|
|
665
|
+
fields: rawFields,
|
|
666
|
+
invalidFields: parsedFields.invalidValues,
|
|
667
|
+
allowedFields: [...SEARCH_REPLACE_FIELDS],
|
|
668
|
+
});
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
const previewMode = resolvePreviewApplyMode(parsed.flags);
|
|
672
|
+
if (previewMode.conflict) {
|
|
673
|
+
return invalidSearchInput("epic.replace", "Use either --preview or --apply, not both.", "Conflicting mode flags", {
|
|
674
|
+
flags: ["preview", "apply"],
|
|
675
|
+
});
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
const replacementSummary = previewMode.mode === "apply"
|
|
679
|
+
? mutations.applyEpicReplacement(epicId, searchText, replacementText, parsedFields.values)
|
|
680
|
+
: mutations.previewEpicReplacement(epicId, searchText, replacementText, parsedFields.values);
|
|
681
|
+
const { matches, summary: matchSummary } = replacementSummary;
|
|
682
|
+
|
|
683
|
+
const summary = {
|
|
684
|
+
...matchSummary,
|
|
685
|
+
mode: previewMode.mode,
|
|
686
|
+
};
|
|
687
|
+
|
|
688
|
+
return okResult({
|
|
689
|
+
command: "epic.replace",
|
|
690
|
+
human: formatSearchHuman(matches, `No ${previewMode.mode === "apply" ? "replacements" : "matches"} found.`),
|
|
691
|
+
data: {
|
|
692
|
+
scope: {
|
|
693
|
+
kind: "epic",
|
|
694
|
+
id: epicId,
|
|
695
|
+
},
|
|
696
|
+
query: {
|
|
697
|
+
search: searchText,
|
|
698
|
+
replace: replacementText,
|
|
699
|
+
fields: parsedFields.values,
|
|
700
|
+
mode: previewMode.mode,
|
|
701
|
+
},
|
|
702
|
+
summary,
|
|
703
|
+
matches,
|
|
704
|
+
},
|
|
452
705
|
});
|
|
453
706
|
}
|
|
454
707
|
case "update": {
|
|
@@ -588,7 +841,7 @@ export async function runEpic(context: CliContext): Promise<CliResult> {
|
|
|
588
841
|
default:
|
|
589
842
|
return failResult({
|
|
590
843
|
command: "epic",
|
|
591
|
-
human: "Usage: trekoon epic <create|list|show|update|delete>",
|
|
844
|
+
human: "Usage: trekoon epic <create|list|show|search|replace|update|delete>",
|
|
592
845
|
data: {
|
|
593
846
|
args: context.args,
|
|
594
847
|
},
|
package/src/commands/help.ts
CHANGED
|
@@ -12,10 +12,12 @@ const ROOT_HELP = [
|
|
|
12
12
|
"Global options:",
|
|
13
13
|
" --json Emit stable JSON machine output",
|
|
14
14
|
" --toon Emit true TOON-encoded output",
|
|
15
|
+
" --compat <mode> Enable explicit machine compatibility mode",
|
|
15
16
|
" --help Show root or command help",
|
|
16
17
|
" --version Print CLI version",
|
|
17
18
|
"",
|
|
18
19
|
"Commands:",
|
|
20
|
+
" help Show root or command help",
|
|
19
21
|
" init Initialize .trekoon storage and local DB",
|
|
20
22
|
" quickstart Show AI execution loop + task detail workflow",
|
|
21
23
|
" wipe Remove local Trekoon state (requires --yes)",
|
|
@@ -72,7 +74,7 @@ const WIPE_HELP = [
|
|
|
72
74
|
].join("\n");
|
|
73
75
|
|
|
74
76
|
const EPIC_HELP = [
|
|
75
|
-
"Usage: trekoon epic <create|list|show|update|delete> [options]",
|
|
77
|
+
"Usage: trekoon epic <create|list|show|search|replace|update|delete> [options]",
|
|
76
78
|
"",
|
|
77
79
|
"List behavior:",
|
|
78
80
|
" Defaults:",
|
|
@@ -88,12 +90,23 @@ const EPIC_HELP = [
|
|
|
88
90
|
"",
|
|
89
91
|
"Show behavior:",
|
|
90
92
|
" Views:",
|
|
93
|
+
" - table: default view",
|
|
91
94
|
" - compact: epic summary",
|
|
92
95
|
" - tree: hierarchy",
|
|
93
96
|
" - detail: descriptions",
|
|
94
97
|
" Machine default:",
|
|
95
98
|
" - With --all, machine modes default to detail",
|
|
96
99
|
"",
|
|
100
|
+
"Search/Replace behavior:",
|
|
101
|
+
" search:",
|
|
102
|
+
" - trekoon epic search <epic-id> \"search text\"",
|
|
103
|
+
" - Options: --fields title|description|title,description, --preview",
|
|
104
|
+
" - Scope: epic title/description + descendant task/subtask title/description",
|
|
105
|
+
" replace:",
|
|
106
|
+
" - trekoon epic replace <epic-id> --search \"text\" --replace \"text\"",
|
|
107
|
+
" - Preview is default; use --apply to mutate",
|
|
108
|
+
" - --preview and --apply are mutually exclusive",
|
|
109
|
+
"",
|
|
97
110
|
"Update behavior:",
|
|
98
111
|
" Bulk target flags:",
|
|
99
112
|
" --all | --ids <csv>",
|
|
@@ -102,14 +115,14 @@ const EPIC_HELP = [
|
|
|
102
115
|
].join("\n");
|
|
103
116
|
|
|
104
117
|
const TASK_HELP = [
|
|
105
|
-
"Usage: trekoon task <create|list|show|ready|next|update|delete> [options]",
|
|
118
|
+
"Usage: trekoon task <create|list|show|ready|next|search|replace|update|delete> [options]",
|
|
106
119
|
"",
|
|
107
120
|
"List behavior:",
|
|
108
121
|
" Defaults:",
|
|
109
122
|
" - Open statuses only: in_progress, in-progress, todo",
|
|
110
123
|
" - Limit: 10",
|
|
111
124
|
" Flags:",
|
|
112
|
-
" --status <csv> | --limit <n> | --cursor <n> | --all | --view table|compact",
|
|
125
|
+
" --epic <id> | --status <csv> | --limit <n> | --cursor <n> | --all | --view table|compact",
|
|
113
126
|
" Pagination:",
|
|
114
127
|
" - --cursor is offset-like",
|
|
115
128
|
" - Machine modes expose meta.pagination.hasMore / nextCursor",
|
|
@@ -118,6 +131,7 @@ const TASK_HELP = [
|
|
|
118
131
|
"",
|
|
119
132
|
"Show behavior:",
|
|
120
133
|
" Views:",
|
|
134
|
+
" - table: default view",
|
|
121
135
|
" - compact: task summary",
|
|
122
136
|
" - tree: hierarchy",
|
|
123
137
|
" - detail: descriptions",
|
|
@@ -133,6 +147,16 @@ const TASK_HELP = [
|
|
|
133
147
|
" - Returns top ready candidate",
|
|
134
148
|
" - Option: --epic <id>",
|
|
135
149
|
"",
|
|
150
|
+
"Search/Replace behavior:",
|
|
151
|
+
" search:",
|
|
152
|
+
" - trekoon task search <task-id> \"search text\"",
|
|
153
|
+
" - Options: --fields title|description|title,description, --preview",
|
|
154
|
+
" - Scope: task title/description + descendant subtask title/description",
|
|
155
|
+
" replace:",
|
|
156
|
+
" - trekoon task replace <task-id> --search \"text\" --replace \"text\"",
|
|
157
|
+
" - Preview is default; use --apply to mutate",
|
|
158
|
+
" - --preview and --apply are mutually exclusive",
|
|
159
|
+
"",
|
|
136
160
|
"Update behavior:",
|
|
137
161
|
" Bulk target flags:",
|
|
138
162
|
" --all | --ids <csv>",
|
|
@@ -141,7 +165,7 @@ const TASK_HELP = [
|
|
|
141
165
|
].join("\n");
|
|
142
166
|
|
|
143
167
|
const SUBTASK_HELP = [
|
|
144
|
-
"Usage: trekoon subtask <create|list|update|delete> [options]",
|
|
168
|
+
"Usage: trekoon subtask <create|list|search|replace|update|delete> [options]",
|
|
145
169
|
"",
|
|
146
170
|
"List behavior:",
|
|
147
171
|
" Defaults:",
|
|
@@ -155,6 +179,16 @@ const SUBTASK_HELP = [
|
|
|
155
179
|
" Constraints:",
|
|
156
180
|
" - --all is mutually exclusive with --status, --limit, and --cursor",
|
|
157
181
|
"",
|
|
182
|
+
"Search/Replace behavior:",
|
|
183
|
+
" search:",
|
|
184
|
+
" - trekoon subtask search <subtask-id> \"search text\"",
|
|
185
|
+
" - Options: --fields title|description|title,description, --preview",
|
|
186
|
+
" - Scope: subtask title/description only",
|
|
187
|
+
" replace:",
|
|
188
|
+
" - trekoon subtask replace <subtask-id> --search \"text\" --replace \"text\"",
|
|
189
|
+
" - Preview is default; use --apply to mutate",
|
|
190
|
+
" - --preview and --apply are mutually exclusive",
|
|
191
|
+
"",
|
|
158
192
|
"Update behavior:",
|
|
159
193
|
" Bulk target flags:",
|
|
160
194
|
" --all | --ids <csv>",
|
|
@@ -229,6 +263,13 @@ const SYNC_HELP = [
|
|
|
229
263
|
" resolve <conflict-id> --use ours|theirs",
|
|
230
264
|
" Resolve a pending conflict by selecting ours or theirs.",
|
|
231
265
|
"",
|
|
266
|
+
"Compatibility mode:",
|
|
267
|
+
" --compat legacy-sync-command-ids",
|
|
268
|
+
" Emits legacy sync command IDs (sync_status, sync_pull, ...)",
|
|
269
|
+
" in machine output only and includes deprecation metadata.",
|
|
270
|
+
" Migration: remove --compat and consume dotted IDs (sync.status).",
|
|
271
|
+
" Planned compatibility window closes after 2026-09-30.",
|
|
272
|
+
"",
|
|
232
273
|
"Examples:",
|
|
233
274
|
" trekoon sync status",
|
|
234
275
|
" trekoon sync status --from main",
|