trekoon 0.2.0 → 0.2.4
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 +232 -297
- package/README.md +288 -16
- package/package.json +1 -1
- package/src/commands/arg-parser.ts +116 -0
- package/src/commands/dep.ts +197 -25
- package/src/commands/epic.ts +490 -28
- package/src/commands/error-utils.ts +111 -0
- package/src/commands/events.ts +23 -3
- package/src/commands/help.ts +83 -17
- package/src/commands/init.ts +115 -9
- package/src/commands/migrate.ts +11 -4
- package/src/commands/quickstart.ts +76 -30
- package/src/commands/session.ts +223 -0
- package/src/commands/skills.ts +100 -63
- package/src/commands/subtask.ts +224 -26
- package/src/commands/sync.ts +64 -17
- package/src/commands/task-readiness.ts +147 -0
- package/src/commands/task.ts +277 -168
- package/src/commands/wipe.ts +15 -5
- package/src/domain/mutation-service.ts +152 -0
- package/src/domain/tracker-domain.ts +503 -0
- package/src/domain/types.ts +80 -0
- package/src/runtime/cli-shell.ts +83 -5
- package/src/storage/database.ts +86 -0
- package/src/storage/migrations.ts +48 -0
- package/src/storage/path.ts +70 -21
- package/src/storage/schema.ts +9 -2
- package/src/storage/worktree-recovery.ts +376 -0
- package/src/sync/branch-db.ts +87 -35
- package/src/sync/git-context.ts +7 -2
- package/src/sync/service.ts +131 -95
- package/src/sync/types.ts +2 -0
package/src/commands/dep.ts
CHANGED
|
@@ -1,43 +1,172 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
findUnknownOption,
|
|
3
|
+
isValidCompactTempKey,
|
|
4
|
+
parseArgs,
|
|
5
|
+
parseCompactEntityRef,
|
|
6
|
+
parseCompactFields,
|
|
7
|
+
readMissingOptionValue,
|
|
8
|
+
readOptions,
|
|
9
|
+
readUnexpectedPositionals,
|
|
10
|
+
suggestOptions,
|
|
11
|
+
} from "./arg-parser";
|
|
12
|
+
import { unexpectedFailureResult } from "./error-utils";
|
|
2
13
|
|
|
3
14
|
import { MutationService } from "../domain/mutation-service";
|
|
4
15
|
import { TrackerDomain } from "../domain/tracker-domain";
|
|
5
|
-
import {
|
|
16
|
+
import {
|
|
17
|
+
COMPACT_TEMP_KEY_PREFIX,
|
|
18
|
+
type CompactBatchResultContract,
|
|
19
|
+
type CompactDependencySpec,
|
|
20
|
+
type CompactEntityRef,
|
|
21
|
+
} from "../domain/types";
|
|
6
22
|
import { failResult, okResult } from "../io/output";
|
|
7
23
|
import { type CliContext, type CliResult } from "../runtime/command-types";
|
|
8
|
-
import { openTrekoonDatabase } from "../storage/database";
|
|
24
|
+
import { openTrekoonDatabase, type TrekoonDatabase } from "../storage/database";
|
|
9
25
|
|
|
10
26
|
function failFromError(error: unknown): CliResult {
|
|
11
|
-
|
|
12
|
-
return failResult({
|
|
13
|
-
command: "dep",
|
|
14
|
-
human: error.message,
|
|
15
|
-
data: {
|
|
16
|
-
code: error.code,
|
|
17
|
-
...(error.details ?? {}),
|
|
18
|
-
},
|
|
19
|
-
error: {
|
|
20
|
-
code: error.code,
|
|
21
|
-
message: error.message,
|
|
22
|
-
},
|
|
23
|
-
});
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
return failResult({
|
|
27
|
+
return unexpectedFailureResult(error, {
|
|
27
28
|
command: "dep",
|
|
28
29
|
human: "Unexpected dep command failure",
|
|
29
|
-
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const ADD_MANY_OPTIONS = ["dep"] as const;
|
|
34
|
+
|
|
35
|
+
function unknownOption(command: string, option: string, allowedOptions: readonly string[]): CliResult {
|
|
36
|
+
const suggestions = suggestOptions(option, allowedOptions).map((suggestion) => `--${suggestion}`);
|
|
37
|
+
const suggestionMessage = suggestions.length > 0 ? ` Did you mean ${suggestions.join(" or ")}?` : "";
|
|
38
|
+
return failResult({
|
|
39
|
+
command,
|
|
40
|
+
human: `Unknown option --${option}.${suggestionMessage}`,
|
|
41
|
+
data: {
|
|
42
|
+
option: `--${option}`,
|
|
43
|
+
allowedOptions: allowedOptions.map((allowedOption) => `--${allowedOption}`),
|
|
44
|
+
suggestions,
|
|
45
|
+
},
|
|
46
|
+
error: {
|
|
47
|
+
code: "unknown_option",
|
|
48
|
+
message: `Unknown option --${option}`,
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function failMissingOptionValue(command: string, option: string): CliResult {
|
|
54
|
+
return failResult({
|
|
55
|
+
command,
|
|
56
|
+
human: `Option --${option} requires a value.`,
|
|
57
|
+
data: {
|
|
58
|
+
code: "invalid_input",
|
|
59
|
+
option,
|
|
60
|
+
},
|
|
61
|
+
error: {
|
|
62
|
+
code: "invalid_input",
|
|
63
|
+
message: `Option --${option} requires a value`,
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function failBatchSpec(command: string, human: string, data: Record<string, unknown>): CliResult {
|
|
69
|
+
return failResult({
|
|
70
|
+
command,
|
|
71
|
+
human,
|
|
72
|
+
data,
|
|
30
73
|
error: {
|
|
31
|
-
code: "
|
|
32
|
-
message:
|
|
74
|
+
code: "invalid_input",
|
|
75
|
+
message: human,
|
|
33
76
|
},
|
|
34
77
|
});
|
|
35
78
|
}
|
|
36
79
|
|
|
80
|
+
function failUnexpectedPositionals(command: string, unexpected: readonly string[]): CliResult {
|
|
81
|
+
return failBatchSpec(command, `Unexpected positional arguments: ${unexpected.join(", ")}.`, {
|
|
82
|
+
unexpectedPositionals: unexpected,
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function validateCompactEntityRef(index: number, rawSpec: string, label: string, reference: CompactEntityRef): CliResult | undefined {
|
|
87
|
+
if (reference.kind === "temp_key" && !isValidCompactTempKey(reference.tempKey)) {
|
|
88
|
+
return failBatchSpec("dep.add-many", `${label} in --dep spec ${index + 1} must use ${COMPACT_TEMP_KEY_PREFIX}<temp-key> with letters, numbers, dot, dash, or underscore.`, {
|
|
89
|
+
option: "dep",
|
|
90
|
+
index,
|
|
91
|
+
rawSpec,
|
|
92
|
+
reference,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (reference.kind === "id" && reference.id.trim().length === 0) {
|
|
97
|
+
return failBatchSpec("dep.add-many", `${label} in --dep spec ${index + 1} is required.`, {
|
|
98
|
+
option: "dep",
|
|
99
|
+
index,
|
|
100
|
+
rawSpec,
|
|
101
|
+
reference,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return undefined;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function parseDependencySpecs(rawSpecs: readonly string[]): { specs: CompactDependencySpec[]; error?: CliResult } {
|
|
109
|
+
const specs: CompactDependencySpec[] = [];
|
|
110
|
+
|
|
111
|
+
for (const [index, rawSpec] of rawSpecs.entries()) {
|
|
112
|
+
const parsed = parseCompactFields(rawSpec);
|
|
113
|
+
if (parsed.invalidEscape !== null) {
|
|
114
|
+
return {
|
|
115
|
+
specs: [],
|
|
116
|
+
error: failBatchSpec("dep.add-many", `Invalid escape sequence ${parsed.invalidEscape} in --dep spec ${index + 1}.`, {
|
|
117
|
+
option: "dep",
|
|
118
|
+
index,
|
|
119
|
+
rawSpec,
|
|
120
|
+
}),
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (parsed.hasDanglingEscape) {
|
|
125
|
+
return {
|
|
126
|
+
specs: [],
|
|
127
|
+
error: failBatchSpec("dep.add-many", `Trailing escape in --dep spec ${index + 1}.`, {
|
|
128
|
+
option: "dep",
|
|
129
|
+
index,
|
|
130
|
+
rawSpec,
|
|
131
|
+
}),
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (parsed.fields.length !== 2) {
|
|
136
|
+
return {
|
|
137
|
+
specs: [],
|
|
138
|
+
error: failBatchSpec("dep.add-many", `Dependency specs must use <source-ref>|<depends-on-ref> in --dep spec ${index + 1}.`, {
|
|
139
|
+
option: "dep",
|
|
140
|
+
index,
|
|
141
|
+
rawSpec,
|
|
142
|
+
fields: parsed.fields,
|
|
143
|
+
}),
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const source = parseCompactEntityRef(parsed.fields[0] ?? "");
|
|
148
|
+
const sourceError = validateCompactEntityRef(index, rawSpec, "Source ref", source);
|
|
149
|
+
if (sourceError !== undefined) {
|
|
150
|
+
return { specs: [], error: sourceError };
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const dependsOn = parseCompactEntityRef(parsed.fields[1] ?? "");
|
|
154
|
+
const dependsOnError = validateCompactEntityRef(index, rawSpec, "Depends-on ref", dependsOn);
|
|
155
|
+
if (dependsOnError !== undefined) {
|
|
156
|
+
return { specs: [], error: dependsOnError };
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
specs.push({ source, dependsOn });
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return { specs };
|
|
163
|
+
}
|
|
164
|
+
|
|
37
165
|
export async function runDep(context: CliContext): Promise<CliResult> {
|
|
38
|
-
|
|
166
|
+
let database: TrekoonDatabase | undefined;
|
|
39
167
|
|
|
40
168
|
try {
|
|
169
|
+
database = openTrekoonDatabase(context.cwd);
|
|
41
170
|
const parsed = parseArgs(context.args);
|
|
42
171
|
const subcommand: string | undefined = parsed.positional[0];
|
|
43
172
|
const sourceId: string = parsed.positional[1] ?? "";
|
|
@@ -55,6 +184,49 @@ export async function runDep(context: CliContext): Promise<CliResult> {
|
|
|
55
184
|
data: { dependency },
|
|
56
185
|
});
|
|
57
186
|
}
|
|
187
|
+
case "add-many": {
|
|
188
|
+
const addManyUnknownOption = findUnknownOption(parsed, ADD_MANY_OPTIONS);
|
|
189
|
+
if (addManyUnknownOption !== undefined) {
|
|
190
|
+
return unknownOption("dep.add-many", addManyUnknownOption, ADD_MANY_OPTIONS);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const missingAddManyOption = readMissingOptionValue(parsed.missingOptionValues, "dep");
|
|
194
|
+
if (missingAddManyOption !== undefined) {
|
|
195
|
+
return failMissingOptionValue("dep.add-many", missingAddManyOption);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const unexpectedPositionals = readUnexpectedPositionals(parsed, 1);
|
|
199
|
+
if (unexpectedPositionals.length > 0) {
|
|
200
|
+
return failUnexpectedPositionals("dep.add-many", unexpectedPositionals);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const rawSpecs = readOptions(parsed.optionEntries, "dep");
|
|
204
|
+
if (rawSpecs.length === 0) {
|
|
205
|
+
return failBatchSpec("dep.add-many", "Provide at least one --dep spec.", {
|
|
206
|
+
option: "dep",
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const specResult = parseDependencySpecs(rawSpecs);
|
|
211
|
+
if (specResult.error !== undefined) {
|
|
212
|
+
return specResult.error;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const created = mutations.addDependencyBatch({
|
|
216
|
+
specs: specResult.specs,
|
|
217
|
+
});
|
|
218
|
+
const result: CompactBatchResultContract = created.result;
|
|
219
|
+
return okResult({
|
|
220
|
+
command: "dep.add-many",
|
|
221
|
+
human: `Added ${created.dependencies.length} dependenc${created.dependencies.length === 1 ? "y" : "ies"}: ${created.dependencies
|
|
222
|
+
.map((dependency) => `${dependency.sourceId} -> ${dependency.dependsOnId}`)
|
|
223
|
+
.join("\n")}`,
|
|
224
|
+
data: {
|
|
225
|
+
dependencies: created.dependencies,
|
|
226
|
+
result,
|
|
227
|
+
},
|
|
228
|
+
});
|
|
229
|
+
}
|
|
58
230
|
case "remove": {
|
|
59
231
|
const removed: number = mutations.removeDependency(sourceId, dependsOnId);
|
|
60
232
|
|
|
@@ -108,7 +280,7 @@ export async function runDep(context: CliContext): Promise<CliResult> {
|
|
|
108
280
|
default:
|
|
109
281
|
return failResult({
|
|
110
282
|
command: "dep",
|
|
111
|
-
human: "Usage: trekoon dep <add|remove|list|reverse>",
|
|
283
|
+
human: "Usage: trekoon dep <add|add-many|remove|list|reverse>",
|
|
112
284
|
data: {
|
|
113
285
|
args: context.args,
|
|
114
286
|
},
|
|
@@ -121,6 +293,6 @@ export async function runDep(context: CliContext): Promise<CliResult> {
|
|
|
121
293
|
} catch (error: unknown) {
|
|
122
294
|
return failFromError(error);
|
|
123
295
|
} finally {
|
|
124
|
-
database
|
|
296
|
+
database?.close();
|
|
125
297
|
}
|
|
126
298
|
}
|