@fukuchang/taskchute-cli 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +18 -0
- package/dist/index.js +25 -18
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,24 @@
|
|
|
2
2
|
|
|
3
3
|
このパッケージの変更履歴。[Keep a Changelog](https://keepachangelog.com/) 準拠。
|
|
4
4
|
|
|
5
|
+
## [0.3.0] - 2026-05-28 — 実績時刻の秒精度サポート
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- `taskchute tasks set-actual-times` の `--start` / `--end` が `HH:MM` に加え
|
|
10
|
+
`HH:MM:SS` も受け入れるようになった。秒は任意で、省略時は従来通り 0 として
|
|
11
|
+
扱う。Web UI でも実績時刻の入力 / 表示が秒精度で行えるようになり、Play ボタン
|
|
12
|
+
を押した時刻も秒単位で `actual_start_time` に記録される。
|
|
13
|
+
|
|
14
|
+
### Notes
|
|
15
|
+
|
|
16
|
+
- 後方互換あり: 既存の `HH:MM` 入力 / 保存データはそのまま動く。Supabase の
|
|
17
|
+
時刻カラムは TEXT のためマイグレーション不要
|
|
18
|
+
- `start_task` / `start` 系で記録される `actual_start_time` は今後 `HH:MM:SS`
|
|
19
|
+
形式で返ることがある (従来は `HH:MM`)。文字列としての扱いは変わらないが、
|
|
20
|
+
下流ツールが正規表現で `HH:MM` のみを期待していた場合は `HH:MM(:SS)?` に
|
|
21
|
+
ゆるめる必要がある
|
|
22
|
+
|
|
5
23
|
## [0.2.0] - 2026-05-28 — Web パリティ完全達成 ✨
|
|
6
24
|
|
|
7
25
|
5 PR (PR1〜5) で MCP/CLI を Web 機能パリティまで拡張し、PR6 で SOLID 観点の
|
package/dist/index.js
CHANGED
|
@@ -1612,6 +1612,12 @@ function toTimeString(date) {
|
|
|
1612
1612
|
const minutes = String(date.getMinutes()).padStart(2, "0");
|
|
1613
1613
|
return `${hours}:${minutes}`;
|
|
1614
1614
|
}
|
|
1615
|
+
function toTimeStringWithSeconds(date) {
|
|
1616
|
+
const hours = String(date.getHours()).padStart(2, "0");
|
|
1617
|
+
const minutes = String(date.getMinutes()).padStart(2, "0");
|
|
1618
|
+
const seconds = String(date.getSeconds()).padStart(2, "0");
|
|
1619
|
+
return `${hours}:${minutes}:${seconds}`;
|
|
1620
|
+
}
|
|
1615
1621
|
|
|
1616
1622
|
// ../domain/src/task-utils.ts
|
|
1617
1623
|
function sessionDurationSeconds(session) {
|
|
@@ -1629,14 +1635,15 @@ function calculateActualSeconds(task, now = /* @__PURE__ */ new Date()) {
|
|
|
1629
1635
|
}
|
|
1630
1636
|
|
|
1631
1637
|
// ../domain/src/task-operations.ts
|
|
1632
|
-
var TIME_RE = /^([01]\d|2[0-3]):([0-5]\d)
|
|
1638
|
+
var TIME_RE = /^([01]\d|2[0-3]):([0-5]\d)(?::([0-5]\d))?$/;
|
|
1633
1639
|
function parseTimeOnDate(dateString, timeString) {
|
|
1634
1640
|
const m = TIME_RE.exec(timeString);
|
|
1635
1641
|
if (!m || !m[1] || !m[2]) {
|
|
1636
1642
|
throw new Error(`Invalid time string: ${timeString}`);
|
|
1637
1643
|
}
|
|
1638
1644
|
const base = fromDateString(dateString);
|
|
1639
|
-
|
|
1645
|
+
const seconds = m[3] ? Number(m[3]) : 0;
|
|
1646
|
+
base.setHours(Number(m[1]), Number(m[2]), seconds, 0);
|
|
1640
1647
|
return base;
|
|
1641
1648
|
}
|
|
1642
1649
|
async function startTask(repo, taskId) {
|
|
@@ -1649,7 +1656,7 @@ async function startTask(repo, taskId) {
|
|
|
1649
1656
|
currentSession: { startedAt: now }
|
|
1650
1657
|
};
|
|
1651
1658
|
if (!task.actualStartTime) {
|
|
1652
|
-
patch.actualStartTime =
|
|
1659
|
+
patch.actualStartTime = toTimeStringWithSeconds(now);
|
|
1653
1660
|
}
|
|
1654
1661
|
return repo.update(taskId, patch);
|
|
1655
1662
|
}
|
|
@@ -1736,7 +1743,7 @@ async function interruptTask(repo, params) {
|
|
|
1736
1743
|
currentSession: { startedAt: interruptionStartedAt },
|
|
1737
1744
|
status: "running",
|
|
1738
1745
|
orderIndex: target.orderIndex + 1,
|
|
1739
|
-
actualStartTime:
|
|
1746
|
+
actualStartTime: toTimeStringWithSeconds(interruptionStartedAt),
|
|
1740
1747
|
projectId: params.interruption.projectId,
|
|
1741
1748
|
contextId: params.interruption.contextId,
|
|
1742
1749
|
tagIds: params.interruption.tagIds ?? []
|
|
@@ -1777,7 +1784,7 @@ async function addParallelTask(repo, input2) {
|
|
|
1777
1784
|
currentSession: startedAt ? { startedAt } : void 0,
|
|
1778
1785
|
status: startImmediately ? "running" : "planned",
|
|
1779
1786
|
orderIndex: maxOrder + 10,
|
|
1780
|
-
actualStartTime: startedAt ?
|
|
1787
|
+
actualStartTime: startedAt ? toTimeStringWithSeconds(startedAt) : void 0,
|
|
1781
1788
|
projectId: input2.projectId,
|
|
1782
1789
|
contextId: input2.contextId,
|
|
1783
1790
|
tagIds: input2.tagIds ?? [],
|
|
@@ -3304,19 +3311,19 @@ function tasksReorderCommand() {
|
|
|
3304
3311
|
console.log(`\u2195 Reordered: ${shortenId(updated.id)} ${updated.title} (order_index=${updated.orderIndex})`);
|
|
3305
3312
|
});
|
|
3306
3313
|
}
|
|
3307
|
-
var
|
|
3314
|
+
var HHMMSS = /^([01]\d|2[0-3]):[0-5]\d(?::[0-5]\d)?$/;
|
|
3308
3315
|
function tasksSetActualTimesCommand() {
|
|
3309
|
-
return new Command("set-actual-times").description("\u5B9F\u7E3E\u958B\u59CB/\u7D42\u4E86\u6642\u523B\u3092\u76F4\u63A5\u6307\u5B9A\u3059\u308B (Play \u62BC\u3057\u5FD8\u308C\u30EA\u30AB\u30D0\u30EA\u7528)").argument("<id-prefix>", "\u30BF\u30B9\u30AF id \u306E\u5148\u982D 4 \u6587\u5B57\u4EE5\u4E0A").option("-d, --date <date>", "\u5BFE\u8C61\u65E5 (\u30C7\u30D5\u30A9\u30EB\u30C8: \u4ECA\u65E5)").option("--start <HH:MM>", "\u5B9F\u7E3E\u958B\u59CB\u6642\u523B").option("--end <HH:MM>", "\u5B9F\u7E3E\u7D42\u4E86\u6642\u523B").option("--clear-start", "\u958B\u59CB\u6642\u523B\u3092\u30AF\u30EA\u30A2 (end \u3082\u81EA\u52D5\u30AF\u30EA\u30A2)").option("--clear-end", "\u7D42\u4E86\u6642\u523B\u306E\u307F\u30AF\u30EA\u30A2 (status \u306F running \u306B)").option("--reset", "\u5B9F\u7E3E\u3092\u5168\u30AF\u30EA\u30A2 (status \u2192 planned)").action(async (prefix, opts) => {
|
|
3316
|
+
return new Command("set-actual-times").description("\u5B9F\u7E3E\u958B\u59CB/\u7D42\u4E86\u6642\u523B\u3092\u76F4\u63A5\u6307\u5B9A\u3059\u308B (Play \u62BC\u3057\u5FD8\u308C\u30EA\u30AB\u30D0\u30EA\u7528)").argument("<id-prefix>", "\u30BF\u30B9\u30AF id \u306E\u5148\u982D 4 \u6587\u5B57\u4EE5\u4E0A").option("-d, --date <date>", "\u5BFE\u8C61\u65E5 (\u30C7\u30D5\u30A9\u30EB\u30C8: \u4ECA\u65E5)").option("--start <HH:MM[:SS]>", "\u5B9F\u7E3E\u958B\u59CB\u6642\u523B (\u79D2\u306F\u4EFB\u610F)").option("--end <HH:MM[:SS]>", "\u5B9F\u7E3E\u7D42\u4E86\u6642\u523B (\u79D2\u306F\u4EFB\u610F)").option("--clear-start", "\u958B\u59CB\u6642\u523B\u3092\u30AF\u30EA\u30A2 (end \u3082\u81EA\u52D5\u30AF\u30EA\u30A2)").option("--clear-end", "\u7D42\u4E86\u6642\u523B\u306E\u307F\u30AF\u30EA\u30A2 (status \u306F running \u306B)").option("--reset", "\u5B9F\u7E3E\u3092\u5168\u30AF\u30EA\u30A2 (status \u2192 planned)").action(async (prefix, opts) => {
|
|
3310
3317
|
const ctx = await createAppContext();
|
|
3311
3318
|
const date = opts.date ?? todayDateString();
|
|
3312
3319
|
const tasks = await ctx.repos.task.findByDate(date);
|
|
3313
3320
|
const taskId = resolveTaskIdByPrefix(tasks, prefix);
|
|
3314
3321
|
const task = tasks.find((t) => t.id === taskId);
|
|
3315
|
-
if (opts.start !== void 0 && !
|
|
3316
|
-
throw new CliError(`--start must be HH:MM (got "${opts.start}").`);
|
|
3322
|
+
if (opts.start !== void 0 && !HHMMSS.test(opts.start)) {
|
|
3323
|
+
throw new CliError(`--start must be HH:MM or HH:MM:SS (got "${opts.start}").`);
|
|
3317
3324
|
}
|
|
3318
|
-
if (opts.end !== void 0 && !
|
|
3319
|
-
throw new CliError(`--end must be HH:MM (got "${opts.end}").`);
|
|
3325
|
+
if (opts.end !== void 0 && !HHMMSS.test(opts.end)) {
|
|
3326
|
+
throw new CliError(`--end must be HH:MM or HH:MM:SS (got "${opts.end}").`);
|
|
3320
3327
|
}
|
|
3321
3328
|
let startTime;
|
|
3322
3329
|
let endTime;
|
|
@@ -3451,15 +3458,15 @@ function timeBlocksArchiveCommand() {
|
|
|
3451
3458
|
console.log(`\u{1F4E6} Archived time-block: ${shortenId(archived.id)} ${archived.name}`);
|
|
3452
3459
|
});
|
|
3453
3460
|
}
|
|
3454
|
-
var
|
|
3461
|
+
var HHMM4 = /^([01]\d|2[0-3]):[0-5]\d$/;
|
|
3455
3462
|
var DATE_RE5 = /^\d{4}-\d{2}-\d{2}$/;
|
|
3456
3463
|
function timeBlocksCreateCommand() {
|
|
3457
3464
|
return new Command("create").description("\u30BF\u30A4\u30E0\u30D6\u30ED\u30C3\u30AF\u3092\u65B0\u898F\u4F5C\u6210\u3059\u308B").argument("<name>", "\u30BF\u30A4\u30E0\u30D6\u30ED\u30C3\u30AF\u540D (\u4F8B: morning / work / evening)").requiredOption("--start-time <HH:MM>", "\u958B\u59CB\u6642\u523B").requiredOption("--end-time <HH:MM>", "\u7D42\u4E86\u6642\u523B").requiredOption("--weekdays <csv>", "\u5BFE\u8C61\u66DC\u65E5 (0=\u65E5 \uFF5E 6=\u571F) \u306E\u30AB\u30F3\u30DE\u533A\u5207\u308A (\u4F8B: 1,2,3,4,5)").option("--project <id-prefix>", "\u30C7\u30D5\u30A9\u30EB\u30C8\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8 id (prefix)").option("--context <id-prefix>", "\u30C7\u30D5\u30A9\u30EB\u30C8\u30B3\u30F3\u30C6\u30AD\u30B9\u30C8 id (prefix)").option("--tag <id-prefix>", "\u30C7\u30D5\u30A9\u30EB\u30C8\u30BF\u30B0 id (prefix)\u3002\u8907\u6570\u6307\u5B9A\u53EF", collectTag7, []).option("--color <color>", "UI \u8272").option("--start-date <YYYY-MM-DD>", "\u6709\u52B9\u671F\u9593\u958B\u59CB\u65E5").option("--end-date <YYYY-MM-DD>", "\u6709\u52B9\u671F\u9593\u7D42\u4E86\u65E5").action(async (name, opts) => {
|
|
3458
3465
|
const ctx = await createAppContext();
|
|
3459
|
-
if (!
|
|
3466
|
+
if (!HHMM4.test(opts.startTime)) {
|
|
3460
3467
|
throw new CliError(`--start-time must be HH:MM (got "${opts.startTime}").`);
|
|
3461
3468
|
}
|
|
3462
|
-
if (!
|
|
3469
|
+
if (!HHMM4.test(opts.endTime)) {
|
|
3463
3470
|
throw new CliError(`--end-time must be HH:MM (got "${opts.endTime}").`);
|
|
3464
3471
|
}
|
|
3465
3472
|
const weekdays = parseWeekdaysList(opts.weekdays);
|
|
@@ -3554,7 +3561,7 @@ function timeBlocksListCommand() {
|
|
|
3554
3561
|
console.table(blocks.map(formatTimeBlockRow));
|
|
3555
3562
|
});
|
|
3556
3563
|
}
|
|
3557
|
-
var
|
|
3564
|
+
var HHMM5 = /^([01]\d|2[0-3]):[0-5]\d$/;
|
|
3558
3565
|
var DATE_RE6 = /^\d{4}-\d{2}-\d{2}$/;
|
|
3559
3566
|
var TIME_BLOCK_STATUSES = ["active", "archived"];
|
|
3560
3567
|
function isTimeBlockStatus(value) {
|
|
@@ -3565,10 +3572,10 @@ function timeBlocksUpdateCommand() {
|
|
|
3565
3572
|
const ctx = await createAppContext();
|
|
3566
3573
|
const all = await ctx.repos.timeBlock.findAll();
|
|
3567
3574
|
const id = resolveResourceIdByPrefix(all, prefix, "time-block");
|
|
3568
|
-
if (opts.startTime !== void 0 && !
|
|
3575
|
+
if (opts.startTime !== void 0 && !HHMM5.test(opts.startTime)) {
|
|
3569
3576
|
throw new CliError(`--start-time must be HH:MM (got "${opts.startTime}").`);
|
|
3570
3577
|
}
|
|
3571
|
-
if (opts.endTime !== void 0 && !
|
|
3578
|
+
if (opts.endTime !== void 0 && !HHMM5.test(opts.endTime)) {
|
|
3572
3579
|
throw new CliError(`--end-time must be HH:MM (got "${opts.endTime}").`);
|
|
3573
3580
|
}
|
|
3574
3581
|
if (opts.startDate !== void 0 && !DATE_RE6.test(opts.startDate)) {
|
|
@@ -3683,7 +3690,7 @@ function whoamiCommand() {
|
|
|
3683
3690
|
|
|
3684
3691
|
// src/index.ts
|
|
3685
3692
|
var program = new Command();
|
|
3686
|
-
program.name("taskchute").description("TaskChute CLI \u2014 CLI/MCP \u304B\u3089 TaskChute \u306B\u30A2\u30AF\u30BB\u30B9\u3059\u308B\u305F\u3081\u306E\u30A8\u30F3\u30C8\u30EA\u30DD\u30A4\u30F3\u30C8").version("0.
|
|
3693
|
+
program.name("taskchute").description("TaskChute CLI \u2014 CLI/MCP \u304B\u3089 TaskChute \u306B\u30A2\u30AF\u30BB\u30B9\u3059\u308B\u305F\u3081\u306E\u30A8\u30F3\u30C8\u30EA\u30DD\u30A4\u30F3\u30C8").version("0.3.0" );
|
|
3687
3694
|
program.addCommand(loginCommand());
|
|
3688
3695
|
program.addCommand(whoamiCommand());
|
|
3689
3696
|
program.addCommand(logoutCommand());
|