@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 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
- base.setHours(Number(m[1]), Number(m[2]), 0, 0);
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 = toTimeString(now);
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: toTimeString(interruptionStartedAt),
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 ? toTimeString(startedAt) : void 0,
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 HHMM4 = /^([01]\d|2[0-3]):[0-5]\d$/;
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 && !HHMM4.test(opts.start)) {
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 && !HHMM4.test(opts.end)) {
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 HHMM5 = /^([01]\d|2[0-3]):[0-5]\d$/;
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 (!HHMM5.test(opts.startTime)) {
3466
+ if (!HHMM4.test(opts.startTime)) {
3460
3467
  throw new CliError(`--start-time must be HH:MM (got "${opts.startTime}").`);
3461
3468
  }
3462
- if (!HHMM5.test(opts.endTime)) {
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 HHMM6 = /^([01]\d|2[0-3]):[0-5]\d$/;
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 && !HHMM6.test(opts.startTime)) {
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 && !HHMM6.test(opts.endTime)) {
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.2.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());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fukuchang/taskchute-cli",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "TaskChute CLI — E2EE 暗号化された TaskChute タスクをターミナルから操作するための CLI",
5
5
  "license": "MIT",
6
6
  "author": "fukuchang",