@ethlete/agent-rules 0.1.0-next.17 → 0.1.0-next.18

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.
Files changed (44) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/README.md +9 -2
  3. package/content/hooks/subagent-model-policy.py +5 -3
  4. package/content/rules/subagent-models.md +7 -7
  5. package/content/skills/design-exploration/SKILL.md +3 -1
  6. package/content/skills/query/SKILL.md +3 -2
  7. package/content/skills/timetrack/SKILL.md +97 -27
  8. package/package.json +1 -1
  9. package/src/lib/frontmatter.js +32 -5
  10. package/src/lib/frontmatter.js.map +1 -1
  11. package/src/lib/git-flow/parse.js +38 -12
  12. package/src/lib/git-flow/parse.js.map +1 -1
  13. package/src/lib/git-flow-repair.js +12 -1
  14. package/src/lib/git-flow-repair.js.map +1 -1
  15. package/src/lib/git.js +8 -2
  16. package/src/lib/git.js.map +1 -1
  17. package/src/lib/gitlab.js +11 -1
  18. package/src/lib/gitlab.js.map +1 -1
  19. package/src/lib/migrate.js +1 -1
  20. package/src/lib/migrate.js.map +1 -1
  21. package/src/lib/output-style.js +11 -3
  22. package/src/lib/output-style.js.map +1 -1
  23. package/src/lib/owned-paths.js +8 -2
  24. package/src/lib/owned-paths.js.map +1 -1
  25. package/src/lib/plan.js +20 -4
  26. package/src/lib/plan.js.map +1 -1
  27. package/src/lib/render.d.ts +4 -0
  28. package/src/lib/render.js +8 -2
  29. package/src/lib/render.js.map +1 -1
  30. package/src/lib/sync.js +3 -0
  31. package/src/lib/sync.js.map +1 -1
  32. package/src/lib/targets/codex.js +1 -1
  33. package/src/lib/targets/codex.js.map +1 -1
  34. package/src/lib/targets/copilot.js +1 -1
  35. package/src/lib/targets/copilot.js.map +1 -1
  36. package/src/lib/targets/git-hooks.js +2 -1
  37. package/src/lib/targets/git-hooks.js.map +1 -1
  38. package/src/lib/targets/hooks-shared.js +1 -1
  39. package/src/lib/targets/hooks-shared.js.map +1 -1
  40. package/src/lib/timetrack-command.js +255 -68
  41. package/src/lib/timetrack-command.js.map +1 -1
  42. package/src/lib/timetrack.d.ts +125 -23
  43. package/src/lib/timetrack.js +53 -17
  44. package/src/lib/timetrack.js.map +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"hooks-shared.js","sourceRoot":"","sources":["../../../../../../libs/agent-rules/src/lib/targets/hooks-shared.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"hooks-shared.js","sourceRoot":"","sources":["../../../../../../libs/agent-rules/src/lib/targets/hooks-shared.ts"],"names":[],"mappings":"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"}
@@ -30,33 +30,65 @@ const FLAGS_WITH_VALUE = [
30
30
  '--paths',
31
31
  '--claim',
32
32
  '--author',
33
+ '--plan',
34
+ '--delete',
35
+ '--repo',
36
+ '--name',
37
+ '--rename',
38
+ '--merge',
39
+ '--into',
33
40
  ];
34
41
  /** Every human-readable line, with anything a terminal would act on printed rather than obeyed. */
35
42
  const say = (line) => console.log((0, plain_text_1.plain)(line));
36
43
  const positionalArgs = (args) => args.filter((entry, index) => { var _a; return !entry.startsWith('--') && !FLAGS_WITH_VALUE.includes((_a = args[index - 1]) !== null && _a !== void 0 ? _a : ''); });
37
44
  const flagValue = (args, flag) => {
38
45
  const index = args.indexOf(flag);
39
- return index === -1 ? undefined : args[index + 1];
46
+ if (index === -1)
47
+ return undefined;
48
+ const value = args[index + 1];
49
+ if (value === undefined || value.startsWith('--'))
50
+ throw new Error(`${flag} needs a value.`);
51
+ return value;
40
52
  };
41
53
  const numberFlag = (args, flag) => {
42
54
  const raw = flagValue(args, flag);
43
55
  if (raw === undefined)
44
56
  return undefined;
45
- const value = Number(raw);
57
+ const value = raw.trim() ? Number(raw) : Number.NaN;
46
58
  if (!Number.isFinite(value))
47
59
  throw new Error(`${flag} takes a number, not ${raw}.`);
48
60
  return value;
49
61
  };
50
- /** `--at` takes anything `Date` reads, so an agent may pass an ISO instant or leave it out for now. */
51
- const instantFlag = (args) => {
52
- const raw = flagValue(args, '--at');
53
- if (!raw)
54
- return Date.now();
55
- const at = new Date(raw);
62
+ const countFlag = (args, flag) => {
63
+ const value = numberFlag(args, flag);
64
+ if (value !== undefined && (!Number.isInteger(value) || value <= 0)) {
65
+ throw new Error(`${flag} takes a whole number above zero, not ${value}.`);
66
+ }
67
+ return value;
68
+ };
69
+ const LOCAL_DATE = /^(\d{4})-(\d{2})-(\d{2})$/;
70
+ const LOCAL_CLOCK = /^(\d{1,2}):(\d{2})$/;
71
+ /**
72
+ * Reads an instant flag. A date alone is local midnight and a clock alone is that time today, where
73
+ * `Date` would read the first as UTC and refuse the second; anything else goes to `Date` as it is.
74
+ */
75
+ const instantOf = (flag, raw) => {
76
+ const date = LOCAL_DATE.exec(raw);
77
+ const clock = LOCAL_CLOCK.exec(raw);
78
+ const today = new Date();
79
+ const at = date
80
+ ? new Date(Number(date[1]), Number(date[2]) - 1, Number(date[3]))
81
+ : clock
82
+ ? new Date(today.getFullYear(), today.getMonth(), today.getDate(), Number(clock[1]), Number(clock[2]))
83
+ : new Date(raw);
56
84
  if (Number.isNaN(at.getTime()))
57
- throw new Error(`--at takes a date, not ${raw}.`);
85
+ throw new Error(`${flag} takes a date, not ${raw}.`);
58
86
  return at.getTime();
59
87
  };
88
+ const instantFlag = (args) => {
89
+ const raw = flagValue(args, '--at');
90
+ return raw ? instantOf('--at', raw) : Date.now();
91
+ };
60
92
  const issueLine = (issue) => [
61
93
  issue.key,
62
94
  issue.issueType,
@@ -208,6 +240,63 @@ const tempoDayLines = (worklogs) => {
208
240
  return `${day} ${hours(total)} ${parts.join(' ')}`;
209
241
  });
210
242
  };
243
+ const syncWriteLine = (write) => {
244
+ var _a;
245
+ return [
246
+ write.kind.padEnd(6),
247
+ (_a = write.issueKey) !== null && _a !== void 0 ? _a : write.proposalId,
248
+ `${clock(write.fromMs)}-${clock(write.fromMs + write.durationMs)}`,
249
+ `${minutesOf(write.durationMs)}m`,
250
+ write.reason,
251
+ ...(write.tempoWorklogId ? [`worklog ${write.tempoWorklogId}`] : []),
252
+ write.description || '(no description)',
253
+ ...(write.blocked ? [`HELD BACK: ${write.blocked}`] : []),
254
+ ].join(' ');
255
+ };
256
+ /** What a sync of a day would write, row by row, and what it leaves alone. */
257
+ const syncPlanLines = (plan) => {
258
+ const count = (kind) => plan.writes.filter((write) => write.kind === kind).length;
259
+ const blocked = plan.writes.filter((write) => write.blocked).length;
260
+ return [
261
+ plan.writes.length
262
+ ? `${plan.day} plan ${plan.planHash} ${count('create')} create, ${count('update')} update, ${count('delete')} delete`
263
+ : `${plan.day} nothing to write`,
264
+ ...plan.writes.map((write) => ` ${syncWriteLine(write)}`),
265
+ `${plan.unchanged} unchanged, ${plan.skipped} skipped (still awaiting review)`,
266
+ ...(blocked ? [`${blocked} row(s) held back: Tempo would refuse them until they are fixed on the day.`] : []),
267
+ ...(plan.unresolvedKeys.length
268
+ ? [`Jira does not know ${plan.unresolvedKeys.join(', ')}, so nothing is written for those rows.`]
269
+ : []),
270
+ ...(plan.coveredMs
271
+ ? [`${minutesOf(plan.coveredMs)}m of the day is already logged by foreign worklogs, so it is left out.`]
272
+ : []),
273
+ `Foreign worklogs (${plan.foreign.length}), never touched`,
274
+ ...plan.foreign.map((worklog) => { var _a; return ` ${(_a = worklog.issueKey) !== null && _a !== void 0 ? _a : `#${worklog.issueId}`} ${clock(worklog.fromMs)} ${minutesOf(worklog.durationMs)}m ${worklog.description || '(no description)'}`; }),
275
+ ];
276
+ };
277
+ /** Every row a write attempted, and what the user has to do about the ones that did not land. */
278
+ const syncRunLines = (run) => [
279
+ ...run.rows.map((row) => {
280
+ var _a;
281
+ return [
282
+ row.status.padEnd(7),
283
+ row.kind.padEnd(6),
284
+ (_a = row.issueKey) !== null && _a !== void 0 ? _a : row.proposalId,
285
+ ...(row.tempoWorklogId ? [`worklog ${row.tempoWorklogId}`] : []),
286
+ ...(row.detail ? [row.detail] : []),
287
+ ].join(' ');
288
+ }),
289
+ ...(run.retryCount
290
+ ? [
291
+ `${run.retryCount} row(s) did not land. Retry them on the Sync page, which retries this run's own plan: a new plan read straight after a write can miss what Tempo just took.`,
292
+ ]
293
+ : []),
294
+ ...(run.unrecorded
295
+ ? [
296
+ `Written, but not recorded: ${run.unrecorded}. Tempo holds these worklogs and the app no longer owns them. Delete them in Tempo before writing this day again, or the time is logged twice.`,
297
+ ]
298
+ : []),
299
+ ];
211
300
  const meetingMs = (events) => events
212
301
  .filter((event) => !event.allDay && event.response !== 'declined')
213
302
  .reduce((sum, event) => sum + event.endMs - event.startMs, 0);
@@ -231,13 +320,6 @@ const calendarDayLines = (events) => {
231
320
  ...dayEvents.map((event) => ` ${calendarEventLine(event)}`),
232
321
  ]);
233
322
  };
234
- /** `--from` and `--to` take anything `Date` reads, so a caller may pass an ISO instant or a clock. */
235
- const instantOf = (flag, raw) => {
236
- const at = new Date(raw);
237
- if (Number.isNaN(at.getTime()))
238
- throw new Error(`${flag} takes a date, not ${raw}.`);
239
- return at.getTime();
240
- };
241
323
  /** Which edits the flags name. `--from` with `--to` is the one pair that names a single change. */
242
324
  const namedEdits = (argv) => {
243
325
  const named = argv.includes('--from') || argv.includes('--to') ? ['--from/--to'] : [];
@@ -305,7 +387,7 @@ const writeExport = (options) => {
305
387
  (0, fs_1.unlinkSync)(path);
306
388
  const handle = (0, fs_1.openSync)(path, 'wx', 0o600);
307
389
  try {
308
- (0, fs_1.writeSync)(handle, data);
390
+ (0, fs_1.writeFileSync)(handle, data);
309
391
  }
310
392
  finally {
311
393
  (0, fs_1.closeSync)(handle);
@@ -317,6 +399,49 @@ const printed = (value, json) => {
317
399
  console.log(JSON.stringify(value, null, 2));
318
400
  return 0;
319
401
  };
402
+ /** Every write waits in the app for the user's press, so what a write prints is where to look next. */
403
+ const queuedLines = (queued, what) => [
404
+ `Queued in Timetrack for the user's approval: ${what}.`,
405
+ `Nothing is written until they approve it. Read the outcome with: timetrack approval ${queued.approvalId}`,
406
+ ];
407
+ const printedQueued = (options) => {
408
+ if (!options.json)
409
+ queuedLines(options.queued, options.what).forEach(say);
410
+ return printed(options.queued, options.json);
411
+ };
412
+ const isSyncWithRun = (value) => typeof value === 'object' && value !== null && 'run' in value && typeof value.run === 'object';
413
+ const UNDECIDED = {
414
+ queued: "still waits for the user's approval in Timetrack.",
415
+ rejected: 'the user rejected it. Nothing was written.',
416
+ expired: 'expired at the end of the day it was asked on. Nothing was written. Ask again.',
417
+ };
418
+ const approvalLines = (found) => {
419
+ if (found.status !== 'approved')
420
+ return [`${found.approvalId} ${UNDECIDED[found.status]}`];
421
+ if (found.error !== undefined)
422
+ return [`${found.approvalId} approved, but it failed: ${found.error}`];
423
+ const unrecorded = unrecordedOf(found.result);
424
+ return [
425
+ `${found.approvalId} approved and carried out.`,
426
+ ...(isSyncWithRun(found.result) ? syncRunLines(found.result.run) : ['Pass --json for what it answered.']),
427
+ ...(unrecorded && !isSyncWithRun(found.result)
428
+ ? [`Carried out, but the app's ledger did not follow: ${unrecorded}. A later sync may undo it.`]
429
+ : []),
430
+ ];
431
+ };
432
+ const unrecordedOf = (value) => typeof value === 'object' && value !== null && 'unrecorded' in value && typeof value.unrecorded === 'string'
433
+ ? value.unrecorded
434
+ : undefined;
435
+ /** Non-zero for anything that did not land in full, so a caller polling in a script can tell. */
436
+ const approvalExitCode = (found) => {
437
+ if (found.status === 'queued')
438
+ return 0;
439
+ if (found.status !== 'approved' || found.error !== undefined)
440
+ return 1;
441
+ if (unrecordedOf(found.result))
442
+ return 1;
443
+ return isSyncWithRun(found.result) && (found.result.run.retryCount || found.result.run.unrecorded) ? 1 : 0;
444
+ };
320
445
  /**
321
446
  * Cuts a placeholder that covered a whole checkout into one per directory its commits worked in.
322
447
  *
@@ -352,16 +477,8 @@ const splitStandIn = (options) => tslib_1.__awaiter(void 0, void 0, void 0, func
352
477
  const claim = flagValue(argv, '--claim');
353
478
  const apply = argv.includes('--force');
354
479
  const projectRoots = (0, git_1.projectRootsOf)(checkout);
355
- const answer = yield (0, timetrack_1.timetrackSplitStandIn)({
356
- id,
357
- branch,
358
- repoPath,
359
- commits,
360
- projectRoots,
361
- paths,
362
- claim,
363
- apply,
364
- });
480
+ const request = { id, branch, repoPath, commits, projectRoots, paths, claim };
481
+ const answer = yield (0, timetrack_1.timetrackSplitStandIn)(request);
365
482
  if (!json) {
366
483
  say(`${standIn.name} ${standIn.days.length} day(s), branch ${branch}`);
367
484
  say(`Directories the commits name (${answer.candidates.length})`);
@@ -369,10 +486,7 @@ const splitStandIn = (options) => tslib_1.__awaiter(void 0, void 0, void 0, func
369
486
  if (!answer.pieces.length) {
370
487
  say('None of them is an automatic grain. Pick the pieces with --paths <dir>,<dir>.');
371
488
  }
372
- else if (apply) {
373
- say(`Split into ${answer.pieces.length}, and the rule that named it rewritten one per directory.`);
374
- }
375
- else {
489
+ else if (!apply) {
376
490
  say(`Would write ${answer.pieces.map((piece) => piece.workPath).join(', ')}. Pass --force to carry it out.`);
377
491
  }
378
492
  if (claim) {
@@ -387,11 +501,16 @@ const splitStandIn = (options) => tslib_1.__awaiter(void 0, void 0, void 0, func
387
501
  say('Name them in the day review, or re-run with --claim <dir> to give them a directory.');
388
502
  }
389
503
  }
504
+ if (apply && answer.pieces.length) {
505
+ const queued = yield (0, timetrack_1.timetrackApplyStandInSplit)(request);
506
+ return printedQueued({ queued, what: `split ${standIn.name} into ${answer.pieces.length}`, json });
507
+ }
390
508
  return printed(answer, json);
391
509
  });
392
510
  const USAGE = `ethlete-agents timetrack — ask the running Timetrack app about Jira
393
511
 
394
512
  The app holds this machine's Jira credentials, so no repository needs a token of its own.
513
+ Every write waits in the app until the user approves it there, and prints an approval id.
395
514
 
396
515
  timetrack status Whether the app is reachable, and which projects it holds
397
516
  timetrack instance The instance's own levels and its branch-subject candidates
@@ -410,6 +529,8 @@ The app holds this machine's Jira credentials, so no repository needs a token of
410
529
  Delete one placeholder, and the rule that named it
411
530
  timetrack standins --rename <id> --name <text>
412
531
  Give one placeholder another name, keeping its days and its rules
532
+ timetrack standins --merge <id> --into <id>
533
+ Fold one open placeholder into another, with its days and its rules
413
534
  timetrack standins --split <id> [--repo <dir>] [--paths <dir>,<dir>] [--claim <dir>] [--author <email>]
414
535
  [--force]
415
536
  Cut one that covered a whole checkout into one per directory
@@ -417,8 +538,14 @@ The app holds this machine's Jira credentials, so no repository needs a token of
417
538
  The account's own Tempo worklogs per day (default: the last 7 days)
418
539
  timetrack calendar [from] [to]
419
540
  The watched calendars' events per day (default: the last 7 days)
541
+ timetrack sync <YYYY-MM-DD> What a Tempo sync of the day would write, as the Sync page plans it
542
+ timetrack sync <YYYY-MM-DD> --write --plan <hash>
543
+ Write that plan to Tempo, once the user confirmed its rows
544
+ timetrack worklog --delete <id> --day <YYYY-MM-DD>
545
+ Delete one of the account's own Tempo worklogs, once the user asked for it
420
546
  timetrack naming [YYYY-MM-DD] Which checkouts the day offers a name for, and why the rest do not
421
547
  timetrack resync [path…] Read the agent session logs of checkouts again, after they got a link
548
+ timetrack approval <id> Where a queued write stands, and what it answered once approved
422
549
 
423
550
  Options for search
424
551
  --project <KEY> Search this project instead of the picked ones
@@ -446,6 +573,11 @@ Options for day
446
573
  Options for resync
447
574
  --replace Overwrite what the store holds for those sessions, after a parser fix
448
575
 
576
+ Options for sync
577
+ --write Queue the write of the plan. Refused without --plan; the app refuses it on
578
+ approval when the plan changed since
579
+ --plan <hash> The plan hash the read printed, for the rows the user confirmed
580
+
449
581
  Options for edit — pass exactly one change
450
582
  --day <YYYY-MM-DD> The day the row is on (default: today)
451
583
  --from <date> --to <date>
@@ -507,7 +639,7 @@ const timetrackCommand = (options) => tslib_1.__awaiter(void 0, void 0, void 0,
507
639
  text: value,
508
640
  projectKey: flagValue(argv, '--project'),
509
641
  assignedToMe: argv.includes('--mine'),
510
- limit: numberFlag(argv, '--limit'),
642
+ limit: countFlag(argv, '--limit'),
511
643
  });
512
644
  if (!json) {
513
645
  if (issues.length === 0)
@@ -517,7 +649,7 @@ const timetrackCommand = (options) => tslib_1.__awaiter(void 0, void 0, void 0,
517
649
  return printed(issues, json);
518
650
  }
519
651
  if (subcommand === 'project') {
520
- const found = yield (0, timetrack_1.timetrackRepoProject)(value !== null && value !== void 0 ? value : root);
652
+ const found = yield (0, timetrack_1.timetrackRepoProject)((0, path_1.resolve)(root, value !== null && value !== void 0 ? value : '.'));
521
653
  if (!json) {
522
654
  const where = found.inherited ? ' (from a directory above it)' : '';
523
655
  if (found.private)
@@ -536,7 +668,7 @@ const timetrackCommand = (options) => tslib_1.__awaiter(void 0, void 0, void 0,
536
668
  const summary = flagValue(argv, '--summary');
537
669
  if (!summary)
538
670
  throw new Error('Pass --summary "…".');
539
- const issue = yield (0, timetrack_1.timetrackCreateIssue)({
671
+ const queued = yield (0, timetrack_1.timetrackCreateIssue)({
540
672
  summary,
541
673
  description: flagValue(argv, '--description'),
542
674
  projectKey: flagValue(argv, '--project'),
@@ -544,9 +676,7 @@ const timetrackCommand = (options) => tslib_1.__awaiter(void 0, void 0, void 0,
544
676
  parentKey: flagValue(argv, '--parent'),
545
677
  subject: flagValue(argv, '--subject'),
546
678
  });
547
- if (!json)
548
- say(`${issue.key} ${summary}`);
549
- return printed(issue, json);
679
+ return printedQueued({ queued, what: `file the issue ${summary}`, json });
550
680
  }
551
681
  if (subcommand === 'log') {
552
682
  const issueKey = flagValue(argv, '--issue');
@@ -555,26 +685,27 @@ const timetrackCommand = (options) => tslib_1.__awaiter(void 0, void 0, void 0,
555
685
  throw new Error('Pass --issue <KEY>.');
556
686
  if (!minutes || minutes <= 0)
557
687
  throw new Error('Pass --minutes <n> above zero.');
558
- const worklog = yield (0, timetrack_1.timetrackAddWorklog)({
688
+ const queued = yield (0, timetrack_1.timetrackAddWorklog)({
559
689
  issueKey,
560
690
  description: flagValue(argv, '--description'),
561
691
  fromMs: instantFlag(argv),
562
692
  durationMs: Math.round(minutes * 60000),
563
693
  });
564
- if (!json) {
565
- say(`${worklog.issueKey} ${minutes}m on ${worklog.day}`);
566
- say('It is a row on the day, not a Tempo entry — review the day in Timetrack, then sync it.');
567
- }
568
- return printed(worklog, json);
694
+ if (!json)
695
+ say('Once approved it is a row on the day, not a Tempo entry — the day still needs a sync.');
696
+ return printedQueued({ queued, what: `a ${minutes}m row for ${issueKey}`, json });
569
697
  }
570
698
  if (subcommand === 'day') {
571
699
  const day = value !== null && value !== void 0 ? value : today();
572
700
  if (!DAY.test(day))
573
701
  throw new Error(`Pass a day as YYYY-MM-DD, not ${day}.`);
574
702
  const found = yield (0, timetrack_1.timetrackDayEvents)(day);
575
- const out = flagValue(argv, '--out');
576
- if (out) {
703
+ const outFlag = flagValue(argv, '--out');
704
+ if (outFlag) {
705
+ const out = (0, path_1.resolve)(root, outFlag);
577
706
  writeExport({ path: out, data: JSON.stringify(found), overwrite: argv.includes('--overwrite') });
707
+ if (json)
708
+ return printed({ day: found.day, events: found.events.length, out }, json);
578
709
  say(`${found.day} ${found.events.length} events written to ${out}, readable by you alone`);
579
710
  say('It holds the day as it was observed: window titles, paths and messages. Delete it when you are done.');
580
711
  return 0;
@@ -605,13 +736,8 @@ const timetrackCommand = (options) => tslib_1.__awaiter(void 0, void 0, void 0,
605
736
  throw new Error('Pass the row id to edit, as `timetrack rows` prints it.');
606
737
  if (!DAY.test(day))
607
738
  throw new Error(`Pass a day as YYYY-MM-DD, not ${day}.`);
608
- const edited = yield (0, timetrack_1.timetrackEditDay)({ day, edits: [editOf({ argv, rowId: value })] });
609
- if (!json) {
610
- say(`${edited.day} ${edited.applied} of 1 edit landed`);
611
- edited.rows.forEach((row) => say(` ${rowLine(row)}`));
612
- edited.warnings.forEach((warning) => say(` ! ${warning.kind}: ${warning.detail}`));
613
- }
614
- return printed(edited, json);
739
+ const queued = yield (0, timetrack_1.timetrackEditDay)({ day, edits: [editOf({ argv, rowId: value })] });
740
+ return printedQueued({ queued, what: `an edit of row ${value} on ${day}`, json });
615
741
  }
616
742
  if (subcommand === 'rules') {
617
743
  const rules = yield (0, timetrack_1.timetrackRules)();
@@ -626,14 +752,11 @@ const timetrackCommand = (options) => tslib_1.__awaiter(void 0, void 0, void 0,
626
752
  }
627
753
  if (subcommand === 'resync') {
628
754
  const named = positionalArgs(argv).slice(1);
629
- const resynced = yield (0, timetrack_1.timetrackResyncAgentSessions)((named.length ? named : ['.']).map((path) => (0, path_1.resolve)(root, path)), {
630
- replace: argv.includes('--replace'),
631
- });
632
- if (!json) {
633
- const how = resynced.replace ? ', replacing what it stored for them' : '';
634
- say(`Timetrack reads the agent sessions of ${resynced.paths.join(', ')} again${how}.`);
635
- }
636
- return printed(resynced, json);
755
+ const paths = (named.length ? named : ['.']).map((path) => (0, path_1.resolve)(root, path));
756
+ const replace = argv.includes('--replace');
757
+ const queued = yield (0, timetrack_1.timetrackResyncAgentSessions)(paths, { replace });
758
+ const how = replace ? ', replacing what it stored for them' : '';
759
+ return printedQueued({ queued, what: `read the agent sessions of ${paths.join(', ')} again${how}`, json });
637
760
  }
638
761
  if (subcommand === 'worklogs') {
639
762
  const to = (_b = positionalArgs(argv)[2]) !== null && _b !== void 0 ? _b : today();
@@ -650,6 +773,49 @@ const timetrackCommand = (options) => tslib_1.__awaiter(void 0, void 0, void 0,
650
773
  }
651
774
  return printed(found, json);
652
775
  }
776
+ if (subcommand === 'worklog') {
777
+ if (argv.includes('--help')) {
778
+ say(USAGE);
779
+ return 0;
780
+ }
781
+ const worklogId = flagValue(argv, '--delete');
782
+ const day = flagValue(argv, '--day');
783
+ if (!worklogId || !/^\d+$/.test(worklogId))
784
+ throw new Error('Pass the Tempo worklog id to delete with --delete <id>.');
785
+ if (!day || !DAY.test(day))
786
+ throw new Error('Pass the day the worklog is on with --day <YYYY-MM-DD>.');
787
+ const queued = yield (0, timetrack_1.timetrackTempoDelete)({ day, worklogId });
788
+ return printedQueued({ queued, what: `delete Tempo worklog ${worklogId} on ${day}`, json });
789
+ }
790
+ if (subcommand === 'sync') {
791
+ if (!value || !DAY.test(value))
792
+ throw new Error('Pass the day to sync as YYYY-MM-DD.');
793
+ const planHash = flagValue(argv, '--plan');
794
+ const write = argv.includes('--write');
795
+ if (write && !planHash) {
796
+ say(`A write names the plan the user confirmed. Read it with \`timetrack sync ${value}\`, show the rows,`);
797
+ say('and pass the hash it prints with --plan once they agree.');
798
+ return 1;
799
+ }
800
+ if (planHash && !write) {
801
+ say('--plan only means something with --write. Nothing was written.');
802
+ return 1;
803
+ }
804
+ if (write && planHash) {
805
+ const queued = yield (0, timetrack_1.timetrackTempoSyncWrite)({ day: value, planHash });
806
+ if (!json)
807
+ say('The app checks the plan again on approval, and refuses it if the day changed since.');
808
+ return printedQueued({ queued, what: `write plan ${planHash} of ${value} to Tempo`, json });
809
+ }
810
+ const found = yield (0, timetrack_1.timetrackTempoSync)(value);
811
+ if (!json) {
812
+ syncPlanLines(found).forEach(say);
813
+ if (found.writes.length) {
814
+ say(`Once the user has confirmed these rows: timetrack sync ${found.day} --write --plan ${found.planHash}`);
815
+ }
816
+ }
817
+ return printed(found, json);
818
+ }
653
819
  if (subcommand === 'calendar') {
654
820
  const to = (_c = positionalArgs(argv)[2]) !== null && _c !== void 0 ? _c : today();
655
821
  const from = value !== null && value !== void 0 ? value : shiftDay(to, -6);
@@ -665,7 +831,9 @@ const timetrackCommand = (options) => tslib_1.__awaiter(void 0, void 0, void 0,
665
831
  return printed(found, json);
666
832
  }
667
833
  if (subcommand === 'naming') {
668
- const day = value && DAY.test(value) ? value : today();
834
+ const day = value !== null && value !== void 0 ? value : today();
835
+ if (!DAY.test(day))
836
+ throw new Error(`Pass a day as YYYY-MM-DD, not ${day}.`);
669
837
  const naming = yield (0, timetrack_1.timetrackNaming)(day);
670
838
  if (!json) {
671
839
  say(`${day} tempo ${naming.tempoReady ? 'configured' : 'not configured'}`);
@@ -679,6 +847,15 @@ const timetrackCommand = (options) => tslib_1.__awaiter(void 0, void 0, void 0,
679
847
  }
680
848
  return printed(naming, json);
681
849
  }
850
+ if (subcommand === 'approval') {
851
+ if (!value)
852
+ throw new Error('Pass the approval id a write printed, e.g. `timetrack approval <id>`.');
853
+ const found = yield (0, timetrack_1.timetrackApprovalStatus)(value);
854
+ if (!json)
855
+ approvalLines(found).forEach(say);
856
+ printed(found, json);
857
+ return approvalExitCode(found);
858
+ }
682
859
  if (subcommand === 'standins') {
683
860
  const split = flagValue(argv, '--split');
684
861
  if (split)
@@ -690,10 +867,18 @@ const timetrackCommand = (options) => tslib_1.__awaiter(void 0, void 0, void 0,
690
867
  return 1;
691
868
  }
692
869
  if (rename && name) {
693
- const renamed = yield (0, timetrack_1.timetrackRenameStandIn)({ id: rename, name });
694
- if (!json)
695
- say(`Renamed ${rename} to ${name}.`);
696
- return printed(renamed, json);
870
+ const queued = yield (0, timetrack_1.timetrackRenameStandIn)({ id: rename, name });
871
+ return printedQueued({ queued, what: `rename ${rename} to ${name}`, json });
872
+ }
873
+ const merge = flagValue(argv, '--merge');
874
+ const into = flagValue(argv, '--into');
875
+ if (merge && !into) {
876
+ say(`A merge needs the placeholder it goes into. Pass --into <id> with --merge ${merge}.`);
877
+ return 1;
878
+ }
879
+ if (merge && into) {
880
+ const queued = yield (0, timetrack_1.timetrackMergeStandIn)({ id: merge, into });
881
+ return printedQueued({ queued, what: `merge ${merge} into ${into}`, json });
697
882
  }
698
883
  const remove = flagValue(argv, '--remove');
699
884
  const stranded = remove ? yield strandedDays(remove) : [];
@@ -703,14 +888,16 @@ const timetrackCommand = (options) => tslib_1.__awaiter(void 0, void 0, void 0,
703
888
  say('The app reopens a placeholder for today only. Pass --force if that is what you want.');
704
889
  return 1;
705
890
  }
706
- const standIns = remove ? yield (0, timetrack_1.timetrackRemoveStandIn)(remove) : yield (0, timetrack_1.timetrackStandIns)();
891
+ if (remove) {
892
+ const queued = yield (0, timetrack_1.timetrackRemoveStandIn)(remove);
893
+ return printedQueued({ queued, what: `delete ${remove} and the rule that named it`, json });
894
+ }
895
+ const standIns = yield (0, timetrack_1.timetrackStandIns)();
707
896
  const rules = json ? [] : (yield (0, timetrack_1.timetrackRules)()).attributionRules;
708
897
  const open = standIns
709
898
  .filter((standIn) => standIn.state === 'open')
710
899
  .sort((left, right) => left.createdAtMs - right.createdAtMs);
711
900
  if (!json) {
712
- if (remove)
713
- say(`Deleted ${remove}, and the rule that named it.`);
714
901
  say(`${open.length} open, ${standIns.length - open.length} resolved`);
715
902
  open.forEach((standIn) => say(` ${describeStandIn({ standIn, rules })}`));
716
903
  if (open.length)