@dinoxx/dinox-cli 1.0.17 → 1.0.19
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 +72 -34
- package/dist/cli.js +15 -1
- package/dist/commands/auth/index.d.ts +2 -0
- package/dist/commands/auth/index.js +209 -163
- package/dist/commands/boxes/index.d.ts +2 -0
- package/dist/commands/boxes/index.js +119 -75
- package/dist/commands/boxes/repo.d.ts +8 -0
- package/dist/commands/boxes/repo.js +41 -0
- package/dist/commands/config/index.d.ts +2 -0
- package/dist/commands/config/index.js +85 -36
- package/dist/commands/descriptor/register.d.ts +8 -0
- package/dist/commands/descriptor/register.js +113 -0
- package/dist/commands/descriptor/types.d.ts +37 -0
- package/dist/commands/descriptor/types.js +17 -0
- package/dist/commands/graph/index.d.ts +2 -0
- package/dist/commands/graph/index.js +143 -73
- package/dist/commands/info/index.d.ts +2 -0
- package/dist/commands/info/index.js +41 -14
- package/dist/commands/mcp/index.js +11 -10
- package/dist/commands/mcp/server.js +6 -3
- package/dist/commands/mcp/tools.js +30 -104
- package/dist/commands/mcp/types.d.ts +8 -1
- package/dist/commands/meta/index.d.ts +2 -0
- package/dist/commands/meta/index.js +64 -33
- package/dist/commands/meta/repo.d.ts +6 -0
- package/dist/commands/meta/repo.js +6 -0
- package/dist/commands/notes/actions.d.ts +10 -0
- package/dist/commands/notes/actions.js +403 -0
- package/dist/commands/notes/descriptors.d.ts +4 -0
- package/dist/commands/notes/descriptors.js +250 -0
- package/dist/commands/notes/dryRun.d.ts +31 -0
- package/dist/commands/notes/dryRun.js +130 -0
- package/dist/commands/notes/index.d.ts +2 -51
- package/dist/commands/notes/index.js +2 -581
- package/dist/commands/notes/repo.d.ts +9 -0
- package/dist/commands/notes/repo.js +56 -10
- package/dist/commands/notes/runtime.d.ts +28 -0
- package/dist/commands/notes/runtime.js +185 -0
- package/dist/commands/notes/searchOutput.d.ts +10 -2
- package/dist/commands/notes/searchOutput.js +14 -2
- package/dist/commands/notes/searchResponse.d.ts +51 -0
- package/dist/commands/notes/searchResponse.js +56 -0
- package/dist/commands/notes/searchSql.js +2 -0
- package/dist/commands/prompt/index.d.ts +2 -0
- package/dist/commands/prompt/index.js +122 -72
- package/dist/commands/prompt/repo.d.ts +7 -0
- package/dist/commands/prompt/repo.js +46 -0
- package/dist/commands/schema/commandMetadata.d.ts +2 -0
- package/dist/commands/schema/commandMetadata.js +18 -0
- package/dist/commands/schema/globalOptions.d.ts +3 -0
- package/dist/commands/schema/globalOptions.js +3 -0
- package/dist/commands/schema/index.d.ts +4 -0
- package/dist/commands/schema/index.js +109 -0
- package/dist/commands/schema/renderSkills.d.ts +6 -0
- package/dist/commands/schema/renderSkills.js +216 -0
- package/dist/commands/schema/spec.d.ts +6 -0
- package/dist/commands/schema/spec.js +40 -0
- package/dist/commands/schema/types.d.ts +47 -0
- package/dist/commands/schema/types.js +1 -0
- package/dist/commands/storage/index.d.ts +4 -0
- package/dist/commands/storage/index.js +292 -0
- package/dist/commands/storage/repo.d.ts +162 -0
- package/dist/commands/storage/repo.js +751 -0
- package/dist/commands/sync.d.ts +2 -0
- package/dist/commands/sync.js +41 -27
- package/dist/commands/tags/index.d.ts +2 -0
- package/dist/commands/tags/index.js +121 -79
- package/dist/commands/tags/repo.d.ts +7 -0
- package/dist/commands/tags/repo.js +52 -0
- package/dist/commands/todo/dryRun.d.ts +23 -0
- package/dist/commands/todo/dryRun.js +133 -0
- package/dist/commands/todo/index.d.ts +2 -0
- package/dist/commands/todo/index.js +285 -135
- package/dist/commands/todo/repo.d.ts +11 -0
- package/dist/commands/todo/repo.js +37 -9
- package/dist/commands/update.d.ts +3 -0
- package/dist/commands/update.js +35 -13
- package/dist/commands/writeDryRun.d.ts +62 -0
- package/dist/commands/writeDryRun.js +131 -0
- package/dist/constants/links.d.ts +1 -1
- package/dist/constants/links.js +1 -1
- package/dist/daemon/ensure.js +3 -2
- package/dist/daemon/index.js +17 -15
- package/dist/dinox.js +9 -13
- package/dist/powersync/schema/index.d.ts +27 -0
- package/dist/powersync/schema/index.js +2 -0
- package/dist/powersync/schema/note.d.ts +1 -0
- package/dist/powersync/schema/note.js +1 -0
- package/dist/powersync/schema/projects.d.ts +3 -0
- package/dist/powersync/schema/projects.js +3 -0
- package/dist/powersync/schema/storage.d.ts +24 -0
- package/dist/powersync/schema/storage.js +24 -0
- package/dist/skills/check.d.ts +6 -0
- package/dist/skills/check.js +254 -0
- package/dist/utils/bidlinkCanonicalization.d.ts +2 -0
- package/dist/utils/bidlinkCanonicalization.js +161 -0
- package/dist/utils/dryRun.d.ts +13 -0
- package/dist/utils/dryRun.js +69 -0
- package/dist/utils/output.d.ts +19 -0
- package/dist/utils/output.js +155 -1
- package/dist/utils/terminalSanitization.d.ts +2 -0
- package/dist/utils/terminalSanitization.js +33 -0
- package/dist/utils/tiptapMarkdown.d.ts +4 -1
- package/dist/utils/tiptapMarkdown.js +285 -113
- package/dist/utils/tiptapMarkdownSchema.d.ts +37 -0
- package/dist/utils/tiptapMarkdownSchema.js +577 -0
- package/dist/utils/tiptapMarkdownSchemaUtils.d.ts +74 -0
- package/dist/utils/tiptapMarkdownSchemaUtils.js +394 -0
- package/dist/utils/updateNotice.d.ts +7 -0
- package/dist/utils/updateNotice.js +64 -0
- package/package.json +25 -3
|
@@ -1,11 +1,15 @@
|
|
|
1
|
+
import { resolveAuthIdentity } from '../../auth/userInfo.js';
|
|
2
|
+
import { registerCommandGroup } from '../descriptor/register.js';
|
|
3
|
+
import { FORMAT_OPTIONS, OFFLINE_OPTIONS, SYNC_TIMEOUT_OPTIONS } from '../schema/globalOptions.js';
|
|
1
4
|
import { loadConfig } from '../../config/store.js';
|
|
2
5
|
import { resolveConfig } from '../../config/resolve.js';
|
|
3
|
-
import {
|
|
6
|
+
import { connectPowerSync, waitForIdleDataFlow } from '../../powersync/runtime.js';
|
|
4
7
|
import { readStringOrFile } from '../../utils/argValue.js';
|
|
5
|
-
import {
|
|
8
|
+
import { printDryRunPreview } from '../../utils/dryRun.js';
|
|
9
|
+
import { DinoxError } from '../../utils/errors.js';
|
|
10
|
+
import { printHumanReadableLine, printSuccess } from '../../utils/output.js';
|
|
6
11
|
import { parseSyncTimeoutMs } from '../../utils/syncTimeout.js';
|
|
7
|
-
import {
|
|
8
|
-
import { resolveAuthIdentity } from '../../auth/userInfo.js';
|
|
12
|
+
import { buildTodoAppendDryRunPreview, buildTodoCreateDryRunPreview, buildTodoUpdateDryRunPreview, } from './dryRun.js';
|
|
9
13
|
import { appendTodoTasks, createTodoNote, normalizeTaskTexts, searchTodoTasks, updateTodoTaskStatus, } from './repo.js';
|
|
10
14
|
const DATE_ONLY_RE = /^(\d{4})-(\d{2})-(\d{2})$/;
|
|
11
15
|
const DAY_MS = 24 * 60 * 60 * 1000;
|
|
@@ -13,6 +17,7 @@ const DEFAULT_TASK_LIMIT = 50;
|
|
|
13
17
|
const DEFAULT_NOTE_SCAN_LIMIT = 200;
|
|
14
18
|
const MAX_TASK_LIMIT = 500;
|
|
15
19
|
const MAX_NOTE_SCAN_LIMIT = 5000;
|
|
20
|
+
const TODO_GLOBAL_OPTIONS = [...FORMAT_OPTIONS, ...OFFLINE_OPTIONS, ...SYNC_TIMEOUT_OPTIONS];
|
|
16
21
|
function parseTaskStatus(value) {
|
|
17
22
|
const normalized = typeof value === 'string' ? value.trim().toLowerCase() : '';
|
|
18
23
|
if (normalized === 'all' || normalized === 'completed' || normalized === 'uncompleted') {
|
|
@@ -170,8 +175,8 @@ async function parseTaskListInput(value) {
|
|
|
170
175
|
const raw = await readStringOrFile(value);
|
|
171
176
|
return parseListInput(raw, '--tasks');
|
|
172
177
|
}
|
|
173
|
-
function collectStringOptionValue(value, previous
|
|
174
|
-
return [...previous, value];
|
|
178
|
+
function collectStringOptionValue(value, previous) {
|
|
179
|
+
return [...(Array.isArray(previous) ? previous : []), value];
|
|
175
180
|
}
|
|
176
181
|
async function parseTaskInput(args) {
|
|
177
182
|
const merged = [];
|
|
@@ -193,148 +198,293 @@ async function syncBeforeTodoCommand(db, offline, timeoutMs) {
|
|
|
193
198
|
}
|
|
194
199
|
await waitForIdleDataFlow(db, Math.min(timeoutMs, 10_000));
|
|
195
200
|
}
|
|
196
|
-
|
|
197
|
-
const
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
finally {
|
|
241
|
-
await db.close().catch(() => undefined);
|
|
242
|
-
}
|
|
201
|
+
async function withTodoDatabase(globals, run) {
|
|
202
|
+
const rawConfig = await loadConfig();
|
|
203
|
+
const config = resolveConfig(rawConfig);
|
|
204
|
+
const timeoutMs = parseSyncTimeoutMs(globals.syncTimeout) ?? config.sync.timeoutMs;
|
|
205
|
+
const offline = Boolean(globals.offline);
|
|
206
|
+
const { db } = await connectPowerSync({ config, offline, timeoutMs });
|
|
207
|
+
try {
|
|
208
|
+
await run({
|
|
209
|
+
db,
|
|
210
|
+
rawConfig,
|
|
211
|
+
config,
|
|
212
|
+
offline,
|
|
213
|
+
timeoutMs,
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
finally {
|
|
217
|
+
await db.close().catch(() => undefined);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
async function runTodoSearch({ args, globals, options }) {
|
|
221
|
+
const searchOptions = options;
|
|
222
|
+
await withTodoDatabase(globals, async ({ db, offline, timeoutMs }) => {
|
|
223
|
+
await syncBeforeTodoCommand(db, offline, timeoutMs);
|
|
224
|
+
const keyword = args.query?.trim() ?? '';
|
|
225
|
+
const taskStatus = parseTaskStatus(searchOptions.status);
|
|
226
|
+
const taskTagFilter = typeof searchOptions.tags === 'string'
|
|
227
|
+
? await parseTagFilterInput(searchOptions.tags)
|
|
228
|
+
: [];
|
|
229
|
+
const taskDateRange = parseTaskDateRange({
|
|
230
|
+
from: searchOptions.from,
|
|
231
|
+
to: searchOptions.to,
|
|
232
|
+
days: searchOptions.days,
|
|
233
|
+
});
|
|
234
|
+
const limit = parsePositiveIntegerOption(searchOptions.limit, '--limit', DEFAULT_TASK_LIMIT, MAX_TASK_LIMIT);
|
|
235
|
+
const scanLimit = parsePositiveIntegerOption(searchOptions.scanLimit, '--scan-limit', DEFAULT_NOTE_SCAN_LIMIT, MAX_NOTE_SCAN_LIMIT);
|
|
236
|
+
const result = await searchTodoTasks(db, keyword, {
|
|
237
|
+
includeDeleted: Boolean(searchOptions.includeDeleted),
|
|
238
|
+
taskStatus,
|
|
239
|
+
taskTagFilter,
|
|
240
|
+
taskDateRange,
|
|
241
|
+
limit,
|
|
242
|
+
scanLimit,
|
|
243
|
+
});
|
|
244
|
+
printSuccess(result);
|
|
243
245
|
});
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
positionalTask: taskArg,
|
|
261
|
-
tasksOption: typeof options.tasks === 'string' ? options.tasks : undefined,
|
|
262
|
-
taskOptionList: Array.isArray(options.task) ? options.task : [],
|
|
263
|
-
});
|
|
264
|
-
const result = await appendTodoTasks(db, {
|
|
265
|
-
noteId: typeof options.noteId === 'string' ? options.noteId : undefined,
|
|
246
|
+
}
|
|
247
|
+
async function runTodoAppend({ args, globals, options }) {
|
|
248
|
+
const appendOptions = options;
|
|
249
|
+
await withTodoDatabase(globals, async ({ db, config, offline, timeoutMs }) => {
|
|
250
|
+
await syncBeforeTodoCommand(db, offline, timeoutMs);
|
|
251
|
+
const taskTexts = await parseTaskInput({
|
|
252
|
+
positionalTask: args.task,
|
|
253
|
+
tasksOption: typeof appendOptions.tasks === 'string' ? appendOptions.tasks : undefined,
|
|
254
|
+
taskOptionList: Array.isArray(appendOptions.task) ? appendOptions.task : [],
|
|
255
|
+
});
|
|
256
|
+
if (Boolean(appendOptions.dryRun)) {
|
|
257
|
+
const preview = await buildTodoAppendDryRunPreview({
|
|
258
|
+
db,
|
|
259
|
+
config,
|
|
260
|
+
offline,
|
|
261
|
+
noteId: typeof appendOptions.noteId === 'string' ? appendOptions.noteId : undefined,
|
|
266
262
|
taskTexts,
|
|
267
263
|
});
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
finally {
|
|
271
|
-
await db.close().catch(() => undefined);
|
|
264
|
+
printDryRunPreview(preview);
|
|
265
|
+
return;
|
|
272
266
|
}
|
|
267
|
+
const result = await appendTodoTasks(db, {
|
|
268
|
+
noteId: typeof appendOptions.noteId === 'string' ? appendOptions.noteId : undefined,
|
|
269
|
+
taskTexts,
|
|
270
|
+
});
|
|
271
|
+
printSuccess({ ok: true, ...result });
|
|
273
272
|
});
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
try {
|
|
290
|
-
await syncBeforeTodoCommand(db, offline, timeoutMs);
|
|
291
|
-
const taskTexts = await parseTaskInput({
|
|
292
|
-
positionalTask: taskArg,
|
|
293
|
-
tasksOption: typeof options.tasks === 'string' ? options.tasks : undefined,
|
|
294
|
-
taskOptionList: Array.isArray(options.task) ? options.task : [],
|
|
295
|
-
});
|
|
296
|
-
const result = await createTodoNote(db, {
|
|
273
|
+
}
|
|
274
|
+
async function runTodoCreate({ args, globals, options }) {
|
|
275
|
+
const createOptions = options;
|
|
276
|
+
await withTodoDatabase(globals, async ({ db, rawConfig, config, offline, timeoutMs }) => {
|
|
277
|
+
await syncBeforeTodoCommand(db, offline, timeoutMs);
|
|
278
|
+
const taskTexts = await parseTaskInput({
|
|
279
|
+
positionalTask: args.task,
|
|
280
|
+
tasksOption: typeof createOptions.tasks === 'string' ? createOptions.tasks : undefined,
|
|
281
|
+
taskOptionList: Array.isArray(createOptions.task) ? createOptions.task : [],
|
|
282
|
+
});
|
|
283
|
+
if (Boolean(createOptions.dryRun)) {
|
|
284
|
+
const preview = await buildTodoCreateDryRunPreview({
|
|
285
|
+
config,
|
|
286
|
+
offline,
|
|
287
|
+
title: typeof createOptions.title === 'string' ? createOptions.title : undefined,
|
|
297
288
|
taskTexts,
|
|
298
|
-
title: typeof options.title === 'string' ? options.title : undefined,
|
|
299
|
-
userId: identity.userId,
|
|
300
289
|
});
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
}
|
|
304
|
-
if (!offline) {
|
|
305
|
-
await waitForIdleDataFlow(db, Math.min(timeoutMs, 5_000));
|
|
306
|
-
}
|
|
307
|
-
printYaml({ ok: true, ...result });
|
|
290
|
+
printDryRunPreview(preview);
|
|
291
|
+
return;
|
|
308
292
|
}
|
|
309
|
-
|
|
310
|
-
|
|
293
|
+
const identity = await resolveAuthIdentity(rawConfig, config);
|
|
294
|
+
const result = await createTodoNote(db, {
|
|
295
|
+
taskTexts,
|
|
296
|
+
title: typeof createOptions.title === 'string' ? createOptions.title : undefined,
|
|
297
|
+
userId: identity.userId,
|
|
298
|
+
});
|
|
299
|
+
if (!offline && !config.powersync.uploadBaseUrl) {
|
|
300
|
+
printHumanReadableLine('warning: upload disabled (powersync.uploadBaseUrl is unset); changes are local-only for now');
|
|
311
301
|
}
|
|
302
|
+
if (!offline) {
|
|
303
|
+
await waitForIdleDataFlow(db, Math.min(timeoutMs, 5_000));
|
|
304
|
+
}
|
|
305
|
+
printSuccess({ ok: true, ...result });
|
|
312
306
|
});
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
const
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
307
|
+
}
|
|
308
|
+
async function runTodoUpdate({ args, globals, options }) {
|
|
309
|
+
const updateOptions = options;
|
|
310
|
+
await withTodoDatabase(globals, async ({ db, config, offline, timeoutMs }) => {
|
|
311
|
+
await syncBeforeTodoCommand(db, offline, timeoutMs);
|
|
312
|
+
const taskId = args.taskId;
|
|
313
|
+
if (!taskId) {
|
|
314
|
+
throw new DinoxError('taskId is required');
|
|
315
|
+
}
|
|
316
|
+
const checked = parseTaskCheckedStatus(updateOptions.status);
|
|
317
|
+
if (Boolean(updateOptions.dryRun)) {
|
|
318
|
+
const preview = await buildTodoUpdateDryRunPreview({
|
|
319
|
+
db,
|
|
320
|
+
config,
|
|
321
|
+
offline,
|
|
328
322
|
taskId,
|
|
329
323
|
checked,
|
|
330
324
|
});
|
|
331
|
-
|
|
325
|
+
printDryRunPreview(preview);
|
|
326
|
+
return;
|
|
332
327
|
}
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
command.outputHelp();
|
|
328
|
+
const result = await updateTodoTaskStatus(db, {
|
|
329
|
+
taskId,
|
|
330
|
+
checked,
|
|
331
|
+
});
|
|
332
|
+
printSuccess({ ok: true, ...result });
|
|
339
333
|
});
|
|
340
334
|
}
|
|
335
|
+
const todoSearchCommand = {
|
|
336
|
+
name: 'search',
|
|
337
|
+
description: 'Search todo tasks extracted from note content',
|
|
338
|
+
arguments: [
|
|
339
|
+
{
|
|
340
|
+
syntax: '[query]',
|
|
341
|
+
description: 'Task text query; may be omitted for filter-only search',
|
|
342
|
+
},
|
|
343
|
+
],
|
|
344
|
+
options: [
|
|
345
|
+
{ flags: '--status <status>', description: 'Task status: all | completed | uncompleted', defaultValue: 'all' },
|
|
346
|
+
{ flags: '--tags <string|@file>', description: 'Task tags (JSON array or comma/newline-separated)' },
|
|
347
|
+
{ flags: '--from <date>', description: 'Task date range start (YYYY-MM-DD or ISO datetime)' },
|
|
348
|
+
{ flags: '--to <date>', description: 'Task date range end (YYYY-MM-DD or ISO datetime)' },
|
|
349
|
+
{ flags: '--days <n>', description: 'Recent N days; mutually exclusive with --from/--to' },
|
|
350
|
+
{ flags: '--limit <n>', description: 'Maximum returned tasks' },
|
|
351
|
+
{ flags: '--scan-limit <n>', description: 'Maximum scanned note rows' },
|
|
352
|
+
{ flags: '--include-deleted', description: 'Include soft-deleted notes' },
|
|
353
|
+
],
|
|
354
|
+
schema: {
|
|
355
|
+
path: 'todo.search',
|
|
356
|
+
risk: 'read',
|
|
357
|
+
auth: 'required',
|
|
358
|
+
globalOptions: TODO_GLOBAL_OPTIONS,
|
|
359
|
+
output: {
|
|
360
|
+
type: 'structured',
|
|
361
|
+
summary: 'Todo search payload with metadata and task list',
|
|
362
|
+
fields: ['tasks.task_id', 'tasks.task_key', 'tasks.note_id', 'tasks.note_title', 'tasks.status', 'tasks.depth', 'tasks.parent_id', 'tasks.tags'],
|
|
363
|
+
metaFields: ['meta.query', 'meta.returned_count', 'meta.limit', 'meta.task_status', 'meta.task_tag_filter', 'meta.task_date_range', 'meta.scanned_note_count'],
|
|
364
|
+
},
|
|
365
|
+
mcp: {
|
|
366
|
+
toolName: 'dinox_search_todos',
|
|
367
|
+
description: 'Search todo tasks',
|
|
368
|
+
properties: [
|
|
369
|
+
{ name: 'query', type: 'string', description: 'Task text query' },
|
|
370
|
+
{ name: 'status', type: 'string', description: 'Task status', enum: ['all', 'completed', 'uncompleted'] },
|
|
371
|
+
{ name: 'tags', type: 'string[]', description: 'Task tags' },
|
|
372
|
+
{ name: 'limit', type: 'number', description: 'Max results (default: 50)' },
|
|
373
|
+
],
|
|
374
|
+
},
|
|
375
|
+
examples: ['dino todo search "报销" --status uncompleted --format json', 'dino todo search --days 7 --format json'],
|
|
376
|
+
},
|
|
377
|
+
action: runTodoSearch,
|
|
378
|
+
};
|
|
379
|
+
const todoAppendCommand = {
|
|
380
|
+
name: 'append',
|
|
381
|
+
description: 'Append one or more tasks to an existing note',
|
|
382
|
+
arguments: [
|
|
383
|
+
{
|
|
384
|
+
syntax: '[task]',
|
|
385
|
+
description: 'Single task text',
|
|
386
|
+
},
|
|
387
|
+
],
|
|
388
|
+
options: [
|
|
389
|
+
{
|
|
390
|
+
flags: '--task <text>',
|
|
391
|
+
description: 'Repeatable task text',
|
|
392
|
+
parser: collectStringOptionValue,
|
|
393
|
+
defaultValue: [],
|
|
394
|
+
},
|
|
395
|
+
{ flags: '--tasks <string|@file>', description: 'Task list (JSON array or comma/newline-separated)' },
|
|
396
|
+
{ flags: '--note-id <id>', description: 'Target note id; omitted means latest eligible note' },
|
|
397
|
+
{ flags: '--dry-run', description: 'Preview the write without executing it' },
|
|
398
|
+
],
|
|
399
|
+
schema: {
|
|
400
|
+
path: 'todo.append',
|
|
401
|
+
risk: 'write',
|
|
402
|
+
auth: 'required',
|
|
403
|
+
globalOptions: TODO_GLOBAL_OPTIONS,
|
|
404
|
+
output: {
|
|
405
|
+
type: 'structured',
|
|
406
|
+
summary: 'Append result with target note and appended task ids',
|
|
407
|
+
fields: ['noteId', 'appendedCount', 'taskIds'],
|
|
408
|
+
},
|
|
409
|
+
examples: ['dino todo append --note-id <id> --task "补交报销单" --format json'],
|
|
410
|
+
},
|
|
411
|
+
action: runTodoAppend,
|
|
412
|
+
};
|
|
413
|
+
const todoCreateCommand = {
|
|
414
|
+
name: 'create',
|
|
415
|
+
description: 'Create a new note containing one or more todo items',
|
|
416
|
+
arguments: [
|
|
417
|
+
{
|
|
418
|
+
syntax: '[task]',
|
|
419
|
+
description: 'Single task text',
|
|
420
|
+
},
|
|
421
|
+
],
|
|
422
|
+
options: [
|
|
423
|
+
{
|
|
424
|
+
flags: '--task <text>',
|
|
425
|
+
description: 'Repeatable task text',
|
|
426
|
+
parser: collectStringOptionValue,
|
|
427
|
+
defaultValue: [],
|
|
428
|
+
},
|
|
429
|
+
{ flags: '--tasks <string|@file>', description: 'Task list (JSON array or comma/newline-separated)' },
|
|
430
|
+
{ flags: '--title <string>', description: 'Optional note title' },
|
|
431
|
+
{ flags: '--dry-run', description: 'Preview the write without executing it' },
|
|
432
|
+
],
|
|
433
|
+
schema: {
|
|
434
|
+
path: 'todo.create',
|
|
435
|
+
risk: 'write',
|
|
436
|
+
auth: 'required',
|
|
437
|
+
globalOptions: TODO_GLOBAL_OPTIONS,
|
|
438
|
+
output: {
|
|
439
|
+
type: 'structured',
|
|
440
|
+
summary: 'Creation result with new note id and task ids',
|
|
441
|
+
fields: ['noteId', 'taskIds', 'title'],
|
|
442
|
+
},
|
|
443
|
+
examples: ['dino todo create --task "补交报销单" --task "整理发票" --format json'],
|
|
444
|
+
},
|
|
445
|
+
action: runTodoCreate,
|
|
446
|
+
};
|
|
447
|
+
const todoUpdateCommand = {
|
|
448
|
+
name: 'update',
|
|
449
|
+
description: 'Update a todo task checked status by task id',
|
|
450
|
+
arguments: [
|
|
451
|
+
{
|
|
452
|
+
syntax: '<taskId>',
|
|
453
|
+
description: 'Task id',
|
|
454
|
+
},
|
|
455
|
+
],
|
|
456
|
+
options: [
|
|
457
|
+
{
|
|
458
|
+
flags: '--status <status>',
|
|
459
|
+
description: 'Target status: completed|uncompleted|done|undone|true|false|1|0',
|
|
460
|
+
required: true,
|
|
461
|
+
},
|
|
462
|
+
{ flags: '--dry-run', description: 'Preview the write without executing it' },
|
|
463
|
+
],
|
|
464
|
+
schema: {
|
|
465
|
+
path: 'todo.update',
|
|
466
|
+
risk: 'write',
|
|
467
|
+
auth: 'required',
|
|
468
|
+
globalOptions: TODO_GLOBAL_OPTIONS,
|
|
469
|
+
output: {
|
|
470
|
+
type: 'structured',
|
|
471
|
+
summary: 'Mutation result with updated status and target note id',
|
|
472
|
+
fields: ['taskId', 'checked', 'updated', 'noteId'],
|
|
473
|
+
},
|
|
474
|
+
examples: ['dino todo update <taskId> --status completed --format json'],
|
|
475
|
+
},
|
|
476
|
+
action: runTodoUpdate,
|
|
477
|
+
};
|
|
478
|
+
export const todoCommandGroup = {
|
|
479
|
+
name: 'todo',
|
|
480
|
+
description: 'Manage todo tasks',
|
|
481
|
+
commands: [
|
|
482
|
+
todoSearchCommand,
|
|
483
|
+
todoAppendCommand,
|
|
484
|
+
todoCreateCommand,
|
|
485
|
+
todoUpdateCommand,
|
|
486
|
+
],
|
|
487
|
+
};
|
|
488
|
+
export function registerTodoCommands(program) {
|
|
489
|
+
registerCommandGroup(program, todoCommandGroup);
|
|
490
|
+
}
|
|
@@ -79,7 +79,18 @@ export type SearchedTodoResponse = {
|
|
|
79
79
|
};
|
|
80
80
|
export type { TaskDateRangeFilter, TaskStatusFilter } from './taskModel.js';
|
|
81
81
|
export { normalizeTaskTexts } from './contentModel.js';
|
|
82
|
+
type TodoMutableNoteRow = {
|
|
83
|
+
id: string | null;
|
|
84
|
+
title: string | null;
|
|
85
|
+
content_json: string | null;
|
|
86
|
+
content_md: string | null;
|
|
87
|
+
is_del: number | null;
|
|
88
|
+
created_at?: string | null;
|
|
89
|
+
};
|
|
82
90
|
export declare function searchTodoTasks(db: AbstractPowerSyncDatabase, query: string, options: SearchTodoOptions): Promise<SearchedTodoResponse>;
|
|
83
91
|
export declare function appendTodoTasks(db: AbstractPowerSyncDatabase, options: AppendTodoTasksOptions): Promise<AppendTodoTasksResult>;
|
|
84
92
|
export declare function createTodoNote(db: AbstractPowerSyncDatabase, options: CreateTodoNoteOptions): Promise<CreateTodoNoteResult>;
|
|
85
93
|
export declare function updateTodoTaskStatus(db: AbstractPowerSyncDatabase, options: UpdateTodoTaskStatusOptions): Promise<UpdateTodoTaskStatusResult>;
|
|
94
|
+
export declare function getTodoAppendTargetById(db: AbstractPowerSyncDatabase, noteId: string): Promise<TodoMutableNoteRow | null>;
|
|
95
|
+
export declare function getLatestTodoAppendTarget(db: AbstractPowerSyncDatabase): Promise<TodoMutableNoteRow | null>;
|
|
96
|
+
export declare function loadTodoTaskCandidatesByTaskId(db: AbstractPowerSyncDatabase, taskId: string): Promise<TodoMutableNoteRow[]>;
|
|
@@ -133,20 +133,48 @@ export async function updateTodoTaskStatus(db, options) {
|
|
|
133
133
|
throw new DinoxError('taskId is required');
|
|
134
134
|
}
|
|
135
135
|
const candidates = await loadTodoTaskCandidatesByTaskId(db, taskId);
|
|
136
|
-
const matches =
|
|
136
|
+
const matches = [];
|
|
137
|
+
const skippedCandidates = [];
|
|
138
|
+
for (const candidate of candidates) {
|
|
137
139
|
try {
|
|
138
140
|
const doc = parseTiptapDocFromDb(candidate.content_json);
|
|
139
141
|
const applied = applyTaskCheckedStatus(doc, taskId, options.checked);
|
|
140
142
|
if (applied.matchedIndices.length === 0) {
|
|
141
|
-
|
|
143
|
+
continue;
|
|
142
144
|
}
|
|
143
|
-
|
|
145
|
+
matches.push({ candidate, applied });
|
|
144
146
|
}
|
|
145
|
-
catch {
|
|
146
|
-
|
|
147
|
+
catch (error) {
|
|
148
|
+
skippedCandidates.push({
|
|
149
|
+
noteId: candidate.id,
|
|
150
|
+
errorMessage: error instanceof Error ? error.message : String(error),
|
|
151
|
+
});
|
|
147
152
|
}
|
|
148
|
-
}
|
|
153
|
+
}
|
|
149
154
|
if (matches.length === 0) {
|
|
155
|
+
if (skippedCandidates.length > 0) {
|
|
156
|
+
const failedNoteIds = Array.from(new Set(skippedCandidates
|
|
157
|
+
.map((entry) => (entry.noteId ?? '').trim())
|
|
158
|
+
.filter((value) => value.length > 0)));
|
|
159
|
+
const sampleFailedIds = failedNoteIds.slice(0, 3);
|
|
160
|
+
const suffix = sampleFailedIds.length > 0
|
|
161
|
+
? ` Failed note ids: ${sampleFailedIds.join(', ')}${failedNoteIds.length > sampleFailedIds.length ? ', ...' : ''}.`
|
|
162
|
+
: '';
|
|
163
|
+
throw new DinoxError(`Task not found: ${taskId}. Skipped ${skippedCandidates.length} candidate note(s) with invalid content_json.${suffix}`, {
|
|
164
|
+
code: 'TODO_TASK_NOT_FOUND',
|
|
165
|
+
suggestion: sampleFailedIds.length > 0
|
|
166
|
+
? `Try 'dino note detail ${sampleFailedIds[0]}' to inspect/repair note content.`
|
|
167
|
+
: undefined,
|
|
168
|
+
details: {
|
|
169
|
+
taskId,
|
|
170
|
+
candidatesCount: candidates.length,
|
|
171
|
+
skippedCandidatesCount: skippedCandidates.length,
|
|
172
|
+
failedNoteIds,
|
|
173
|
+
skippedCandidates: skippedCandidates.slice(0, 5),
|
|
174
|
+
},
|
|
175
|
+
recoverable: true,
|
|
176
|
+
});
|
|
177
|
+
}
|
|
150
178
|
throw new DinoxError(`Task not found: ${taskId}`);
|
|
151
179
|
}
|
|
152
180
|
if (matches.length > 1) {
|
|
@@ -222,7 +250,7 @@ async function loadTodoNoteRows(db, includeDeleted, scanLimit) {
|
|
|
222
250
|
LIMIT ?
|
|
223
251
|
`, [scanLimit]);
|
|
224
252
|
}
|
|
225
|
-
async function getTodoAppendTargetById(db, noteId) {
|
|
253
|
+
export async function getTodoAppendTargetById(db, noteId) {
|
|
226
254
|
return db.getOptional(`
|
|
227
255
|
SELECT
|
|
228
256
|
n.id,
|
|
@@ -234,7 +262,7 @@ async function getTodoAppendTargetById(db, noteId) {
|
|
|
234
262
|
WHERE n.id = ?
|
|
235
263
|
`, [noteId]);
|
|
236
264
|
}
|
|
237
|
-
async function getLatestTodoAppendTarget(db) {
|
|
265
|
+
export async function getLatestTodoAppendTarget(db) {
|
|
238
266
|
return db.getOptional(`
|
|
239
267
|
SELECT
|
|
240
268
|
n.id,
|
|
@@ -251,7 +279,7 @@ async function getLatestTodoAppendTarget(db) {
|
|
|
251
279
|
LIMIT 1
|
|
252
280
|
`, []);
|
|
253
281
|
}
|
|
254
|
-
async function loadTodoTaskCandidatesByTaskId(db, taskId) {
|
|
282
|
+
export async function loadTodoTaskCandidatesByTaskId(db, taskId) {
|
|
255
283
|
const escapedTaskId = escapeLikePattern(taskId);
|
|
256
284
|
return db.getAll(`
|
|
257
285
|
SELECT
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
|
+
import type { CommandDescriptor } from './descriptor/types.js';
|
|
2
3
|
type PackageManager = 'npm' | 'pnpm' | 'yarn' | 'bun';
|
|
3
4
|
type LogFn = (...parts: unknown[]) => void;
|
|
4
5
|
type UpdateCommandDeps = {
|
|
@@ -7,5 +8,7 @@ type UpdateCommandDeps = {
|
|
|
7
8
|
getVersion: () => string;
|
|
8
9
|
log: LogFn;
|
|
9
10
|
};
|
|
11
|
+
export declare function createUpdateCommandDescriptor(overrides?: Partial<UpdateCommandDeps>): CommandDescriptor;
|
|
12
|
+
export declare const updateCommandDescriptor: CommandDescriptor;
|
|
10
13
|
export declare function registerUpdateCommand(program: Command, overrides?: Partial<UpdateCommandDeps>): void;
|
|
11
14
|
export {};
|
package/dist/commands/update.js
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import { spawn } from 'node:child_process';
|
|
2
|
+
import { registerStandaloneCommand } from './descriptor/register.js';
|
|
2
3
|
import { SKILLS_REPO_URL } from '../constants/links.js';
|
|
4
|
+
import { printHumanReadableLine } from '../utils/output.js';
|
|
3
5
|
import { getPackageVersion } from '../utils/version.js';
|
|
4
6
|
const PACKAGE_NAME = '@dinoxx/dinox-cli';
|
|
7
|
+
function logHumanReadable(...parts) {
|
|
8
|
+
printHumanReadableLine(parts.map((part) => String(part)).join(' '));
|
|
9
|
+
}
|
|
5
10
|
function detectPackageManager() {
|
|
6
11
|
const scriptPath = process.argv[1] ?? '';
|
|
7
12
|
if (scriptPath.includes('/.bun/'))
|
|
@@ -38,7 +43,7 @@ function resolveDeps(overrides) {
|
|
|
38
43
|
detectPackageManager,
|
|
39
44
|
runUpdate,
|
|
40
45
|
getVersion: getPackageVersion,
|
|
41
|
-
log:
|
|
46
|
+
log: logHumanReadable,
|
|
42
47
|
...overrides,
|
|
43
48
|
};
|
|
44
49
|
}
|
|
@@ -46,17 +51,34 @@ function printSkillsReminder(log) {
|
|
|
46
51
|
log(`skills repo: ${SKILLS_REPO_URL}`);
|
|
47
52
|
log('ai reminder: review and update local Dinox skills after CLI updates (especially dino-dinox and dino-update-cli).');
|
|
48
53
|
}
|
|
49
|
-
export function
|
|
54
|
+
export function createUpdateCommandDescriptor(overrides = {}) {
|
|
50
55
|
const deps = resolveDeps(overrides);
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
56
|
+
return {
|
|
57
|
+
name: 'update',
|
|
58
|
+
description: `Update ${PACKAGE_NAME} to the latest version`,
|
|
59
|
+
schema: {
|
|
60
|
+
path: 'update',
|
|
61
|
+
risk: 'write',
|
|
62
|
+
auth: 'none',
|
|
63
|
+
globalOptions: [],
|
|
64
|
+
output: {
|
|
65
|
+
type: 'structured',
|
|
66
|
+
summary: 'Human-readable updater progress and bundled skills reminder',
|
|
67
|
+
fields: ['current version log', 'package manager log', 'skills repo reminder'],
|
|
68
|
+
},
|
|
69
|
+
examples: ['dino update'],
|
|
70
|
+
},
|
|
71
|
+
action: async () => {
|
|
72
|
+
const pm = deps.detectPackageManager();
|
|
73
|
+
deps.log(`current version: ${deps.getVersion()}`);
|
|
74
|
+
deps.log(`updating ${PACKAGE_NAME} via ${pm}...`);
|
|
75
|
+
await deps.runUpdate(pm);
|
|
76
|
+
deps.log('done');
|
|
77
|
+
printSkillsReminder(deps.log);
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
export const updateCommandDescriptor = createUpdateCommandDescriptor();
|
|
82
|
+
export function registerUpdateCommand(program, overrides = {}) {
|
|
83
|
+
registerStandaloneCommand(program, createUpdateCommandDescriptor(overrides));
|
|
62
84
|
}
|