@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.
- package/CHANGELOG.md +24 -0
- package/README.md +9 -2
- package/content/hooks/subagent-model-policy.py +5 -3
- package/content/rules/subagent-models.md +7 -7
- package/content/skills/design-exploration/SKILL.md +3 -1
- package/content/skills/query/SKILL.md +3 -2
- package/content/skills/timetrack/SKILL.md +97 -27
- package/package.json +1 -1
- package/src/lib/frontmatter.js +32 -5
- package/src/lib/frontmatter.js.map +1 -1
- package/src/lib/git-flow/parse.js +38 -12
- package/src/lib/git-flow/parse.js.map +1 -1
- package/src/lib/git-flow-repair.js +12 -1
- package/src/lib/git-flow-repair.js.map +1 -1
- package/src/lib/git.js +8 -2
- package/src/lib/git.js.map +1 -1
- package/src/lib/gitlab.js +11 -1
- package/src/lib/gitlab.js.map +1 -1
- package/src/lib/migrate.js +1 -1
- package/src/lib/migrate.js.map +1 -1
- package/src/lib/output-style.js +11 -3
- package/src/lib/output-style.js.map +1 -1
- package/src/lib/owned-paths.js +8 -2
- package/src/lib/owned-paths.js.map +1 -1
- package/src/lib/plan.js +20 -4
- package/src/lib/plan.js.map +1 -1
- package/src/lib/render.d.ts +4 -0
- package/src/lib/render.js +8 -2
- package/src/lib/render.js.map +1 -1
- package/src/lib/sync.js +3 -0
- package/src/lib/sync.js.map +1 -1
- package/src/lib/targets/codex.js +1 -1
- package/src/lib/targets/codex.js.map +1 -1
- package/src/lib/targets/copilot.js +1 -1
- package/src/lib/targets/copilot.js.map +1 -1
- package/src/lib/targets/git-hooks.js +2 -1
- package/src/lib/targets/git-hooks.js.map +1 -1
- package/src/lib/targets/hooks-shared.js +1 -1
- package/src/lib/targets/hooks-shared.js.map +1 -1
- package/src/lib/timetrack-command.js +255 -68
- package/src/lib/timetrack-command.js.map +1 -1
- package/src/lib/timetrack.d.ts +125 -23
- package/src/lib/timetrack.js +53 -17
- 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":";;;AAAA,2BAAkC;AAClC,+BAA4B;AAC5B,kDAAqD;AACrD,sCAAyC;AAmBzC;;;GAGG;AACU,QAAA,WAAW,GAAmC;IACzD,iBAAiB,EAAE;QACjB,MAAM,EAAE;YACN,EAAE,KAAK,EAAE,kBAAkB,EAAE;YAC7B,4EAA4E;YAC5E,EAAE,KAAK,EAAE,cAAc,EAAE,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE;YAC7C,mFAAmF;YACnF,iEAAiE;YACjE,EAAE,KAAK,EAAE,eAAe,EAAE,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE;YAC9C,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE;SACtC;QACD,IAAI,EAAE,oBAAoB;QAC1B,OAAO,EAAE,EAAE;KACZ;IACD,uBAAuB,EAAE;QACvB,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,YAAY,EAAE,CAAC;QACxD,IAAI,EAAE,0BAA0B;QAChC,OAAO,EAAE,EAAE;QACX,MAAM,EAAE,CAAC,QAAQ,CAAC;KACnB;CACF,CAAC;AAEF,mGAAmG;AAC5F,MAAM,aAAa,GAAG,CAAC,OAA2C,EAAE,EAAE,CAC3E,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE;;IAC5B,MAAM,MAAM,GAAG,MAAA,MAAA,mBAAW,CAAC,IAAI,CAAC,0CAAE,MAAM,mCAAI,EAAE,CAAC;IAE/C,OAAO,MAAM,CAAC,MAAM,KAAK,CAAC,IAAI,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAC/D,CAAC,CAAC,CAAC;AALQ,QAAA,aAAa,iBAKrB;AAEL,4EAA4E;AACrE,MAAM,cAAc,GAAG,CAAC,OAAwC,EAAE,EAAE;;IACzE,OAAA,CAAC,MAAA,MAAA,mBAAW,CAAC,OAAO,CAAC,IAAI,CAAC,0CAAE,MAAM,mCAAI,EAAE,CAAC,CAAC,MAAM,CAC9C,CAAC,YAAY,EAAE,EAAE,eAAC,OAAA,CAAC,MAAA,YAAY,CAAC,MAAM,mCAAI,EAAE,CAAC,CAAC,MAAM,KAAK,CAAC,KAAI,MAAA,YAAY,CAAC,MAAM,0CAAE,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA,CAAA,EAAA,CAC3G,CAAA;CAAA,CAAC;AAHS,QAAA,cAAc,kBAGvB;AAEG,MAAM,gBAAgB,GAAG,CAAC,KAAe,EAAE,EAAE;IAClD,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,
|
|
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
|
-
|
|
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
|
-
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
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(
|
|
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.
|
|
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
|
|
356
|
-
|
|
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:
|
|
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 :
|
|
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
|
|
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
|
-
|
|
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
|
|
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(
|
|
566
|
-
|
|
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
|
|
576
|
-
if (
|
|
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
|
|
609
|
-
|
|
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
|
|
630
|
-
|
|
631
|
-
});
|
|
632
|
-
|
|
633
|
-
|
|
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 &&
|
|
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
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
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
|
-
|
|
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)
|