@taskset/cli 3.0.3 → 5.1.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/CHANGELOG.md +39 -0
- package/README.md +8 -3
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +686 -19
- package/docs/_meta.ts +8 -0
- package/docs/cli-reference.md +461 -0
- package/docs/configuration.md +61 -0
- package/docs/document-types.md +100 -0
- package/docs/getting-started.md +99 -0
- package/docs/index.md +37 -0
- package/docs/maintainers/_meta.ts +7 -0
- package/docs/maintainers/architecture/_meta.ts +5 -0
- package/docs/maintainers/architecture/decisions/0001-documentation-platform.md +70 -0
- package/docs/maintainers/architecture/decisions/0002-code-architecture.md +31 -0
- package/docs/maintainers/architecture/decisions/0003-snapshot-policy.md +31 -0
- package/docs/maintainers/architecture/decisions/_meta.ts +5 -0
- package/docs/maintainers/architecture/overview.md +111 -0
- package/docs/maintainers/architecture/synchronization.md +56 -0
- package/docs/maintainers/development/_meta.ts +6 -0
- package/docs/maintainers/development/contributing.md +59 -0
- package/docs/maintainers/development/documentation.md +69 -0
- package/docs/maintainers/development/engineering.md +59 -0
- package/docs/maintainers/development/testing.md +89 -0
- package/docs/maintainers/index.md +22 -0
- package/docs/maintainers/product/_meta.ts +3 -0
- package/docs/maintainers/product/vision.md +76 -0
- package/docs/maintainers/technology.md +55 -0
- package/docs/task-files.md +174 -0
- package/package.json +10 -8
- package/skills/taskset/SKILL.md +227 -0
- package/skills/taskset/references/changesets-examples.md +97 -0
- package/skills/taskset/references/document-modeling-examples.md +63 -0
- package/skills/taskset/references/monorepo-task-modeling.md +125 -0
- package/skills/taskset/references/task-modeling-examples.md +249 -0
- package/skills/taskset-implement/SKILL.md +234 -0
- package/skills/taskset-implement/agents/openai.yaml +4 -0
- package/skills/taskset-implement/references/architecture/client-and-server.md +69 -0
- package/skills/taskset-implement/references/architecture/documentation-and-generated.md +70 -0
- package/skills/taskset-implement/references/architecture/ownership-and-dependencies.md +107 -0
- package/skills/taskset-implement/references/architecture/product-and-source.md +80 -0
- package/skills/taskset-implement/references/architecture/storage-and-snapshots.md +45 -0
- package/skills/taskset-implement/references/architecture.md +34 -0
- package/skills/taskset-implement/references/conventions/backend-and-tooling.md +33 -0
- package/skills/taskset-implement/references/conventions/design.md +38 -0
- package/skills/taskset-implement/references/conventions/interfaces-and-ui.md +50 -0
- package/skills/taskset-implement/references/conventions/naming-and-packages.md +64 -0
- package/skills/taskset-implement/references/conventions/task-files.md +89 -0
- package/skills/taskset-implement/references/conventions/tests-and-docs.md +55 -0
- package/skills/taskset-implement/references/conventions/typescript-and-exports.md +41 -0
- package/skills/taskset-implement/references/conventions.md +40 -0
- package/skills/taskset-implement/references/release.md +135 -0
- package/skills/taskset-implement/references/workflows/dependencies-and-docs-site.md +54 -0
- package/skills/taskset-implement/references/workflows/environment-and-pnpm.md +108 -0
- package/skills/taskset-implement/references/workflows/persisted-data-and-git.md +30 -0
- package/skills/taskset-implement/references/workflows/validation.md +34 -0
- package/skills/taskset-implement/references/workflows/vitest-and-test-strategy.md +82 -0
- package/skills/taskset-implement/references/workflows.md +33 -0
- package/src/cli.ts +851 -17
package/src/cli.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises'
|
|
1
2
|
import path from 'node:path'
|
|
2
3
|
import { parseArgs } from 'node:util'
|
|
3
4
|
import {
|
|
5
|
+
DocumentIdSchema,
|
|
6
|
+
DocumentStatusSchema,
|
|
7
|
+
DocumentTimestampSchema,
|
|
4
8
|
TaskIdSchema,
|
|
5
9
|
TaskPrioritySchema,
|
|
6
10
|
TaskRiskSchema,
|
|
@@ -8,28 +12,49 @@ import {
|
|
|
8
12
|
TaskTimestampSchema,
|
|
9
13
|
} from '@taskset/contracts'
|
|
10
14
|
import {
|
|
15
|
+
buildDocumentGraph,
|
|
11
16
|
buildTaskIndex,
|
|
17
|
+
createDocument,
|
|
12
18
|
createSnapshot,
|
|
13
19
|
createTask,
|
|
20
|
+
type DerivedDocumentRelationships,
|
|
14
21
|
type DerivedTaskRelationships,
|
|
22
|
+
DOCUMENT_SORT_DIRECTIONS,
|
|
23
|
+
DOCUMENT_SORT_KEYS,
|
|
24
|
+
type DocumentBatchOperation,
|
|
25
|
+
type DocumentQuery,
|
|
26
|
+
DocumentQuerySchema,
|
|
27
|
+
type DocumentRecord,
|
|
28
|
+
deleteDocument,
|
|
15
29
|
deleteTask,
|
|
16
30
|
diagnoseRepository,
|
|
17
31
|
discoverRepository,
|
|
32
|
+
executeDocumentBatch,
|
|
18
33
|
generateViews,
|
|
34
|
+
importDocument,
|
|
19
35
|
initializeRepository,
|
|
36
|
+
listDocuments,
|
|
20
37
|
listSnapshots,
|
|
38
|
+
migrateTaskIds,
|
|
39
|
+
normalizeDocumentKind,
|
|
21
40
|
normalizeRepositoryPath,
|
|
41
|
+
queryDocuments,
|
|
22
42
|
queryTasks,
|
|
23
43
|
RepositoryPathError,
|
|
44
|
+
readDocument,
|
|
24
45
|
readTask,
|
|
25
46
|
restoreSnapshot,
|
|
47
|
+
serializeDocumentFile,
|
|
26
48
|
serializeTaskFile,
|
|
49
|
+
syncRepository,
|
|
27
50
|
TASK_SORT_DIRECTIONS,
|
|
28
51
|
TASK_SORT_KEYS,
|
|
29
52
|
type TaskQuery,
|
|
30
53
|
TaskQuerySchema,
|
|
31
54
|
type TaskRecord,
|
|
55
|
+
type UpdateDocumentInput,
|
|
32
56
|
type UpdateTaskInput,
|
|
57
|
+
updateDocument,
|
|
33
58
|
updateTask,
|
|
34
59
|
} from '@taskset/core'
|
|
35
60
|
import * as z from 'zod'
|
|
@@ -47,12 +72,22 @@ const USAGE = `Usage:
|
|
|
47
72
|
taskset task update <task-id> [metadata options]
|
|
48
73
|
taskset task status <task-id> <status> [--json] [--cwd <path>]
|
|
49
74
|
taskset task delete <task-id> [--remove-dependencies] [--json] [--cwd <path>]
|
|
75
|
+
taskset task migrate-ids [--json] [--cwd <path>]
|
|
76
|
+
taskset sync [--concurrency <count>] [--json] [--cwd <path>]
|
|
77
|
+
taskset document create <story|flow|decision|adr|dr|research|runbook> --title <title> [metadata options]
|
|
78
|
+
taskset document import <markdown-path> [--type <type>] [--move]
|
|
79
|
+
taskset document batch <manifest.json> [--concurrency <count>] [--json]
|
|
80
|
+
taskset document list [type] [query options]
|
|
81
|
+
taskset document show <document-id> [--type <type>] [--include-derived] [--json]
|
|
82
|
+
taskset document update <document-id> [metadata options] [--type <type>]
|
|
83
|
+
taskset document status <document-id> <status> [--type <type>] [--json]
|
|
84
|
+
taskset document delete <document-id> [--type <type>] [--remove-dependencies] [--json]
|
|
50
85
|
|
|
51
86
|
Metadata options:
|
|
52
87
|
--status --priority --order --owner --team --estimate --effort --risk --due-date
|
|
53
88
|
--label --assignee --reviewer --depends-on --related --duplicate
|
|
54
89
|
--parent --file --directory --project --body
|
|
55
|
-
Repeat array options. Task updates clear arrays with --clear-dependencies,
|
|
90
|
+
Repeat array options. Task and document updates clear arrays with --clear-dependencies,
|
|
56
91
|
--clear-labels, --clear-assignees, --clear-reviewers, --clear-related,
|
|
57
92
|
--clear-files, --clear-directories, or --clear-projects. Clear scalar
|
|
58
93
|
relationships with --clear-parent or --clear-owner.
|
|
@@ -104,31 +139,98 @@ const CommonValuesSchema = z.strictObject({
|
|
|
104
139
|
json: JsonSchema,
|
|
105
140
|
})
|
|
106
141
|
|
|
107
|
-
const
|
|
142
|
+
const ConcurrencySchema = z.coerce.number().int().min(1).max(32).optional()
|
|
143
|
+
const DocumentKindInputSchema = TrimmedStringSchema.transform((value, context) => {
|
|
144
|
+
try {
|
|
145
|
+
return normalizeDocumentKind(value)
|
|
146
|
+
} catch (error) {
|
|
147
|
+
context.addIssue({
|
|
148
|
+
code: 'custom',
|
|
149
|
+
message: error instanceof Error ? error.message : 'Invalid document type',
|
|
150
|
+
})
|
|
151
|
+
return z.NEVER
|
|
152
|
+
}
|
|
153
|
+
})
|
|
154
|
+
const DocumentInputSchema = z.strictObject({
|
|
155
|
+
type: DocumentKindInputSchema,
|
|
108
156
|
title: TrimmedStringSchema,
|
|
109
|
-
status:
|
|
157
|
+
status: DocumentStatusSchema.optional(),
|
|
110
158
|
priority: TaskPrioritySchema.optional(),
|
|
111
|
-
order: z.
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
159
|
+
order: z.number().finite().nonnegative().optional(),
|
|
160
|
+
labels: StringListSchema.optional(),
|
|
161
|
+
dependsOn: TaskIdListSchema.optional(),
|
|
162
|
+
files: StringListSchema.optional(),
|
|
115
163
|
owner: TrimmedStringSchema.optional(),
|
|
116
|
-
|
|
117
|
-
|
|
164
|
+
assignees: StringListSchema.optional(),
|
|
165
|
+
reviewers: StringListSchema.optional(),
|
|
118
166
|
team: TrimmedStringSchema.optional(),
|
|
119
|
-
estimate: z.
|
|
120
|
-
effort: z.
|
|
167
|
+
estimate: z.number().int().nonnegative().optional(),
|
|
168
|
+
effort: z.number().finite().nonnegative().optional(),
|
|
121
169
|
risk: TaskRiskSchema.optional(),
|
|
122
|
-
|
|
170
|
+
dueDate: DocumentTimestampSchema.optional(),
|
|
123
171
|
related: TaskIdListSchema.optional(),
|
|
124
|
-
|
|
172
|
+
duplicates: TaskIdListSchema.optional(),
|
|
125
173
|
parent: TaskIdSchema.optional(),
|
|
126
|
-
|
|
127
|
-
|
|
174
|
+
directories: StringListSchema.optional(),
|
|
175
|
+
projects: StringListSchema.optional(),
|
|
128
176
|
body: z.string().optional(),
|
|
129
|
-
cwd: CwdSchema,
|
|
130
|
-
json: JsonSchema,
|
|
131
177
|
})
|
|
178
|
+
const DocumentUpdateSchema = z
|
|
179
|
+
.strictObject({
|
|
180
|
+
title: TrimmedStringSchema.optional(),
|
|
181
|
+
status: DocumentStatusSchema.optional(),
|
|
182
|
+
priority: TaskPrioritySchema.nullable().optional(),
|
|
183
|
+
order: z.number().finite().nonnegative().nullable().optional(),
|
|
184
|
+
labels: StringListSchema.optional(),
|
|
185
|
+
dependsOn: TaskIdListSchema.optional(),
|
|
186
|
+
files: StringListSchema.optional(),
|
|
187
|
+
owner: TrimmedStringSchema.nullable().optional(),
|
|
188
|
+
assignees: StringListSchema.optional(),
|
|
189
|
+
reviewers: StringListSchema.optional(),
|
|
190
|
+
team: TrimmedStringSchema.nullable().optional(),
|
|
191
|
+
estimate: z.number().int().nonnegative().nullable().optional(),
|
|
192
|
+
effort: z.number().finite().nonnegative().nullable().optional(),
|
|
193
|
+
risk: TaskRiskSchema.nullable().optional(),
|
|
194
|
+
dueDate: DocumentTimestampSchema.nullable().optional(),
|
|
195
|
+
related: TaskIdListSchema.optional(),
|
|
196
|
+
duplicates: TaskIdListSchema.optional(),
|
|
197
|
+
parent: TaskIdSchema.nullable().optional(),
|
|
198
|
+
directories: StringListSchema.optional(),
|
|
199
|
+
projects: StringListSchema.optional(),
|
|
200
|
+
body: z.string().optional(),
|
|
201
|
+
})
|
|
202
|
+
.refine((input) => Object.keys(input).length > 0, 'Document update requires at least one field')
|
|
203
|
+
const DocumentBatchSchema = z
|
|
204
|
+
.array(
|
|
205
|
+
z.discriminatedUnion('action', [
|
|
206
|
+
z.strictObject({ action: z.literal('create'), input: DocumentInputSchema }),
|
|
207
|
+
z.strictObject({
|
|
208
|
+
action: z.literal('import'),
|
|
209
|
+
sourcePath: TrimmedStringSchema,
|
|
210
|
+
options: z
|
|
211
|
+
.strictObject({
|
|
212
|
+
type: DocumentKindInputSchema.optional(),
|
|
213
|
+
title: TrimmedStringSchema.optional(),
|
|
214
|
+
move: z.boolean().optional(),
|
|
215
|
+
})
|
|
216
|
+
.optional(),
|
|
217
|
+
}),
|
|
218
|
+
z.strictObject({
|
|
219
|
+
action: z.literal('update'),
|
|
220
|
+
id: TrimmedStringSchema,
|
|
221
|
+
type: DocumentKindInputSchema.optional(),
|
|
222
|
+
input: DocumentUpdateSchema,
|
|
223
|
+
}),
|
|
224
|
+
z.strictObject({
|
|
225
|
+
action: z.literal('export'),
|
|
226
|
+
id: TrimmedStringSchema,
|
|
227
|
+
type: DocumentKindInputSchema.optional(),
|
|
228
|
+
targetPath: TrimmedStringSchema,
|
|
229
|
+
overwrite: z.boolean().optional(),
|
|
230
|
+
}),
|
|
231
|
+
]),
|
|
232
|
+
)
|
|
233
|
+
.min(1)
|
|
132
234
|
|
|
133
235
|
const UPDATE_CLEAR_PAIRS = [
|
|
134
236
|
['priority', 'clear-priority'],
|
|
@@ -151,6 +253,161 @@ const UPDATE_CLEAR_PAIRS = [
|
|
|
151
253
|
['project', 'clear-projects'],
|
|
152
254
|
] as const
|
|
153
255
|
|
|
256
|
+
const DocumentCreateValuesSchema = z.strictObject({
|
|
257
|
+
title: TrimmedStringSchema,
|
|
258
|
+
status: DocumentStatusSchema.optional(),
|
|
259
|
+
priority: TaskPrioritySchema.optional(),
|
|
260
|
+
order: z.coerce.number().finite().nonnegative().optional(),
|
|
261
|
+
label: StringListSchema.optional(),
|
|
262
|
+
'depends-on': TaskIdListSchema.optional(),
|
|
263
|
+
file: StringListSchema.optional(),
|
|
264
|
+
owner: TrimmedStringSchema.optional(),
|
|
265
|
+
assignee: StringListSchema.optional(),
|
|
266
|
+
reviewer: StringListSchema.optional(),
|
|
267
|
+
team: TrimmedStringSchema.optional(),
|
|
268
|
+
estimate: z.coerce.number().int().nonnegative().optional(),
|
|
269
|
+
effort: z.coerce.number().finite().nonnegative().optional(),
|
|
270
|
+
risk: TaskRiskSchema.optional(),
|
|
271
|
+
'due-date': DocumentTimestampSchema.optional(),
|
|
272
|
+
related: TaskIdListSchema.optional(),
|
|
273
|
+
duplicate: TaskIdListSchema.optional(),
|
|
274
|
+
parent: TaskIdSchema.optional(),
|
|
275
|
+
directory: StringListSchema.optional(),
|
|
276
|
+
project: StringListSchema.optional(),
|
|
277
|
+
body: z.string().optional(),
|
|
278
|
+
cwd: CwdSchema,
|
|
279
|
+
json: JsonSchema,
|
|
280
|
+
})
|
|
281
|
+
|
|
282
|
+
const DocumentUpdateValuesSchema = z
|
|
283
|
+
.strictObject({
|
|
284
|
+
title: TrimmedStringSchema.optional(),
|
|
285
|
+
status: DocumentStatusSchema.optional(),
|
|
286
|
+
priority: TaskPrioritySchema.optional(),
|
|
287
|
+
order: z.coerce.number().finite().nonnegative().optional(),
|
|
288
|
+
label: StringListSchema.optional(),
|
|
289
|
+
'depends-on': TaskIdListSchema.optional(),
|
|
290
|
+
file: StringListSchema.optional(),
|
|
291
|
+
owner: TrimmedStringSchema.optional(),
|
|
292
|
+
assignee: StringListSchema.optional(),
|
|
293
|
+
reviewer: StringListSchema.optional(),
|
|
294
|
+
team: TrimmedStringSchema.optional(),
|
|
295
|
+
estimate: z.coerce.number().int().nonnegative().optional(),
|
|
296
|
+
effort: z.coerce.number().finite().nonnegative().optional(),
|
|
297
|
+
risk: TaskRiskSchema.optional(),
|
|
298
|
+
'due-date': DocumentTimestampSchema.optional(),
|
|
299
|
+
related: TaskIdListSchema.optional(),
|
|
300
|
+
duplicate: TaskIdListSchema.optional(),
|
|
301
|
+
parent: TaskIdSchema.optional(),
|
|
302
|
+
directory: StringListSchema.optional(),
|
|
303
|
+
project: StringListSchema.optional(),
|
|
304
|
+
body: z.string().optional(),
|
|
305
|
+
type: TrimmedStringSchema.optional(),
|
|
306
|
+
'clear-priority': z.boolean().optional(),
|
|
307
|
+
'clear-order': z.boolean().optional(),
|
|
308
|
+
'clear-labels': z.boolean().optional(),
|
|
309
|
+
'clear-dependencies': z.boolean().optional(),
|
|
310
|
+
'clear-files': z.boolean().optional(),
|
|
311
|
+
'clear-owner': z.boolean().optional(),
|
|
312
|
+
'clear-assignees': z.boolean().optional(),
|
|
313
|
+
'clear-reviewers': z.boolean().optional(),
|
|
314
|
+
'clear-team': z.boolean().optional(),
|
|
315
|
+
'clear-estimate': z.boolean().optional(),
|
|
316
|
+
'clear-effort': z.boolean().optional(),
|
|
317
|
+
'clear-risk': z.boolean().optional(),
|
|
318
|
+
'clear-due-date': z.boolean().optional(),
|
|
319
|
+
'clear-related': z.boolean().optional(),
|
|
320
|
+
'clear-duplicates': z.boolean().optional(),
|
|
321
|
+
'clear-parent': z.boolean().optional(),
|
|
322
|
+
'clear-directories': z.boolean().optional(),
|
|
323
|
+
'clear-projects': z.boolean().optional(),
|
|
324
|
+
cwd: CwdSchema,
|
|
325
|
+
json: JsonSchema,
|
|
326
|
+
})
|
|
327
|
+
.superRefine((values, context) => {
|
|
328
|
+
for (const [valueKey, clearKey] of UPDATE_CLEAR_PAIRS) {
|
|
329
|
+
if (values[valueKey] !== undefined && values[clearKey]) {
|
|
330
|
+
context.addIssue({
|
|
331
|
+
code: 'custom',
|
|
332
|
+
path: [clearKey],
|
|
333
|
+
message: `Cannot set and clear ${valueKey} in the same update`,
|
|
334
|
+
})
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
const nonControlKeys = Object.keys(values).filter(
|
|
339
|
+
(key) => key !== 'cwd' && key !== 'json' && key !== 'type',
|
|
340
|
+
)
|
|
341
|
+
|
|
342
|
+
if (nonControlKeys.length === 0) {
|
|
343
|
+
context.addIssue({
|
|
344
|
+
code: 'custom',
|
|
345
|
+
message: 'Document update requires at least one field option',
|
|
346
|
+
})
|
|
347
|
+
}
|
|
348
|
+
})
|
|
349
|
+
|
|
350
|
+
const DocumentListValuesSchema = z.strictObject({
|
|
351
|
+
status: z.array(DocumentStatusSchema).optional(),
|
|
352
|
+
priority: z.array(TaskPrioritySchema).optional(),
|
|
353
|
+
label: StringListSchema.optional(),
|
|
354
|
+
owner: StringListSchema.optional(),
|
|
355
|
+
assignee: StringListSchema.optional(),
|
|
356
|
+
reviewer: StringListSchema.optional(),
|
|
357
|
+
team: StringListSchema.optional(),
|
|
358
|
+
risk: z.array(TaskRiskSchema).optional(),
|
|
359
|
+
project: StringListSchema.optional(),
|
|
360
|
+
'depends-on': TaskIdSchema.optional(),
|
|
361
|
+
related: TaskIdSchema.optional(),
|
|
362
|
+
duplicate: TaskIdSchema.optional(),
|
|
363
|
+
parent: TaskIdSchema.optional(),
|
|
364
|
+
file: StringListSchema.optional(),
|
|
365
|
+
directory: StringListSchema.optional(),
|
|
366
|
+
'estimate-min': z.coerce.number().finite().nonnegative().optional(),
|
|
367
|
+
'estimate-max': z.coerce.number().finite().nonnegative().optional(),
|
|
368
|
+
'effort-min': z.coerce.number().finite().nonnegative().optional(),
|
|
369
|
+
'effort-max': z.coerce.number().finite().nonnegative().optional(),
|
|
370
|
+
'due-before': DocumentTimestampSchema.optional(),
|
|
371
|
+
'due-after': DocumentTimestampSchema.optional(),
|
|
372
|
+
'created-before': DocumentTimestampSchema.optional(),
|
|
373
|
+
'created-after': DocumentTimestampSchema.optional(),
|
|
374
|
+
'updated-before': DocumentTimestampSchema.optional(),
|
|
375
|
+
'updated-after': DocumentTimestampSchema.optional(),
|
|
376
|
+
search: z.string().optional(),
|
|
377
|
+
sort: z.enum(DOCUMENT_SORT_KEYS).optional(),
|
|
378
|
+
direction: z.enum(DOCUMENT_SORT_DIRECTIONS).optional(),
|
|
379
|
+
impact: z.boolean().optional(),
|
|
380
|
+
'include-derived': z.boolean().optional(),
|
|
381
|
+
cwd: CwdSchema,
|
|
382
|
+
json: JsonSchema,
|
|
383
|
+
})
|
|
384
|
+
|
|
385
|
+
const CreateValuesSchema = z.strictObject({
|
|
386
|
+
title: TrimmedStringSchema,
|
|
387
|
+
status: TaskStatusSchema.optional(),
|
|
388
|
+
priority: TaskPrioritySchema.optional(),
|
|
389
|
+
order: z.coerce.number().finite().nonnegative().optional(),
|
|
390
|
+
label: StringListSchema.optional(),
|
|
391
|
+
'depends-on': TaskIdListSchema.optional(),
|
|
392
|
+
file: StringListSchema.optional(),
|
|
393
|
+
owner: TrimmedStringSchema.optional(),
|
|
394
|
+
assignee: StringListSchema.optional(),
|
|
395
|
+
reviewer: StringListSchema.optional(),
|
|
396
|
+
team: TrimmedStringSchema.optional(),
|
|
397
|
+
estimate: z.coerce.number().int().nonnegative().optional(),
|
|
398
|
+
effort: z.coerce.number().finite().nonnegative().optional(),
|
|
399
|
+
risk: TaskRiskSchema.optional(),
|
|
400
|
+
'due-date': TaskTimestampSchema.optional(),
|
|
401
|
+
related: TaskIdListSchema.optional(),
|
|
402
|
+
duplicate: TaskIdListSchema.optional(),
|
|
403
|
+
parent: TaskIdSchema.optional(),
|
|
404
|
+
directory: StringListSchema.optional(),
|
|
405
|
+
project: StringListSchema.optional(),
|
|
406
|
+
body: z.string().optional(),
|
|
407
|
+
cwd: CwdSchema,
|
|
408
|
+
json: JsonSchema,
|
|
409
|
+
})
|
|
410
|
+
|
|
154
411
|
const UpdateValuesSchema = z
|
|
155
412
|
.strictObject({
|
|
156
413
|
title: TrimmedStringSchema.optional(),
|
|
@@ -342,6 +599,17 @@ function taskRecordJson(
|
|
|
342
599
|
}
|
|
343
600
|
}
|
|
344
601
|
|
|
602
|
+
function documentRecordJson(
|
|
603
|
+
record: DocumentRecord,
|
|
604
|
+
derived?: DerivedDocumentRelationships,
|
|
605
|
+
): Record<string, unknown> {
|
|
606
|
+
return {
|
|
607
|
+
relativePath: record.relativePath,
|
|
608
|
+
...record.document.metadata,
|
|
609
|
+
...(derived ? { derived } : {}),
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
|
|
345
613
|
function writeTaskRecord(
|
|
346
614
|
record: TaskRecord,
|
|
347
615
|
json: boolean | undefined,
|
|
@@ -354,6 +622,18 @@ function writeTaskRecord(
|
|
|
354
622
|
}
|
|
355
623
|
}
|
|
356
624
|
|
|
625
|
+
function writeDocumentRecord(
|
|
626
|
+
record: DocumentRecord,
|
|
627
|
+
json: boolean | undefined,
|
|
628
|
+
stdout: (value: string) => void,
|
|
629
|
+
): void {
|
|
630
|
+
if (json) {
|
|
631
|
+
stdout(`${JSON.stringify(documentRecordJson(record), null, 2)}\n`)
|
|
632
|
+
} else {
|
|
633
|
+
stdout(`${record.document.metadata.id}\n`)
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
|
|
357
637
|
function warningWriter(
|
|
358
638
|
stderr: (value: string) => void,
|
|
359
639
|
): (warning: { readonly message: string }) => void {
|
|
@@ -468,6 +748,107 @@ function updateInputFromValues(
|
|
|
468
748
|
}
|
|
469
749
|
}
|
|
470
750
|
|
|
751
|
+
function documentUpdateInputFromValues(
|
|
752
|
+
repository: Awaited<ReturnType<typeof discoverRepository>>,
|
|
753
|
+
values: z.infer<typeof DocumentUpdateValuesSchema>,
|
|
754
|
+
): UpdateDocumentInput {
|
|
755
|
+
return {
|
|
756
|
+
...(values.title !== undefined ? { title: values.title } : {}),
|
|
757
|
+
...(values.status !== undefined ? { status: values.status } : {}),
|
|
758
|
+
...(values['clear-priority']
|
|
759
|
+
? { priority: null }
|
|
760
|
+
: values.priority !== undefined
|
|
761
|
+
? { priority: values.priority }
|
|
762
|
+
: {}),
|
|
763
|
+
...(values['clear-order']
|
|
764
|
+
? { order: null }
|
|
765
|
+
: values.order !== undefined
|
|
766
|
+
? { order: values.order }
|
|
767
|
+
: {}),
|
|
768
|
+
...(values['clear-labels']
|
|
769
|
+
? { labels: [] }
|
|
770
|
+
: values.label !== undefined
|
|
771
|
+
? { labels: values.label }
|
|
772
|
+
: {}),
|
|
773
|
+
...(values['clear-dependencies']
|
|
774
|
+
? { dependsOn: [] }
|
|
775
|
+
: values['depends-on'] !== undefined
|
|
776
|
+
? { dependsOn: values['depends-on'] }
|
|
777
|
+
: {}),
|
|
778
|
+
...(values['clear-files']
|
|
779
|
+
? { files: [] }
|
|
780
|
+
: values.file !== undefined
|
|
781
|
+
? { files: normalizeMutationPaths(repository, values.file) }
|
|
782
|
+
: {}),
|
|
783
|
+
...(values['clear-owner']
|
|
784
|
+
? { owner: null }
|
|
785
|
+
: values.owner !== undefined
|
|
786
|
+
? { owner: values.owner }
|
|
787
|
+
: {}),
|
|
788
|
+
...(values['clear-assignees']
|
|
789
|
+
? { assignees: [] }
|
|
790
|
+
: values.assignee !== undefined
|
|
791
|
+
? { assignees: values.assignee }
|
|
792
|
+
: {}),
|
|
793
|
+
...(values['clear-reviewers']
|
|
794
|
+
? { reviewers: [] }
|
|
795
|
+
: values.reviewer !== undefined
|
|
796
|
+
? { reviewers: values.reviewer }
|
|
797
|
+
: {}),
|
|
798
|
+
...(values['clear-team']
|
|
799
|
+
? { team: null }
|
|
800
|
+
: values.team !== undefined
|
|
801
|
+
? { team: values.team }
|
|
802
|
+
: {}),
|
|
803
|
+
...(values['clear-estimate']
|
|
804
|
+
? { estimate: null }
|
|
805
|
+
: values.estimate !== undefined
|
|
806
|
+
? { estimate: values.estimate }
|
|
807
|
+
: {}),
|
|
808
|
+
...(values['clear-effort']
|
|
809
|
+
? { effort: null }
|
|
810
|
+
: values.effort !== undefined
|
|
811
|
+
? { effort: values.effort }
|
|
812
|
+
: {}),
|
|
813
|
+
...(values['clear-risk']
|
|
814
|
+
? { risk: null }
|
|
815
|
+
: values.risk !== undefined
|
|
816
|
+
? { risk: values.risk }
|
|
817
|
+
: {}),
|
|
818
|
+
...(values['clear-due-date']
|
|
819
|
+
? { dueDate: null }
|
|
820
|
+
: values['due-date'] !== undefined
|
|
821
|
+
? { dueDate: values['due-date'] }
|
|
822
|
+
: {}),
|
|
823
|
+
...(values['clear-related']
|
|
824
|
+
? { related: [] }
|
|
825
|
+
: values.related !== undefined
|
|
826
|
+
? { related: values.related }
|
|
827
|
+
: {}),
|
|
828
|
+
...(values['clear-duplicates']
|
|
829
|
+
? { duplicates: [] }
|
|
830
|
+
: values.duplicate !== undefined
|
|
831
|
+
? { duplicates: values.duplicate }
|
|
832
|
+
: {}),
|
|
833
|
+
...(values['clear-parent']
|
|
834
|
+
? { parent: null }
|
|
835
|
+
: values.parent !== undefined
|
|
836
|
+
? { parent: values.parent }
|
|
837
|
+
: {}),
|
|
838
|
+
...(values['clear-directories']
|
|
839
|
+
? { directories: [] }
|
|
840
|
+
: values.directory !== undefined
|
|
841
|
+
? { directories: normalizeMutationPaths(repository, values.directory) }
|
|
842
|
+
: {}),
|
|
843
|
+
...(values['clear-projects']
|
|
844
|
+
? { projects: [] }
|
|
845
|
+
: values.project !== undefined
|
|
846
|
+
? { projects: values.project }
|
|
847
|
+
: {}),
|
|
848
|
+
...(values.body !== undefined ? { body: values.body } : {}),
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
|
|
471
852
|
export async function runCli(args: readonly string[], context: CliContext = {}): Promise<number> {
|
|
472
853
|
const cwd = path.resolve(context.cwd ?? process.cwd())
|
|
473
854
|
const stdout = context.stdout ?? ((value: string) => process.stdout.write(value))
|
|
@@ -555,6 +936,33 @@ export async function runCli(args: readonly string[], context: CliContext = {}):
|
|
|
555
936
|
return 0
|
|
556
937
|
}
|
|
557
938
|
|
|
939
|
+
if (command === 'sync') {
|
|
940
|
+
const parsed = parseArgs({
|
|
941
|
+
args: commandArgs,
|
|
942
|
+
allowPositionals: false,
|
|
943
|
+
options: { ...commonOptionDefinitions, concurrency: { type: 'string' } },
|
|
944
|
+
})
|
|
945
|
+
const values = parseSchema(
|
|
946
|
+
z.strictObject({ ...CommonValuesSchema.shape, concurrency: ConcurrencySchema }),
|
|
947
|
+
parsed.values,
|
|
948
|
+
'sync options',
|
|
949
|
+
)
|
|
950
|
+
const repository = await discoverRepository(resolveCommandCwd(cwd, values.cwd))
|
|
951
|
+
const result = await syncRepository(repository, {
|
|
952
|
+
concurrency: values.concurrency,
|
|
953
|
+
onProgress: (progress) =>
|
|
954
|
+
stderr(
|
|
955
|
+
`sync ${progress.phase}: ${progress.completed}/${progress.total} (${progress.percent}%)\n`,
|
|
956
|
+
),
|
|
957
|
+
})
|
|
958
|
+
stdout(
|
|
959
|
+
values.json
|
|
960
|
+
? `${JSON.stringify(result, null, 2)}\n`
|
|
961
|
+
: `Synced ${result.migrations.length} migrations and generated views\n`,
|
|
962
|
+
)
|
|
963
|
+
return 0
|
|
964
|
+
}
|
|
965
|
+
|
|
558
966
|
if (command === 'snapshot') {
|
|
559
967
|
if (subcommand === 'create' || subcommand === 'list') {
|
|
560
968
|
const parsed = parseArgs({
|
|
@@ -625,10 +1033,436 @@ export async function runCli(args: readonly string[], context: CliContext = {}):
|
|
|
625
1033
|
throw new CliUsageError(`Unknown snapshot command "${subcommand ?? ''}"`)
|
|
626
1034
|
}
|
|
627
1035
|
|
|
1036
|
+
if (command === 'document' || command === 'doc') {
|
|
1037
|
+
if (subcommand === 'batch') {
|
|
1038
|
+
const parsed = parseArgs({
|
|
1039
|
+
args: subcommandArgs,
|
|
1040
|
+
allowPositionals: true,
|
|
1041
|
+
options: { ...commonOptionDefinitions, concurrency: { type: 'string' } },
|
|
1042
|
+
})
|
|
1043
|
+
const [manifestPath] = requirePositionals(
|
|
1044
|
+
parsed.positionals,
|
|
1045
|
+
1,
|
|
1046
|
+
'exactly one batch manifest path',
|
|
1047
|
+
)
|
|
1048
|
+
const values = parseSchema(
|
|
1049
|
+
z.strictObject({ ...CommonValuesSchema.shape, concurrency: ConcurrencySchema }),
|
|
1050
|
+
parsed.values,
|
|
1051
|
+
'document batch options',
|
|
1052
|
+
)
|
|
1053
|
+
const repository = await discoverRepository(resolveCommandCwd(cwd, values.cwd))
|
|
1054
|
+
const validatedManifestPath = parseSchema(
|
|
1055
|
+
z.string(),
|
|
1056
|
+
manifestPath,
|
|
1057
|
+
'document batch manifest path',
|
|
1058
|
+
)
|
|
1059
|
+
const raw = JSON.parse(
|
|
1060
|
+
await readFile(path.resolve(repository.rootDirectory, validatedManifestPath), 'utf8'),
|
|
1061
|
+
) as unknown
|
|
1062
|
+
const operations = parseSchema(
|
|
1063
|
+
DocumentBatchSchema,
|
|
1064
|
+
raw,
|
|
1065
|
+
'document batch manifest',
|
|
1066
|
+
) as readonly DocumentBatchOperation[]
|
|
1067
|
+
const results = await executeDocumentBatch(repository, operations, {
|
|
1068
|
+
concurrency: values.concurrency,
|
|
1069
|
+
onProgress: (progress) =>
|
|
1070
|
+
stderr(
|
|
1071
|
+
`document ${progress.action}: ${progress.completed}/${progress.total} (${progress.percent}%)\n`,
|
|
1072
|
+
),
|
|
1073
|
+
})
|
|
1074
|
+
stdout(
|
|
1075
|
+
values.json
|
|
1076
|
+
? `${JSON.stringify(results, null, 2)}\n`
|
|
1077
|
+
: `${results.map((result) => result.relativePath).join('\n')}\n`,
|
|
1078
|
+
)
|
|
1079
|
+
return 0
|
|
1080
|
+
}
|
|
1081
|
+
if (subcommand === 'create') {
|
|
1082
|
+
const parsed = parseArgs({
|
|
1083
|
+
args: subcommandArgs,
|
|
1084
|
+
allowPositionals: true,
|
|
1085
|
+
options: { ...metadataOptionDefinitions, ...commonOptionDefinitions },
|
|
1086
|
+
})
|
|
1087
|
+
const [rawType] = requirePositionals(parsed.positionals, 1, 'exactly one document type')
|
|
1088
|
+
const values = parseSchema(
|
|
1089
|
+
DocumentCreateValuesSchema,
|
|
1090
|
+
parsed.values,
|
|
1091
|
+
'document create options',
|
|
1092
|
+
)
|
|
1093
|
+
const repository = await discoverRepository(resolveCommandCwd(cwd, values.cwd))
|
|
1094
|
+
const record = await createDocument(
|
|
1095
|
+
repository,
|
|
1096
|
+
{
|
|
1097
|
+
type: normalizeDocumentKind(parseSchema(z.string(), rawType, 'document type')),
|
|
1098
|
+
title: values.title,
|
|
1099
|
+
...(values.status ? { status: values.status } : {}),
|
|
1100
|
+
...(values.priority ? { priority: values.priority } : {}),
|
|
1101
|
+
...(values.order !== undefined ? { order: values.order } : {}),
|
|
1102
|
+
...(values.label ? { labels: values.label } : {}),
|
|
1103
|
+
...(values['depends-on'] ? { dependsOn: values['depends-on'] } : {}),
|
|
1104
|
+
...(values.file ? { files: normalizeMutationPaths(repository, values.file) } : {}),
|
|
1105
|
+
...(values.owner ? { owner: values.owner } : {}),
|
|
1106
|
+
...(values.assignee ? { assignees: values.assignee } : {}),
|
|
1107
|
+
...(values.reviewer ? { reviewers: values.reviewer } : {}),
|
|
1108
|
+
...(values.team ? { team: values.team } : {}),
|
|
1109
|
+
...(values.estimate !== undefined ? { estimate: values.estimate } : {}),
|
|
1110
|
+
...(values.effort !== undefined ? { effort: values.effort } : {}),
|
|
1111
|
+
...(values.risk ? { risk: values.risk } : {}),
|
|
1112
|
+
...(values['due-date'] ? { dueDate: values['due-date'] } : {}),
|
|
1113
|
+
...(values.related ? { related: values.related } : {}),
|
|
1114
|
+
...(values.duplicate ? { duplicates: values.duplicate } : {}),
|
|
1115
|
+
...(values.parent ? { parent: values.parent } : {}),
|
|
1116
|
+
...(values.directory
|
|
1117
|
+
? { directories: normalizeMutationPaths(repository, values.directory) }
|
|
1118
|
+
: {}),
|
|
1119
|
+
...(values.project ? { projects: values.project } : {}),
|
|
1120
|
+
...(values.body !== undefined ? { body: values.body } : {}),
|
|
1121
|
+
},
|
|
1122
|
+
{ onWarning },
|
|
1123
|
+
)
|
|
1124
|
+
writeDocumentRecord(record, values.json, stdout)
|
|
1125
|
+
return 0
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
if (subcommand === 'import') {
|
|
1129
|
+
const parsed = parseArgs({
|
|
1130
|
+
args: subcommandArgs,
|
|
1131
|
+
allowPositionals: true,
|
|
1132
|
+
options: {
|
|
1133
|
+
...commonOptionDefinitions,
|
|
1134
|
+
type: { type: 'string' },
|
|
1135
|
+
title: { type: 'string' },
|
|
1136
|
+
move: { type: 'boolean' },
|
|
1137
|
+
},
|
|
1138
|
+
})
|
|
1139
|
+
const [sourcePath] = requirePositionals(parsed.positionals, 1, 'exactly one Markdown path')
|
|
1140
|
+
const values = parseSchema(
|
|
1141
|
+
z.strictObject({
|
|
1142
|
+
type: TrimmedStringSchema.optional(),
|
|
1143
|
+
title: TrimmedStringSchema.optional(),
|
|
1144
|
+
move: z.boolean().optional(),
|
|
1145
|
+
cwd: CwdSchema,
|
|
1146
|
+
json: JsonSchema,
|
|
1147
|
+
}),
|
|
1148
|
+
parsed.values,
|
|
1149
|
+
'document import options',
|
|
1150
|
+
)
|
|
1151
|
+
const repository = await discoverRepository(resolveCommandCwd(cwd, values.cwd))
|
|
1152
|
+
const record = await importDocument(
|
|
1153
|
+
repository,
|
|
1154
|
+
parseSchema(z.string(), sourcePath, 'document import path'),
|
|
1155
|
+
{
|
|
1156
|
+
...(values.type ? { type: normalizeDocumentKind(values.type) } : {}),
|
|
1157
|
+
...(values.title ? { title: values.title } : {}),
|
|
1158
|
+
move: values.move,
|
|
1159
|
+
},
|
|
1160
|
+
)
|
|
1161
|
+
stdout(values.json ? `${JSON.stringify(record, null, 2)}\n` : `${record.relativePath}\n`)
|
|
1162
|
+
return 0
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
if (subcommand === 'list') {
|
|
1166
|
+
const parsed = parseArgs({
|
|
1167
|
+
args: subcommandArgs,
|
|
1168
|
+
allowPositionals: true,
|
|
1169
|
+
options: {
|
|
1170
|
+
cwd: { type: 'string' },
|
|
1171
|
+
json: { type: 'boolean' },
|
|
1172
|
+
status: { type: 'string', multiple: true },
|
|
1173
|
+
priority: { type: 'string', multiple: true },
|
|
1174
|
+
label: { type: 'string', multiple: true },
|
|
1175
|
+
owner: { type: 'string', multiple: true },
|
|
1176
|
+
assignee: { type: 'string', multiple: true },
|
|
1177
|
+
reviewer: { type: 'string', multiple: true },
|
|
1178
|
+
team: { type: 'string', multiple: true },
|
|
1179
|
+
risk: { type: 'string', multiple: true },
|
|
1180
|
+
project: { type: 'string', multiple: true },
|
|
1181
|
+
'depends-on': { type: 'string' },
|
|
1182
|
+
related: { type: 'string' },
|
|
1183
|
+
duplicate: { type: 'string' },
|
|
1184
|
+
parent: { type: 'string' },
|
|
1185
|
+
file: { type: 'string', multiple: true },
|
|
1186
|
+
directory: { type: 'string', multiple: true },
|
|
1187
|
+
'estimate-min': { type: 'string' },
|
|
1188
|
+
'estimate-max': { type: 'string' },
|
|
1189
|
+
'effort-min': { type: 'string' },
|
|
1190
|
+
'effort-max': { type: 'string' },
|
|
1191
|
+
'due-before': { type: 'string' },
|
|
1192
|
+
'due-after': { type: 'string' },
|
|
1193
|
+
'created-before': { type: 'string' },
|
|
1194
|
+
'created-after': { type: 'string' },
|
|
1195
|
+
'updated-before': { type: 'string' },
|
|
1196
|
+
'updated-after': { type: 'string' },
|
|
1197
|
+
search: { type: 'string' },
|
|
1198
|
+
sort: { type: 'string' },
|
|
1199
|
+
direction: { type: 'string' },
|
|
1200
|
+
impact: { type: 'boolean' },
|
|
1201
|
+
'include-derived': { type: 'boolean' },
|
|
1202
|
+
},
|
|
1203
|
+
})
|
|
1204
|
+
if (parsed.positionals.length > 1)
|
|
1205
|
+
throw new CliUsageError('Expected at most one document type')
|
|
1206
|
+
const values = parseSchema(DocumentListValuesSchema, parsed.values, 'document list options')
|
|
1207
|
+
const typeFilter = parsed.positionals[0]
|
|
1208
|
+
? normalizeDocumentKind(parsed.positionals[0])
|
|
1209
|
+
: undefined
|
|
1210
|
+
const query = parseSchema(
|
|
1211
|
+
DocumentQuerySchema,
|
|
1212
|
+
{
|
|
1213
|
+
...(typeFilter ? { types: [typeFilter] } : {}),
|
|
1214
|
+
...(values.status ? { statuses: values.status } : {}),
|
|
1215
|
+
...(values.priority ? { priorities: values.priority } : {}),
|
|
1216
|
+
...(values.label ? { labels: values.label } : {}),
|
|
1217
|
+
...(values.owner ? { owners: values.owner } : {}),
|
|
1218
|
+
...(values.assignee ? { assignees: values.assignee } : {}),
|
|
1219
|
+
...(values.reviewer ? { reviewers: values.reviewer } : {}),
|
|
1220
|
+
...(values.team ? { teams: values.team } : {}),
|
|
1221
|
+
...(values.risk ? { risks: values.risk } : {}),
|
|
1222
|
+
...(values.project ? { projects: values.project } : {}),
|
|
1223
|
+
...(values['depends-on'] ? { dependsOn: values['depends-on'] } : {}),
|
|
1224
|
+
...(values.related ? { related: values.related } : {}),
|
|
1225
|
+
...(values.duplicate ? { duplicate: values.duplicate } : {}),
|
|
1226
|
+
...(values.parent ? { parent: values.parent } : {}),
|
|
1227
|
+
...(values.file ? { files: values.file } : {}),
|
|
1228
|
+
...(values.directory ? { directories: values.directory } : {}),
|
|
1229
|
+
...(values['estimate-min'] !== undefined
|
|
1230
|
+
? { estimateMin: values['estimate-min'] }
|
|
1231
|
+
: {}),
|
|
1232
|
+
...(values['estimate-max'] !== undefined
|
|
1233
|
+
? { estimateMax: values['estimate-max'] }
|
|
1234
|
+
: {}),
|
|
1235
|
+
...(values['effort-min'] !== undefined ? { effortMin: values['effort-min'] } : {}),
|
|
1236
|
+
...(values['effort-max'] !== undefined ? { effortMax: values['effort-max'] } : {}),
|
|
1237
|
+
...(values['due-before'] ? { dueBefore: values['due-before'] } : {}),
|
|
1238
|
+
...(values['due-after'] ? { dueAfter: values['due-after'] } : {}),
|
|
1239
|
+
...(values['created-before'] ? { createdBefore: values['created-before'] } : {}),
|
|
1240
|
+
...(values['created-after'] ? { createdAfter: values['created-after'] } : {}),
|
|
1241
|
+
...(values['updated-before'] ? { updatedBefore: values['updated-before'] } : {}),
|
|
1242
|
+
...(values['updated-after'] ? { updatedAfter: values['updated-after'] } : {}),
|
|
1243
|
+
...(values.search !== undefined ? { text: values.search } : {}),
|
|
1244
|
+
...(values.sort ? { sortBy: values.sort } : {}),
|
|
1245
|
+
...(values.direction ? { direction: values.direction } : {}),
|
|
1246
|
+
...(values.impact ? { impact: true } : {}),
|
|
1247
|
+
} satisfies DocumentQuery,
|
|
1248
|
+
'document list query',
|
|
1249
|
+
)
|
|
1250
|
+
const repository = await discoverRepository(resolveCommandCwd(cwd, values.cwd))
|
|
1251
|
+
const result = await queryDocuments(repository, query)
|
|
1252
|
+
const derivedGraph = values['include-derived']
|
|
1253
|
+
? buildDocumentGraph(await listDocuments(repository))
|
|
1254
|
+
: undefined
|
|
1255
|
+
const serialize = (record: DocumentRecord) =>
|
|
1256
|
+
documentRecordJson(record, derivedGraph?.derive(record.document.metadata.id))
|
|
1257
|
+
|
|
1258
|
+
if (values.json) {
|
|
1259
|
+
stdout(
|
|
1260
|
+
`${JSON.stringify(
|
|
1261
|
+
values.impact
|
|
1262
|
+
? {
|
|
1263
|
+
direct: result.direct.map(serialize),
|
|
1264
|
+
impacted: result.impacted.map(serialize),
|
|
1265
|
+
}
|
|
1266
|
+
: result.direct.map(serialize),
|
|
1267
|
+
null,
|
|
1268
|
+
2,
|
|
1269
|
+
)}\n`,
|
|
1270
|
+
)
|
|
1271
|
+
} else {
|
|
1272
|
+
for (const record of result.direct) {
|
|
1273
|
+
stdout(
|
|
1274
|
+
`${values.impact ? 'direct\t' : ''}${record.document.metadata.id}\t${record.document.metadata.type}\t${record.document.metadata.status}\t${record.document.metadata.title}\n`,
|
|
1275
|
+
)
|
|
1276
|
+
}
|
|
1277
|
+
for (const record of result.impacted) {
|
|
1278
|
+
stdout(
|
|
1279
|
+
`impact\t${record.document.metadata.id}\t${record.document.metadata.type}\t${record.document.metadata.status}\t${record.document.metadata.title}\n`,
|
|
1280
|
+
)
|
|
1281
|
+
}
|
|
1282
|
+
}
|
|
1283
|
+
return 0
|
|
1284
|
+
}
|
|
1285
|
+
|
|
1286
|
+
if (subcommand === 'show') {
|
|
1287
|
+
const parsed = parseArgs({
|
|
1288
|
+
args: subcommandArgs,
|
|
1289
|
+
allowPositionals: true,
|
|
1290
|
+
options: {
|
|
1291
|
+
...commonOptionDefinitions,
|
|
1292
|
+
type: { type: 'string' },
|
|
1293
|
+
'include-derived': { type: 'boolean' },
|
|
1294
|
+
},
|
|
1295
|
+
})
|
|
1296
|
+
const [id] = requirePositionals(parsed.positionals, 1, 'exactly one document ID')
|
|
1297
|
+
const values = parseSchema(
|
|
1298
|
+
z.strictObject({
|
|
1299
|
+
...CommonValuesSchema.shape,
|
|
1300
|
+
type: TrimmedStringSchema.optional(),
|
|
1301
|
+
'include-derived': z.boolean().optional(),
|
|
1302
|
+
}),
|
|
1303
|
+
parsed.values,
|
|
1304
|
+
'document show options',
|
|
1305
|
+
)
|
|
1306
|
+
const repository = await discoverRepository(resolveCommandCwd(cwd, values.cwd))
|
|
1307
|
+
const validatedId = parseSchema(DocumentIdSchema, id, 'document ID')
|
|
1308
|
+
const record = await readDocument(
|
|
1309
|
+
repository,
|
|
1310
|
+
validatedId,
|
|
1311
|
+
values.type ? normalizeDocumentKind(values.type) : undefined,
|
|
1312
|
+
)
|
|
1313
|
+
|
|
1314
|
+
if (values.json) {
|
|
1315
|
+
const derived = values['include-derived']
|
|
1316
|
+
? buildDocumentGraph(await listDocuments(repository)).derive(validatedId)
|
|
1317
|
+
: undefined
|
|
1318
|
+
stdout(
|
|
1319
|
+
`${JSON.stringify(
|
|
1320
|
+
{
|
|
1321
|
+
relativePath: record.relativePath,
|
|
1322
|
+
metadata: record.document.metadata,
|
|
1323
|
+
body: record.document.body,
|
|
1324
|
+
...(derived ? { derived } : {}),
|
|
1325
|
+
},
|
|
1326
|
+
null,
|
|
1327
|
+
2,
|
|
1328
|
+
)}\n`,
|
|
1329
|
+
)
|
|
1330
|
+
} else {
|
|
1331
|
+
stdout(serializeDocumentFile(record.document))
|
|
1332
|
+
}
|
|
1333
|
+
return 0
|
|
1334
|
+
}
|
|
1335
|
+
|
|
1336
|
+
if (subcommand === 'update') {
|
|
1337
|
+
const clearDefinitions = Object.fromEntries(
|
|
1338
|
+
UPDATE_CLEAR_PAIRS.map(([, clear]) => [clear, { type: 'boolean' as const }]),
|
|
1339
|
+
)
|
|
1340
|
+
const parsed = parseArgs({
|
|
1341
|
+
args: subcommandArgs,
|
|
1342
|
+
allowPositionals: true,
|
|
1343
|
+
options: {
|
|
1344
|
+
...metadataOptionDefinitions,
|
|
1345
|
+
...clearDefinitions,
|
|
1346
|
+
...commonOptionDefinitions,
|
|
1347
|
+
type: { type: 'string' },
|
|
1348
|
+
},
|
|
1349
|
+
})
|
|
1350
|
+
const [documentId] = requirePositionals(parsed.positionals, 1, 'exactly one document ID')
|
|
1351
|
+
const validatedDocumentId = parseSchema(DocumentIdSchema, documentId, 'document ID')
|
|
1352
|
+
const values = parseSchema(
|
|
1353
|
+
DocumentUpdateValuesSchema,
|
|
1354
|
+
parsed.values,
|
|
1355
|
+
'document update options',
|
|
1356
|
+
)
|
|
1357
|
+
const repository = await discoverRepository(resolveCommandCwd(cwd, values.cwd))
|
|
1358
|
+
const record = await updateDocument(
|
|
1359
|
+
repository,
|
|
1360
|
+
validatedDocumentId,
|
|
1361
|
+
documentUpdateInputFromValues(repository, values),
|
|
1362
|
+
values.type ? normalizeDocumentKind(values.type) : undefined,
|
|
1363
|
+
{ onWarning },
|
|
1364
|
+
)
|
|
1365
|
+
writeDocumentRecord(record, values.json, stdout)
|
|
1366
|
+
return 0
|
|
1367
|
+
}
|
|
1368
|
+
|
|
1369
|
+
if (subcommand === 'status') {
|
|
1370
|
+
const parsed = parseArgs({
|
|
1371
|
+
args: subcommandArgs,
|
|
1372
|
+
allowPositionals: true,
|
|
1373
|
+
options: { ...commonOptionDefinitions, type: { type: 'string' } },
|
|
1374
|
+
})
|
|
1375
|
+
const [documentId, status] = requirePositionals(
|
|
1376
|
+
parsed.positionals,
|
|
1377
|
+
2,
|
|
1378
|
+
'a document ID and status',
|
|
1379
|
+
)
|
|
1380
|
+
const validatedDocumentId = parseSchema(DocumentIdSchema, documentId, 'document ID')
|
|
1381
|
+
const validatedStatus = parseSchema(DocumentStatusSchema, status, 'document status')
|
|
1382
|
+
const values = parseSchema(
|
|
1383
|
+
z.strictObject({ ...CommonValuesSchema.shape, type: TrimmedStringSchema.optional() }),
|
|
1384
|
+
parsed.values,
|
|
1385
|
+
'document status options',
|
|
1386
|
+
)
|
|
1387
|
+
const repository = await discoverRepository(resolveCommandCwd(cwd, values.cwd))
|
|
1388
|
+
const record = await updateDocument(
|
|
1389
|
+
repository,
|
|
1390
|
+
validatedDocumentId,
|
|
1391
|
+
{ status: validatedStatus },
|
|
1392
|
+
values.type ? normalizeDocumentKind(values.type) : undefined,
|
|
1393
|
+
{ onWarning },
|
|
1394
|
+
)
|
|
1395
|
+
writeDocumentRecord(record, values.json, stdout)
|
|
1396
|
+
return 0
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1399
|
+
if (subcommand === 'delete') {
|
|
1400
|
+
const parsed = parseArgs({
|
|
1401
|
+
args: subcommandArgs,
|
|
1402
|
+
allowPositionals: true,
|
|
1403
|
+
options: {
|
|
1404
|
+
...commonOptionDefinitions,
|
|
1405
|
+
type: { type: 'string' },
|
|
1406
|
+
'remove-dependencies': { type: 'boolean' },
|
|
1407
|
+
},
|
|
1408
|
+
})
|
|
1409
|
+
const [documentId] = requirePositionals(parsed.positionals, 1, 'exactly one document ID')
|
|
1410
|
+
const validatedDocumentId = parseSchema(DocumentIdSchema, documentId, 'document ID')
|
|
1411
|
+
const values = parseSchema(
|
|
1412
|
+
z.strictObject({
|
|
1413
|
+
cwd: CwdSchema,
|
|
1414
|
+
json: JsonSchema,
|
|
1415
|
+
type: TrimmedStringSchema.optional(),
|
|
1416
|
+
'remove-dependencies': z.boolean().optional(),
|
|
1417
|
+
}),
|
|
1418
|
+
parsed.values,
|
|
1419
|
+
'document delete options',
|
|
1420
|
+
)
|
|
1421
|
+
const repository = await discoverRepository(resolveCommandCwd(cwd, values.cwd))
|
|
1422
|
+
const record = await deleteDocument(
|
|
1423
|
+
repository,
|
|
1424
|
+
validatedDocumentId,
|
|
1425
|
+
values.type ? normalizeDocumentKind(values.type) : undefined,
|
|
1426
|
+
{
|
|
1427
|
+
removeDependencies: values['remove-dependencies'],
|
|
1428
|
+
onWarning,
|
|
1429
|
+
},
|
|
1430
|
+
)
|
|
1431
|
+
|
|
1432
|
+
if (values.json) {
|
|
1433
|
+
stdout(`${JSON.stringify({ deleted: true, ...documentRecordJson(record) }, null, 2)}\n`)
|
|
1434
|
+
} else {
|
|
1435
|
+
stdout(`${validatedDocumentId}\n`)
|
|
1436
|
+
}
|
|
1437
|
+
return 0
|
|
1438
|
+
}
|
|
1439
|
+
|
|
1440
|
+
throw new CliUsageError(`Unknown document command "${subcommand ?? ''}"`)
|
|
1441
|
+
}
|
|
1442
|
+
|
|
628
1443
|
if (command !== 'task') {
|
|
629
1444
|
throw new CliUsageError(`Unknown command "${command}"`)
|
|
630
1445
|
}
|
|
631
1446
|
|
|
1447
|
+
if (subcommand === 'migrate-ids') {
|
|
1448
|
+
const parsed = parseArgs({
|
|
1449
|
+
args: subcommandArgs,
|
|
1450
|
+
allowPositionals: false,
|
|
1451
|
+
options: commonOptionDefinitions,
|
|
1452
|
+
})
|
|
1453
|
+
const values = parseSchema(CommonValuesSchema, parsed.values, 'task migrate-ids options')
|
|
1454
|
+
const repository = await discoverRepository(resolveCommandCwd(cwd, values.cwd))
|
|
1455
|
+
const migrations = await migrateTaskIds(repository, {
|
|
1456
|
+
onProgress: (progress) =>
|
|
1457
|
+
stderr(
|
|
1458
|
+
`migration ${progress.phase}: ${progress.completed}/${progress.total} (${progress.percent}%)\n`,
|
|
1459
|
+
),
|
|
1460
|
+
})
|
|
1461
|
+
if (values.json) stdout(`${JSON.stringify(migrations, null, 2)}\n`)
|
|
1462
|
+
else for (const migration of migrations) stdout(`${migration.from}\t${migration.to}\n`)
|
|
1463
|
+
return 0
|
|
1464
|
+
}
|
|
1465
|
+
|
|
632
1466
|
if (subcommand === 'create') {
|
|
633
1467
|
const parsed = parseArgs({
|
|
634
1468
|
args: subcommandArgs,
|