@fukuchang/taskchute-cli 0.4.0 → 0.6.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 +12 -4
- package/README.md +55 -52
- package/dist/index.js +208 -129
- package/package.json +7 -7
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
このパッケージの変更履歴。[Keep a Changelog](https://keepachangelog.com/) 準拠。
|
|
4
4
|
|
|
5
|
+
## [0.6.0] - 2026-10-22 — Node.js セキュリティ修正版を要求
|
|
6
|
+
|
|
7
|
+
### Changed
|
|
8
|
+
|
|
9
|
+
- 対応 Node.js を 22.23.4 以降の22系、24.22.1 以降の24系、26.12.1 以降の26系に変更。
|
|
10
|
+
- Node.js 20 のサポートを終了し、ビルド対象を Node.js 22 に変更。
|
|
11
|
+
- CLI の更新とは別に、実行環境の Node.js 本体も更新する必要があります。
|
|
12
|
+
|
|
5
13
|
## [0.3.0] - 2026-05-28 — 実績時刻の秒精度サポート
|
|
6
14
|
|
|
7
15
|
### Added
|
|
@@ -87,7 +95,7 @@ Web の Routine / TimeBlock ページの write 操作を CLI から完結でき
|
|
|
87
95
|
- `taskchute routines delete <id-prefix>` (alias `rm`) — 物理削除 (task の
|
|
88
96
|
routine_id は NULL 化)
|
|
89
97
|
- `taskchute time-blocks create <name> --start-time HH:MM --end-time HH:MM
|
|
90
|
-
|
|
98
|
+
--weekdays 1,2,3,4,5 [...]` — タイムブロック新規作成
|
|
91
99
|
- `taskchute time-blocks update / archive / delete (rm)` (同パターン)
|
|
92
100
|
- `src/format/parse-frequency.ts` を新設。`parseFrequencySpec` で `--frequency`
|
|
93
101
|
の短縮表記を domain の discriminated union に変換。`parseWeekdaysList` で
|
|
@@ -212,9 +220,9 @@ Web の Today で当たり前にできる task 操作を CLI からも全部で
|
|
|
212
220
|
|
|
213
221
|
- npm provenance (sigstore attestation) は付与していない。sigstore は
|
|
214
222
|
private repo の OIDC token を検証対象外としているため、private repo
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
223
|
+
- provenance は構造上成立しない。Trusted Publisher (OIDC 限定 publish)
|
|
224
|
+
の保証自体は維持されている。将来 repo を public 化する判断をした
|
|
225
|
+
時点で `--provenance` を有効化する。
|
|
218
226
|
|
|
219
227
|
## [0.1.0] - 2026-05-23
|
|
220
228
|
|
package/README.md
CHANGED
|
@@ -6,10 +6,13 @@ TaskChute (E2EE 暗号化されたタスク管理アプリ) のタスクをタ
|
|
|
6
6
|
|
|
7
7
|
## 必要なもの
|
|
8
8
|
|
|
9
|
-
- Node.js
|
|
9
|
+
- Node.js 22.23.4 以降の22系 / 24.22.1 以降の24系 / 26.12.1 以降の26系(24系を推奨、各系統の最新パッチ版を使用)
|
|
10
10
|
- TaskChute Web アプリで作成済みのアカウント
|
|
11
11
|
- E2EE セットアップ (Web アプリの「Settings → データの暗号化」で passphrase を設定済み)
|
|
12
12
|
|
|
13
|
+
[2026年10月のセキュリティ修正](https://nodejs.org/en/blog/vulnerability/october-2026-security-releases)を含む Node.js が必要です。
|
|
14
|
+
CLI の更新だけでは Node.js 本体は更新されません。実行環境の Node.js も更新してください。
|
|
15
|
+
|
|
13
16
|
> ⚠️ タスクデータは end-to-end 暗号化されているため、復号には passphrase が必須。
|
|
14
17
|
> Web アプリで E2EE をセットアップしていない状態では CLI からタスクを操作できない。
|
|
15
18
|
|
|
@@ -31,11 +34,11 @@ taskchute --help
|
|
|
31
34
|
|
|
32
35
|
CLI は Supabase に直接接続するため、接続情報を環境変数で渡す必要がある。
|
|
33
36
|
|
|
34
|
-
| 変数名
|
|
35
|
-
|
|
|
36
|
-
| `TASKCHUTE_SUPABASE_URL`
|
|
37
|
-
| `TASKCHUTE_SUPABASE_PUBLISHABLE_KEY` | 必須 | Supabase の publishable (anon) key
|
|
38
|
-
| `TASKCHUTE_PASSPHRASE`
|
|
37
|
+
| 変数名 | 必須 | 用途 |
|
|
38
|
+
| ------------------------------------ | ---- | ------------------------------------------------------------------ |
|
|
39
|
+
| `TASKCHUTE_SUPABASE_URL` | 必須 | Supabase プロジェクトの URL |
|
|
40
|
+
| `TASKCHUTE_SUPABASE_PUBLISHABLE_KEY` | 必須 | Supabase の publishable (anon) key |
|
|
41
|
+
| `TASKCHUTE_PASSPHRASE` | 任意 | E2EE passphrase。設定すれば対話プロンプトをスキップ。CI/自動化向け |
|
|
39
42
|
|
|
40
43
|
`.zshrc` などに書いておくのが楽。
|
|
41
44
|
|
|
@@ -68,52 +71,52 @@ taskchute tasks done <taskId-or-index>
|
|
|
68
71
|
|
|
69
72
|
## コマンド一覧
|
|
70
73
|
|
|
71
|
-
| Command
|
|
72
|
-
|
|
|
73
|
-
| `taskchute login`
|
|
74
|
-
| `taskchute whoami`
|
|
75
|
-
| `taskchute logout`
|
|
76
|
-
| `taskchute unlock`
|
|
77
|
-
| `taskchute lock`
|
|
78
|
-
| `taskchute tasks list [--date YYYY-MM-DD]`
|
|
79
|
-
| `taskchute tasks add <title> [--estimated 30m] [--date <date>] [--project <prefix>] [--context <prefix>] [--tag <prefix>...] [--scheduled HH:MM] [--description <text>] [--notify] [--background]`
|
|
80
|
-
| `taskchute tasks update <id-prefix> [--title ...] [--description ...] [--clear-description] [--estimated 1h] [--project <prefix>] [--clear-project] [--context <prefix>] [--clear-context] [--tag <prefix>...] [--no-tags] [--scheduled HH:MM] [--clear-scheduled] [--notify] [--no-notify] [--background] [--no-background]` | フィールド部分更新。`--clear-X` で明示クリア、`--no-tags` で全タグ外し
|
|
81
|
-
| `taskchute tasks delete <id-prefix> [--hard]` / `rm`
|
|
82
|
-
| `taskchute tasks start <id-prefix>`
|
|
83
|
-
| `taskchute tasks pause <id-prefix>`
|
|
84
|
-
| `taskchute tasks done <id-prefix>`
|
|
85
|
-
| `taskchute tasks skip <id-prefix>`
|
|
86
|
-
| `taskchute tasks reopen <id-prefix>`
|
|
87
|
-
| `taskchute tasks recent [--days 90] [--query <text>] [--limit 100]`
|
|
88
|
-
| `taskchute projects list [--archived
|
|
89
|
-
| `taskchute tags list [--archived
|
|
90
|
-
| `taskchute contexts list [--archived
|
|
91
|
-
| `taskchute routines list [--paused
|
|
92
|
-
| `taskchute time-blocks list [--archived
|
|
93
|
-
| `taskchute projects create <name> [--description ...] [--color ...] [--parent <prefix>] [--started-at YYYY-MM-DD] [--target-end YYYY-MM-DD]`
|
|
94
|
-
| `taskchute projects update <id-prefix> [--name ...] [--description ...] [--clear-description] [--color ...] [--clear-color] [--parent <prefix>] [--clear-parent] [--started-at YYYY-MM-DD] [--clear-started-at] [--target-end YYYY-MM-DD] [--clear-target-end]`
|
|
95
|
-
| `taskchute projects archive <id-prefix>`
|
|
96
|
-
| `taskchute projects delete <id-prefix>` / `rm`
|
|
97
|
-
| `taskchute tags create <name> [--description ...] [--color ...]`
|
|
98
|
-
| `taskchute tags update <id-prefix> [--name ...] [--description ...] [--clear-description] [--color ...] [--clear-color]`
|
|
99
|
-
| `taskchute tags archive <id-prefix>`
|
|
100
|
-
| `taskchute tags delete <id-prefix>` / `rm`
|
|
101
|
-
| `taskchute contexts create <name> [--description ...] [--icon ...] [--color ...]`
|
|
102
|
-
| `taskchute contexts update <id-prefix> [--name ...] [--description ...] [--clear-description] [--icon ...] [--clear-icon] [--color ...] [--clear-color]`
|
|
103
|
-
| `taskchute contexts archive <id-prefix>`
|
|
104
|
-
| `taskchute contexts delete <id-prefix>` / `rm`
|
|
105
|
-
| `taskchute routines create <title> --estimated <dur> --frequency <spec> [...]`
|
|
106
|
-
| `taskchute routines update <id-prefix> [...]`
|
|
107
|
-
| `taskchute routines archive <id-prefix>`
|
|
108
|
-
| `taskchute routines delete <id-prefix>` / `rm`
|
|
109
|
-
| `taskchute time-blocks create <name> --start-time HH:MM --end-time HH:MM --weekdays 1,2,3 [...]`
|
|
110
|
-
| `taskchute time-blocks update <id-prefix> [...]`
|
|
111
|
-
| `taskchute time-blocks archive <id-prefix>`
|
|
112
|
-
| `taskchute time-blocks delete <id-prefix>` / `rm`
|
|
113
|
-
| `taskchute tasks set-actual-times <id-prefix> [--start HH:MM] [--end HH:MM] [--clear-start] [--clear-end] [--reset]`
|
|
114
|
-
| `taskchute tasks interrupt <target-id-prefix> <title> --estimated <dur> [...]`
|
|
115
|
-
| `taskchute tasks parallel <title> --estimated <dur> [--no-start-immediately] [--background] [...]`
|
|
116
|
-
| `taskchute tasks reorder <id-prefix> --order-index <n> [--scheduled HH:MM] [--clear-scheduled]`
|
|
74
|
+
| Command | 説明 |
|
|
75
|
+
| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- | ------------------ | ----------------------------------------------------------------- |
|
|
76
|
+
| `taskchute login` | email + password で Supabase にログインし、session を OS keyring に保存 |
|
|
77
|
+
| `taskchute whoami` | 現在ログイン中のユーザーを表示 |
|
|
78
|
+
| `taskchute logout` | ログアウト (keyring から session 削除) |
|
|
79
|
+
| `taskchute unlock` | E2EE passphrase を keyring に保存し、以降のコマンドを対話なしに走らせる |
|
|
80
|
+
| `taskchute lock` | keyring から passphrase を削除 (session は維持) |
|
|
81
|
+
| `taskchute tasks list [--date YYYY-MM-DD]` | タスク一覧表示 |
|
|
82
|
+
| `taskchute tasks add <title> [--estimated 30m] [--date <date>] [--project <prefix>] [--context <prefix>] [--tag <prefix>...] [--scheduled HH:MM] [--description <text>] [--notify] [--background]` | タスク追加 |
|
|
83
|
+
| `taskchute tasks update <id-prefix> [--title ...] [--description ...] [--clear-description] [--estimated 1h] [--project <prefix>] [--clear-project] [--context <prefix>] [--clear-context] [--tag <prefix>...] [--no-tags] [--scheduled HH:MM] [--clear-scheduled] [--notify] [--no-notify] [--background] [--no-background]` | フィールド部分更新。`--clear-X` で明示クリア、`--no-tags` で全タグ外し |
|
|
84
|
+
| `taskchute tasks delete <id-prefix> [--hard]` / `rm` | タスク削除。既定は Routine 由来なら今日以降は skip 記録、`--hard` で物理削除のみ |
|
|
85
|
+
| `taskchute tasks start <id-prefix>` | タスク開始 |
|
|
86
|
+
| `taskchute tasks pause <id-prefix>` | タスク中断 |
|
|
87
|
+
| `taskchute tasks done <id-prefix>` | タスク完了 |
|
|
88
|
+
| `taskchute tasks skip <id-prefix>` | タスクをスキップ (status → skipped) |
|
|
89
|
+
| `taskchute tasks reopen <id-prefix>` | completed / skipped を planned に戻す |
|
|
90
|
+
| `taskchute tasks recent [--days 90] [--query <text>] [--limit 100]` | 過去 N 日のタスク (fuzzy match 対応) |
|
|
91
|
+
| `taskchute projects list [--archived | --all]` | プロジェクト一覧 |
|
|
92
|
+
| `taskchute tags list [--archived | --all]` | タグ一覧 |
|
|
93
|
+
| `taskchute contexts list [--archived | --all]` | コンテキスト一覧 |
|
|
94
|
+
| `taskchute routines list [--paused | --archived | --all]` | ルーチン一覧 (frequency は `weekly:1,3,5` のような短縮表記で表示) |
|
|
95
|
+
| `taskchute time-blocks list [--archived | --all]` | タイムブロック一覧 |
|
|
96
|
+
| `taskchute projects create <name> [--description ...] [--color ...] [--parent <prefix>] [--started-at YYYY-MM-DD] [--target-end YYYY-MM-DD]` | プロジェクト新規作成 |
|
|
97
|
+
| `taskchute projects update <id-prefix> [--name ...] [--description ...] [--clear-description] [--color ...] [--clear-color] [--parent <prefix>] [--clear-parent] [--started-at YYYY-MM-DD] [--clear-started-at] [--target-end YYYY-MM-DD] [--clear-target-end]` | プロジェクト部分更新 |
|
|
98
|
+
| `taskchute projects archive <id-prefix>` | アーカイブ (タスクの project_id 維持) |
|
|
99
|
+
| `taskchute projects delete <id-prefix>` / `rm` | 物理削除 (タスクの project_id は NULL 化) |
|
|
100
|
+
| `taskchute tags create <name> [--description ...] [--color ...]` | タグ新規作成 |
|
|
101
|
+
| `taskchute tags update <id-prefix> [--name ...] [--description ...] [--clear-description] [--color ...] [--clear-color]` | タグ部分更新 |
|
|
102
|
+
| `taskchute tags archive <id-prefix>` | アーカイブ (タスクの tag_ids 配列維持) |
|
|
103
|
+
| `taskchute tags delete <id-prefix>` / `rm` | 物理削除 (tag_ids 配列に孤児 id が残るので注意) |
|
|
104
|
+
| `taskchute contexts create <name> [--description ...] [--icon ...] [--color ...]` | コンテキスト新規作成 |
|
|
105
|
+
| `taskchute contexts update <id-prefix> [--name ...] [--description ...] [--clear-description] [--icon ...] [--clear-icon] [--color ...] [--clear-color]` | コンテキスト部分更新 |
|
|
106
|
+
| `taskchute contexts archive <id-prefix>` | アーカイブ |
|
|
107
|
+
| `taskchute contexts delete <id-prefix>` / `rm` | 物理削除 (タスクの context_id は NULL 化) |
|
|
108
|
+
| `taskchute routines create <title> --estimated <dur> --frequency <spec> [...]` | ルーチン新規作成。`--frequency` は `daily` / `weekly:1,3,5` / `monthly:1,15` / `custom:CRON` |
|
|
109
|
+
| `taskchute routines update <id-prefix> [...]` | ルーチン部分更新 (**未来 planned task に自動伝播**) |
|
|
110
|
+
| `taskchute routines archive <id-prefix>` | アーカイブ (既存 task の routine_id 維持) |
|
|
111
|
+
| `taskchute routines delete <id-prefix>` / `rm` | 物理削除 (task の routine_id は NULL 化) |
|
|
112
|
+
| `taskchute time-blocks create <name> --start-time HH:MM --end-time HH:MM --weekdays 1,2,3 [...]` | タイムブロック新規作成 (`weekdays` は 0=日 ~ 6=土) |
|
|
113
|
+
| `taskchute time-blocks update <id-prefix> [...]` | タイムブロック部分更新 |
|
|
114
|
+
| `taskchute time-blocks archive <id-prefix>` | アーカイブ |
|
|
115
|
+
| `taskchute time-blocks delete <id-prefix>` / `rm` | 物理削除 |
|
|
116
|
+
| `taskchute tasks set-actual-times <id-prefix> [--start HH:MM] [--end HH:MM] [--clear-start] [--clear-end] [--reset]` | 実績開始/終了時刻を直接指定 (Play 押し忘れリカバリ用) |
|
|
117
|
+
| `taskchute tasks interrupt <target-id-prefix> <title> --estimated <dur> [...]` | 走行中タスクを「割り込み」で分割 (3 タスクを 1 操作で生成) |
|
|
118
|
+
| `taskchute tasks parallel <title> --estimated <dur> [--no-start-immediately] [--background] [...]` | 並列タスク追加 (既定で即計測開始) |
|
|
119
|
+
| `taskchute tasks reorder <id-prefix> --order-index <n> [--scheduled HH:MM] [--clear-scheduled]` | 並び順 (orderIndex) と所属タイムブロックを直接指定 |
|
|
117
120
|
|
|
118
121
|
詳細オプションは `taskchute <command> --help` で確認。
|
|
119
122
|
|
package/dist/index.js
CHANGED
|
@@ -22,9 +22,7 @@ function readSupabaseConfigFromEnv() {
|
|
|
22
22
|
const url = process.env.TASKCHUTE_SUPABASE_URL;
|
|
23
23
|
const publishableKey = process.env.TASKCHUTE_SUPABASE_PUBLISHABLE_KEY;
|
|
24
24
|
if (!url || !publishableKey) {
|
|
25
|
-
throw new Error(
|
|
26
|
-
`Supabase \u306E\u74B0\u5883\u5909\u6570\u304C\u898B\u3064\u304B\u3089\u306A\u3044\u3002${ENV_URL} \u3068 ${ENV_KEY} \u3092\u8A2D\u5B9A\u3057\u3066\u306D\u301C`
|
|
27
|
-
);
|
|
25
|
+
throw new Error(`Supabase \u306E\u74B0\u5883\u5909\u6570\u304C\u898B\u3064\u304B\u3089\u306A\u3044\u3002${ENV_URL} \u3068 ${ENV_KEY} \u3092\u8A2D\u5B9A\u3057\u3066\u306D\u301C`);
|
|
28
26
|
}
|
|
29
27
|
return { url, publishableKey };
|
|
30
28
|
}
|
|
@@ -203,13 +201,10 @@ async function deriveKeyMaterial(password4, salt, params = DEFAULT_ARGON2ID_PARA
|
|
|
203
201
|
return copyToArrayBuffer(hash);
|
|
204
202
|
}
|
|
205
203
|
async function importKEK(material) {
|
|
206
|
-
return crypto.subtle.importKey(
|
|
207
|
-
"
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
false,
|
|
211
|
-
["wrapKey", "unwrapKey"]
|
|
212
|
-
);
|
|
204
|
+
return crypto.subtle.importKey("raw", material, { name: "AES-GCM" }, false, [
|
|
205
|
+
"wrapKey",
|
|
206
|
+
"unwrapKey"
|
|
207
|
+
]);
|
|
213
208
|
}
|
|
214
209
|
async function deriveKEK(password4, salt, params = DEFAULT_ARGON2ID_PARAMS) {
|
|
215
210
|
const material = await deriveKeyMaterial(password4, salt, params);
|
|
@@ -413,11 +408,7 @@ async function unlockMasterKey(snapshot, method) {
|
|
|
413
408
|
snapshot.passphrase.kdfParams
|
|
414
409
|
);
|
|
415
410
|
try {
|
|
416
|
-
return await unwrapMasterKey(
|
|
417
|
-
snapshot.passphrase.wrapped,
|
|
418
|
-
snapshot.passphrase.iv,
|
|
419
|
-
kek
|
|
420
|
-
);
|
|
411
|
+
return await unwrapMasterKey(snapshot.passphrase.wrapped, snapshot.passphrase.iv, kek);
|
|
421
412
|
} catch {
|
|
422
413
|
throw new UnlockError("Wrong passphrase", "wrong-key");
|
|
423
414
|
}
|
|
@@ -433,11 +424,7 @@ async function unlockMasterKey(snapshot, method) {
|
|
|
433
424
|
);
|
|
434
425
|
const kek = await importKEK(material);
|
|
435
426
|
try {
|
|
436
|
-
return await unwrapMasterKey(
|
|
437
|
-
snapshot.recovery.wrapped,
|
|
438
|
-
snapshot.recovery.iv,
|
|
439
|
-
kek
|
|
440
|
-
);
|
|
427
|
+
return await unwrapMasterKey(snapshot.recovery.wrapped, snapshot.recovery.iv, kek);
|
|
441
428
|
} catch {
|
|
442
429
|
throw new UnlockError("Wrong recovery key", "wrong-key");
|
|
443
430
|
}
|
|
@@ -491,13 +478,10 @@ function dateToIsoOrNull(date) {
|
|
|
491
478
|
// ../infra-supabase/src/supabase-context-repository.ts
|
|
492
479
|
async function rowToContext(row, cryptoSession) {
|
|
493
480
|
const [name, description] = await Promise.all([
|
|
494
|
-
decryptRequiredTextFromWire(
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
row.key_id,
|
|
499
|
-
{ entityId: row.id, field: "name" }
|
|
500
|
-
),
|
|
481
|
+
decryptRequiredTextFromWire(cryptoSession, row.name_ciphertext, row.name_iv, row.key_id, {
|
|
482
|
+
entityId: row.id,
|
|
483
|
+
field: "name"
|
|
484
|
+
}),
|
|
501
485
|
decryptOptionalTextFromWire(
|
|
502
486
|
cryptoSession,
|
|
503
487
|
row.description_ciphertext,
|
|
@@ -614,13 +598,10 @@ var SupabaseContextRepository = class {
|
|
|
614
598
|
// ../infra-supabase/src/supabase-project-repository.ts
|
|
615
599
|
async function rowToProject(row, cryptoSession) {
|
|
616
600
|
const [name, description] = await Promise.all([
|
|
617
|
-
decryptRequiredTextFromWire(
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
row.key_id,
|
|
622
|
-
{ entityId: row.id, field: "name" }
|
|
623
|
-
),
|
|
601
|
+
decryptRequiredTextFromWire(cryptoSession, row.name_ciphertext, row.name_iv, row.key_id, {
|
|
602
|
+
entityId: row.id,
|
|
603
|
+
field: "name"
|
|
604
|
+
}),
|
|
624
605
|
decryptOptionalTextFromWire(
|
|
625
606
|
cryptoSession,
|
|
626
607
|
row.description_ciphertext,
|
|
@@ -774,13 +755,10 @@ var SupabaseRoutineExpansionRepository = class {
|
|
|
774
755
|
// ../infra-supabase/src/supabase-routine-repository.ts
|
|
775
756
|
async function rowToRoutine(row, cryptoSession) {
|
|
776
757
|
const [title, description] = await Promise.all([
|
|
777
|
-
decryptRequiredTextFromWire(
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
row.key_id,
|
|
782
|
-
{ entityId: row.id, field: "title" }
|
|
783
|
-
),
|
|
758
|
+
decryptRequiredTextFromWire(cryptoSession, row.title_ciphertext, row.title_iv, row.key_id, {
|
|
759
|
+
entityId: row.id,
|
|
760
|
+
field: "title"
|
|
761
|
+
}),
|
|
784
762
|
decryptOptionalTextFromWire(
|
|
785
763
|
cryptoSession,
|
|
786
764
|
row.description_ciphertext,
|
|
@@ -921,13 +899,10 @@ var SupabaseRoutineRepository = class {
|
|
|
921
899
|
// ../infra-supabase/src/supabase-tag-repository.ts
|
|
922
900
|
async function rowToTag(row, cryptoSession) {
|
|
923
901
|
const [name, description] = await Promise.all([
|
|
924
|
-
decryptRequiredTextFromWire(
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
row.key_id,
|
|
929
|
-
{ entityId: row.id, field: "name" }
|
|
930
|
-
),
|
|
902
|
+
decryptRequiredTextFromWire(cryptoSession, row.name_ciphertext, row.name_iv, row.key_id, {
|
|
903
|
+
entityId: row.id,
|
|
904
|
+
field: "name"
|
|
905
|
+
}),
|
|
931
906
|
decryptOptionalTextFromWire(
|
|
932
907
|
cryptoSession,
|
|
933
908
|
row.description_ciphertext,
|
|
@@ -1038,13 +1013,10 @@ var SupabaseTagRepository = class {
|
|
|
1038
1013
|
// ../infra-supabase/src/supabase-task-note-repository.ts
|
|
1039
1014
|
async function rowToTaskNote(row, cryptoSession) {
|
|
1040
1015
|
const [body, metaJson, historyJson] = await Promise.all([
|
|
1041
|
-
decryptRequiredTextFromWire(
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
row.key_id,
|
|
1046
|
-
{ entityId: row.id, field: "body" }
|
|
1047
|
-
),
|
|
1016
|
+
decryptRequiredTextFromWire(cryptoSession, row.body_ciphertext, row.body_iv, row.key_id, {
|
|
1017
|
+
entityId: row.id,
|
|
1018
|
+
field: "body"
|
|
1019
|
+
}),
|
|
1048
1020
|
decryptOptionalTextFromWire(
|
|
1049
1021
|
cryptoSession,
|
|
1050
1022
|
row.metadata_ciphertext,
|
|
@@ -1135,9 +1107,7 @@ async function encryptTaskNoteFieldsInto(row, entityId, patch, cryptoSession) {
|
|
|
1135
1107
|
if (touched) {
|
|
1136
1108
|
const keyId = cryptoSession.getCurrentKeyId();
|
|
1137
1109
|
if (keyId === null) {
|
|
1138
|
-
throw new Error(
|
|
1139
|
-
"SupabaseTaskNoteRepository: session became locked mid-write"
|
|
1140
|
-
);
|
|
1110
|
+
throw new Error("SupabaseTaskNoteRepository: session became locked mid-write");
|
|
1141
1111
|
}
|
|
1142
1112
|
row.key_id = keyId;
|
|
1143
1113
|
}
|
|
@@ -1164,9 +1134,7 @@ var SupabaseTaskNoteRepository = class {
|
|
|
1164
1134
|
if (taskIds.length === 0) return result;
|
|
1165
1135
|
const { data, error } = await this.supabase.from("task_notes").select("*").in("task_id", taskIds).order("created_at", { ascending: true });
|
|
1166
1136
|
if (error) throw error;
|
|
1167
|
-
const decoded = await Promise.all(
|
|
1168
|
-
data.map((row) => rowToTaskNote(row, this.cryptoSession))
|
|
1169
|
-
);
|
|
1137
|
+
const decoded = await Promise.all(data.map((row) => rowToTaskNote(row, this.cryptoSession)));
|
|
1170
1138
|
for (const note of decoded) {
|
|
1171
1139
|
const bucket = result.get(note.taskId);
|
|
1172
1140
|
if (bucket) bucket.push(note);
|
|
@@ -1215,13 +1183,10 @@ var SupabaseTaskNoteRepository = class {
|
|
|
1215
1183
|
// ../infra-supabase/src/supabase-task-repository.ts
|
|
1216
1184
|
async function rowToTask(row, cryptoSession) {
|
|
1217
1185
|
const [title, description] = await Promise.all([
|
|
1218
|
-
decryptRequiredTextFromWire(
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
row.key_id,
|
|
1223
|
-
{ entityId: row.id, field: "title" }
|
|
1224
|
-
),
|
|
1186
|
+
decryptRequiredTextFromWire(cryptoSession, row.title_ciphertext, row.title_iv, row.key_id, {
|
|
1187
|
+
entityId: row.id,
|
|
1188
|
+
field: "title"
|
|
1189
|
+
}),
|
|
1225
1190
|
decryptOptionalTextFromWire(
|
|
1226
1191
|
cryptoSession,
|
|
1227
1192
|
row.description_ciphertext,
|
|
@@ -1665,20 +1630,41 @@ async function startTask(repo, taskId) {
|
|
|
1665
1630
|
return repo.update(taskId, patch);
|
|
1666
1631
|
}
|
|
1667
1632
|
async function pauseTask(repo, taskId) {
|
|
1668
|
-
const
|
|
1669
|
-
if (!
|
|
1670
|
-
if (!
|
|
1633
|
+
const target = await repo.findById(taskId);
|
|
1634
|
+
if (!target) throw new Error(`Task not found: ${taskId}`);
|
|
1635
|
+
if (!target.currentSession) {
|
|
1671
1636
|
throw new Error(`Task is not currently running: ${taskId}`);
|
|
1672
1637
|
}
|
|
1673
|
-
const
|
|
1674
|
-
|
|
1675
|
-
|
|
1638
|
+
const now = /* @__PURE__ */ new Date();
|
|
1639
|
+
const closedSession = {
|
|
1640
|
+
startedAt: target.currentSession.startedAt,
|
|
1641
|
+
finishedAt: now
|
|
1676
1642
|
};
|
|
1677
|
-
|
|
1678
|
-
status: "
|
|
1679
|
-
sessions: [...
|
|
1643
|
+
const finishedOriginal = await repo.update(target.id, {
|
|
1644
|
+
status: "completed",
|
|
1645
|
+
sessions: [...target.sessions, closedSession],
|
|
1680
1646
|
currentSession: void 0
|
|
1681
1647
|
});
|
|
1648
|
+
const elapsed = calculateActualSeconds(finishedOriginal);
|
|
1649
|
+
const continuation = await repo.create({
|
|
1650
|
+
date: target.date,
|
|
1651
|
+
title: target.title,
|
|
1652
|
+
description: target.description,
|
|
1653
|
+
estimatedSeconds: Math.max(0, Math.floor(target.estimatedSeconds - elapsed)),
|
|
1654
|
+
sessions: [],
|
|
1655
|
+
status: "planned",
|
|
1656
|
+
orderIndex: target.orderIndex + 1,
|
|
1657
|
+
splitFromTaskId: target.id,
|
|
1658
|
+
projectId: target.projectId,
|
|
1659
|
+
contextId: target.contextId,
|
|
1660
|
+
// continuation は「中断したタスクの残り」。中断時刻を予定時刻にすることで
|
|
1661
|
+
// ソート上は元タスクの直後 (effectiveTime 同値 + orderIndex 順) に並び、
|
|
1662
|
+
// TimeBlock 境界をまたいだ場合も自然に新しいブロックへ追随する。
|
|
1663
|
+
scheduledStartTime: toTimeString(now),
|
|
1664
|
+
tagIds: target.tagIds,
|
|
1665
|
+
isBackground: target.isBackground
|
|
1666
|
+
});
|
|
1667
|
+
return { finishedOriginal, continuation };
|
|
1682
1668
|
}
|
|
1683
1669
|
async function skipTask(repo, taskId) {
|
|
1684
1670
|
const task = await repo.findById(taskId);
|
|
@@ -1833,19 +1819,13 @@ function buildSetActualTimesPatch(task, input2) {
|
|
|
1833
1819
|
// ../domain/src/task-reorder.ts
|
|
1834
1820
|
function computeNextOrderIndex(existingTasks) {
|
|
1835
1821
|
if (existingTasks.length === 0) return 10;
|
|
1836
|
-
const max = existingTasks.reduce(
|
|
1837
|
-
(acc, t) => t.orderIndex > acc ? t.orderIndex : acc,
|
|
1838
|
-
0
|
|
1839
|
-
);
|
|
1822
|
+
const max = existingTasks.reduce((acc, t) => t.orderIndex > acc ? t.orderIndex : acc, 0);
|
|
1840
1823
|
return max + 10;
|
|
1841
1824
|
}
|
|
1842
1825
|
|
|
1843
1826
|
// ../domain/src/task-suggest.ts
|
|
1844
1827
|
function normalizeForSearch(str) {
|
|
1845
|
-
return str.trim().toLowerCase().replace(
|
|
1846
|
-
/[ぁ-ゖ]/g,
|
|
1847
|
-
(ch) => String.fromCharCode(ch.charCodeAt(0) + 96)
|
|
1848
|
-
);
|
|
1828
|
+
return str.trim().toLowerCase().replace(/[ぁ-ゖ]/g, (ch) => String.fromCharCode(ch.charCodeAt(0) + 96));
|
|
1849
1829
|
}
|
|
1850
1830
|
function fuzzyScore(query, target) {
|
|
1851
1831
|
const q = query;
|
|
@@ -1908,7 +1888,6 @@ function findSimilarTasks(query, tasks, limit = 5) {
|
|
|
1908
1888
|
function describeStatusTransition(from, to) {
|
|
1909
1889
|
if (from === "planned" && to === "running") return "\u958B\u59CB";
|
|
1910
1890
|
if (from === "paused" && to === "running") return "\u518D\u958B";
|
|
1911
|
-
if (from === "running" && to === "paused") return "\u4E00\u6642\u505C\u6B62";
|
|
1912
1891
|
if (to === "completed") return "\u5B8C\u4E86";
|
|
1913
1892
|
if (to === "skipped") return "\u30B9\u30AD\u30C3\u30D7";
|
|
1914
1893
|
if (to === "planned") return "\u672A\u7740\u624B\u306B\u623B\u3059";
|
|
@@ -2158,6 +2137,42 @@ async function interruptTaskWithAudit(taskRepo, taskNoteRepo, params) {
|
|
|
2158
2137
|
}
|
|
2159
2138
|
return result;
|
|
2160
2139
|
}
|
|
2140
|
+
async function pauseTaskWithAudit(taskRepo, taskNoteRepo, taskId) {
|
|
2141
|
+
const result = await pauseTask(taskRepo, taskId);
|
|
2142
|
+
const notes = buildPauseSplitNoteInputs(result.finishedOriginal, result.continuation);
|
|
2143
|
+
try {
|
|
2144
|
+
await Promise.all(notes.map((input2) => taskNoteRepo.create(input2)));
|
|
2145
|
+
} catch (err) {
|
|
2146
|
+
console.error("[audit] failed to record pause split notes", {
|
|
2147
|
+
sourceTaskId: result.finishedOriginal.id,
|
|
2148
|
+
err
|
|
2149
|
+
});
|
|
2150
|
+
}
|
|
2151
|
+
return result;
|
|
2152
|
+
}
|
|
2153
|
+
function buildPauseSplitNoteInputs(source, continuation) {
|
|
2154
|
+
const sourceTitle = titleOrPlaceholder(source);
|
|
2155
|
+
return [
|
|
2156
|
+
{
|
|
2157
|
+
taskId: source.id,
|
|
2158
|
+
type: "split",
|
|
2159
|
+
body: `\u4E2D\u65AD\u3057\u3066\u6B8B\u4F5C\u696D\u3092\u5206\u5272\u3002\u7D9A\u304D\u306F\u5225\u30BF\u30B9\u30AF\u3067\u7D99\u7D9A\u3002`,
|
|
2160
|
+
metadata: {
|
|
2161
|
+
event: "pause-source",
|
|
2162
|
+
continuationTaskId: continuation.id
|
|
2163
|
+
}
|
|
2164
|
+
},
|
|
2165
|
+
{
|
|
2166
|
+
taskId: continuation.id,
|
|
2167
|
+
type: "split",
|
|
2168
|
+
body: `\u300C${sourceTitle}\u300D\u306E\u6B8B\u4F5C\u696D\u3068\u3057\u3066\u7D99\u7D9A\u3002`,
|
|
2169
|
+
metadata: {
|
|
2170
|
+
event: "pause-continuation",
|
|
2171
|
+
sourceTaskId: source.id
|
|
2172
|
+
}
|
|
2173
|
+
}
|
|
2174
|
+
];
|
|
2175
|
+
}
|
|
2161
2176
|
function buildSplitNoteInputs(source, interruption, continuation) {
|
|
2162
2177
|
const sourceTitle = titleOrPlaceholder(source);
|
|
2163
2178
|
const interruptionTitle = titleOrPlaceholder(interruption);
|
|
@@ -2194,8 +2209,6 @@ function buildSplitNoteInputs(source, interruption, continuation) {
|
|
|
2194
2209
|
}
|
|
2195
2210
|
];
|
|
2196
2211
|
}
|
|
2197
|
-
|
|
2198
|
-
// ../domain/src/routine-expansion.ts
|
|
2199
2212
|
function buildRoutineTaskPatch(routine) {
|
|
2200
2213
|
return {
|
|
2201
2214
|
title: routine.title,
|
|
@@ -2523,10 +2536,7 @@ function contextsListCommand() {
|
|
|
2523
2536
|
});
|
|
2524
2537
|
}
|
|
2525
2538
|
function contextsUpdateCommand() {
|
|
2526
|
-
return new Command("update").description("\u30B3\u30F3\u30C6\u30AD\u30B9\u30C8\u3092\u90E8\u5206\u66F4\u65B0\u3059\u308B").argument("<id-prefix>", "\u30B3\u30F3\u30C6\u30AD\u30B9\u30C8 id \u306E\u5148\u982D 4 \u6587\u5B57\u4EE5\u4E0A").option("--name <text>", "\u540D\u524D\u5909\u66F4").option("--description <text>", "\u8A73\u7D30\u8AAC\u660E").option("--clear-description", "\u8A73\u7D30\u8AAC\u660E\u3092\u7A7A\u306B\u3059\u308B").option("--icon <icon>", "\u7D75\u6587\u5B57\u307E\u305F\u306F\u30A2\u30A4\u30B3\u30F3\u540D").option("--clear-icon", "\u30A2\u30A4\u30B3\u30F3\u3092\u5916\u3059").option("--color <color>", "UI \u8272").option("--clear-color", "\u8272\u3092\u5916\u3059").option(
|
|
2527
|
-
"--notify-at-start",
|
|
2528
|
-
"\u3053\u306E\u30B3\u30F3\u30C6\u30AD\u30B9\u30C8\u3092\u4ED8\u3051\u308B\u65B0\u898F/\u66F4\u65B0\u30BF\u30B9\u30AF\u306E\u901A\u77E5\u3092 ON \u7D99\u627F\u3059\u308B"
|
|
2529
|
-
).option("--no-notify-at-start", "\u30B3\u30F3\u30C6\u30AD\u30B9\u30C8\u306E\u901A\u77E5\u7D99\u627F\u3092 OFF \u306B\u3059\u308B").action(async (prefix, opts) => {
|
|
2539
|
+
return new Command("update").description("\u30B3\u30F3\u30C6\u30AD\u30B9\u30C8\u3092\u90E8\u5206\u66F4\u65B0\u3059\u308B").argument("<id-prefix>", "\u30B3\u30F3\u30C6\u30AD\u30B9\u30C8 id \u306E\u5148\u982D 4 \u6587\u5B57\u4EE5\u4E0A").option("--name <text>", "\u540D\u524D\u5909\u66F4").option("--description <text>", "\u8A73\u7D30\u8AAC\u660E").option("--clear-description", "\u8A73\u7D30\u8AAC\u660E\u3092\u7A7A\u306B\u3059\u308B").option("--icon <icon>", "\u7D75\u6587\u5B57\u307E\u305F\u306F\u30A2\u30A4\u30B3\u30F3\u540D").option("--clear-icon", "\u30A2\u30A4\u30B3\u30F3\u3092\u5916\u3059").option("--color <color>", "UI \u8272").option("--clear-color", "\u8272\u3092\u5916\u3059").option("--notify-at-start", "\u3053\u306E\u30B3\u30F3\u30C6\u30AD\u30B9\u30C8\u3092\u4ED8\u3051\u308B\u65B0\u898F/\u66F4\u65B0\u30BF\u30B9\u30AF\u306E\u901A\u77E5\u3092 ON \u7D99\u627F\u3059\u308B").option("--no-notify-at-start", "\u30B3\u30F3\u30C6\u30AD\u30B9\u30C8\u306E\u901A\u77E5\u7D99\u627F\u3092 OFF \u306B\u3059\u308B").action(async (prefix, opts) => {
|
|
2530
2540
|
const ctx = await createAppContext();
|
|
2531
2541
|
const all = await ctx.repos.context.findAll();
|
|
2532
2542
|
const id = resolveResourceIdByPrefix(all, prefix, "context");
|
|
@@ -2622,7 +2632,8 @@ function projectsCreateCommand() {
|
|
|
2622
2632
|
if (opts.color !== void 0) input2.color = opts.color;
|
|
2623
2633
|
if (parentProjectId !== void 0) input2.parentProjectId = parentProjectId;
|
|
2624
2634
|
if (opts.startedAt !== void 0) input2.startedAt = /* @__PURE__ */ new Date(`${opts.startedAt}T00:00:00Z`);
|
|
2625
|
-
if (opts.targetEnd !== void 0)
|
|
2635
|
+
if (opts.targetEnd !== void 0)
|
|
2636
|
+
input2.targetEndDate = /* @__PURE__ */ new Date(`${opts.targetEnd}T00:00:00Z`);
|
|
2626
2637
|
const project = await ctx.repos.project.create(input2);
|
|
2627
2638
|
console.log(`\u2713 Created project: ${shortenId(project.id)} ${project.name}`);
|
|
2628
2639
|
});
|
|
@@ -2736,22 +2747,22 @@ function parseEstimated(input2) {
|
|
|
2736
2747
|
rest = "";
|
|
2737
2748
|
}
|
|
2738
2749
|
if (rest !== "" || hMatch === null && mMatch === null || hours > MAX_HOURS) {
|
|
2739
|
-
throw new Error(
|
|
2740
|
-
`Invalid estimated format: "${input2}". Use formats like "30m", "1h", "1h30m".`
|
|
2741
|
-
);
|
|
2750
|
+
throw new Error(`Invalid estimated format: "${input2}". Use formats like "30m", "1h", "1h30m".`);
|
|
2742
2751
|
}
|
|
2743
2752
|
return (hours * 60 + minutes) * 60;
|
|
2744
2753
|
}
|
|
2745
2754
|
|
|
2746
2755
|
// src/format/parse-frequency.ts
|
|
2756
|
+
var FREQUENCY_USAGE = "daily / weekly:1,3,5 / biweekly:1,5@2026-06-22 / monthly:1,15 / nth-weekday:2:5,4:5 / custom:CRON_STRING";
|
|
2757
|
+
var DATE_RE3 = /^\d{4}-\d{2}-\d{2}$/;
|
|
2747
2758
|
function parseFrequencySpec(spec) {
|
|
2748
2759
|
if (spec === "daily") return { type: "daily" };
|
|
2749
|
-
const
|
|
2750
|
-
if (
|
|
2751
|
-
throw new CliError(
|
|
2752
|
-
`Invalid --frequency "${spec}". Use one of: daily / weekly:1,3,5 / monthly:1,15 / custom:CRON_STRING`
|
|
2753
|
-
);
|
|
2760
|
+
const colonIndex = spec.indexOf(":");
|
|
2761
|
+
if (colonIndex < 0) {
|
|
2762
|
+
throw new CliError(`Invalid --frequency "${spec}". Use one of: ${FREQUENCY_USAGE}`);
|
|
2754
2763
|
}
|
|
2764
|
+
const typeKey = spec.slice(0, colonIndex);
|
|
2765
|
+
const rest = spec.slice(colonIndex + 1);
|
|
2755
2766
|
if (typeKey === "weekly") {
|
|
2756
2767
|
const weekdays = parseNumberList(rest, 0, 6);
|
|
2757
2768
|
if (weekdays.length === 0) {
|
|
@@ -2759,6 +2770,21 @@ function parseFrequencySpec(spec) {
|
|
|
2759
2770
|
}
|
|
2760
2771
|
return { type: "weekly", weekdays };
|
|
2761
2772
|
}
|
|
2773
|
+
if (typeKey === "biweekly") {
|
|
2774
|
+
const atIndex = rest.indexOf("@");
|
|
2775
|
+
if (atIndex < 0) {
|
|
2776
|
+
throw new CliError("biweekly frequency requires an anchor date, e.g. biweekly:1,5@2026-06-22");
|
|
2777
|
+
}
|
|
2778
|
+
const weekdays = parseNumberList(rest.slice(0, atIndex), 0, 6);
|
|
2779
|
+
const anchorDate = rest.slice(atIndex + 1);
|
|
2780
|
+
if (weekdays.length === 0) {
|
|
2781
|
+
throw new CliError("biweekly frequency must have at least 1 weekday.");
|
|
2782
|
+
}
|
|
2783
|
+
if (!DATE_RE3.test(anchorDate)) {
|
|
2784
|
+
throw new CliError(`Invalid anchor date "${anchorDate}". Use YYYY-MM-DD.`);
|
|
2785
|
+
}
|
|
2786
|
+
return { type: "weekly", weekdays, intervalWeeks: 2, anchorDate };
|
|
2787
|
+
}
|
|
2762
2788
|
if (typeKey === "monthly") {
|
|
2763
2789
|
const days = parseNumberList(rest, 1, 31);
|
|
2764
2790
|
if (days.length === 0) {
|
|
@@ -2766,13 +2792,33 @@ function parseFrequencySpec(spec) {
|
|
|
2766
2792
|
}
|
|
2767
2793
|
return { type: "monthly", days };
|
|
2768
2794
|
}
|
|
2795
|
+
if (typeKey === "nth-weekday") {
|
|
2796
|
+
const pairs = rest.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
2797
|
+
if (pairs.length === 0) {
|
|
2798
|
+
throw new CliError("nth-weekday frequency must have at least 1 (nth:weekday) pair.");
|
|
2799
|
+
}
|
|
2800
|
+
const occurrences = pairs.map((pair) => {
|
|
2801
|
+
const [nthRaw, weekdayRaw] = pair.split(":");
|
|
2802
|
+
if (!nthRaw || !weekdayRaw) {
|
|
2803
|
+
throw new CliError(`Invalid nth-weekday pair "${pair}". Use nth:weekday (e.g. 2:5).`);
|
|
2804
|
+
}
|
|
2805
|
+
const nth = Number(nthRaw);
|
|
2806
|
+
const weekday = Number(weekdayRaw);
|
|
2807
|
+
if (!Number.isInteger(nth) || !(nth >= 1 && nth <= 5 || nth === -1)) {
|
|
2808
|
+
throw new CliError(`Invalid nth "${nthRaw}" (must be 1-5 or -1 for last).`);
|
|
2809
|
+
}
|
|
2810
|
+
if (!Number.isInteger(weekday) || weekday < 0 || weekday > 6) {
|
|
2811
|
+
throw new CliError(`Invalid weekday "${weekdayRaw}" (must be 0-6).`);
|
|
2812
|
+
}
|
|
2813
|
+
return { nth, weekday };
|
|
2814
|
+
});
|
|
2815
|
+
return { type: "monthly-nth-weekday", occurrences };
|
|
2816
|
+
}
|
|
2769
2817
|
if (typeKey === "custom") {
|
|
2770
2818
|
if (rest.length === 0) throw new CliError("custom frequency requires a cron string.");
|
|
2771
2819
|
return { type: "custom", cron: rest };
|
|
2772
2820
|
}
|
|
2773
|
-
throw new CliError(
|
|
2774
|
-
`Unknown frequency type "${typeKey}". Use one of: daily / weekly:1,3,5 / monthly:1,15 / custom:CRON_STRING`
|
|
2775
|
-
);
|
|
2821
|
+
throw new CliError(`Unknown frequency type "${typeKey}". Use one of: ${FREQUENCY_USAGE}`);
|
|
2776
2822
|
}
|
|
2777
2823
|
function parseNumberList(csv, min, max) {
|
|
2778
2824
|
const parts = csv.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
@@ -2792,11 +2838,11 @@ function parseWeekdaysList(csv) {
|
|
|
2792
2838
|
|
|
2793
2839
|
// src/commands/routines/create.ts
|
|
2794
2840
|
var HHMM = /^([01]\d|2[0-3]):[0-5]\d$/;
|
|
2795
|
-
var
|
|
2841
|
+
var DATE_RE4 = /^\d{4}-\d{2}-\d{2}$/;
|
|
2796
2842
|
function routinesCreateCommand() {
|
|
2797
2843
|
return new Command("create").description("\u30EB\u30FC\u30C1\u30F3\u3092\u65B0\u898F\u4F5C\u6210\u3059\u308B").argument("<title>", "\u30EB\u30FC\u30C1\u30F3\u306E\u30BF\u30A4\u30C8\u30EB").requiredOption("-e, --estimated <duration>", "\u898B\u7A4D\u3082\u308A\u6642\u9593 (\u4F8B: 30m, 1h, 1h30m)").requiredOption(
|
|
2798
2844
|
"--frequency <spec>",
|
|
2799
|
-
"\u983B\u5EA6: daily / weekly:1,3,5 / monthly:1,15 / custom:
|
|
2845
|
+
"\u983B\u5EA6: daily / weekly:1,3,5 / biweekly:1,5@2026-06-22 / monthly:1,15 / nth-weekday:2:5,4:5 / custom:CRON"
|
|
2800
2846
|
).option("--description <text>", "\u8A73\u7D30\u8AAC\u660E").option("--project <id-prefix>", "\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8 id (prefix)").option("--context <id-prefix>", "\u30B3\u30F3\u30C6\u30AD\u30B9\u30C8 id (prefix)").option("--tag <id-prefix>", "\u30BF\u30B0 id (prefix)\u3002\u8907\u6570\u6307\u5B9A\u53EF", collectTag, []).option("--scheduled <HH:MM>", "\u30C7\u30D5\u30A9\u30EB\u30C8\u306E\u4E88\u5B9A\u958B\u59CB\u6642\u523B").option("--no-auto-expand", "Today \u81EA\u52D5\u5C55\u958B\u3092\u7121\u52B9\u5316 (\u30C7\u30D5\u30A9\u30EB\u30C8\u306F\u6709\u52B9)").option("--start-date <YYYY-MM-DD>", "\u6709\u52B9\u671F\u9593\u306E\u958B\u59CB\u65E5").option("--end-date <YYYY-MM-DD>", "\u6709\u52B9\u671F\u9593\u306E\u7D42\u4E86\u65E5").action(async (title, opts) => {
|
|
2801
2847
|
const ctx = await createAppContext();
|
|
2802
2848
|
const estimatedSeconds = parseEstimated(opts.estimated);
|
|
@@ -2804,10 +2850,10 @@ function routinesCreateCommand() {
|
|
|
2804
2850
|
if (opts.scheduled !== void 0 && !HHMM.test(opts.scheduled)) {
|
|
2805
2851
|
throw new CliError(`--scheduled must be HH:MM (got "${opts.scheduled}").`);
|
|
2806
2852
|
}
|
|
2807
|
-
if (opts.startDate !== void 0 && !
|
|
2853
|
+
if (opts.startDate !== void 0 && !DATE_RE4.test(opts.startDate)) {
|
|
2808
2854
|
throw new CliError(`--start-date must be YYYY-MM-DD (got "${opts.startDate}").`);
|
|
2809
2855
|
}
|
|
2810
|
-
if (opts.endDate !== void 0 && !
|
|
2856
|
+
if (opts.endDate !== void 0 && !DATE_RE4.test(opts.endDate)) {
|
|
2811
2857
|
throw new CliError(`--end-date must be YYYY-MM-DD (got "${opts.endDate}").`);
|
|
2812
2858
|
}
|
|
2813
2859
|
let projectId;
|
|
@@ -2864,10 +2910,17 @@ function formatFrequency(freq) {
|
|
|
2864
2910
|
switch (freq.type) {
|
|
2865
2911
|
case "daily":
|
|
2866
2912
|
return "daily";
|
|
2867
|
-
case "weekly":
|
|
2913
|
+
case "weekly": {
|
|
2914
|
+
const interval = freq.intervalWeeks ?? 1;
|
|
2915
|
+
if (interval >= 2 && freq.anchorDate) {
|
|
2916
|
+
return `biweekly:${freq.weekdays.join(",")}@${freq.anchorDate}`;
|
|
2917
|
+
}
|
|
2868
2918
|
return `weekly:${freq.weekdays.join(",")}`;
|
|
2919
|
+
}
|
|
2869
2920
|
case "monthly":
|
|
2870
2921
|
return `monthly:${freq.days.join(",")}`;
|
|
2922
|
+
case "monthly-nth-weekday":
|
|
2923
|
+
return `nth-weekday:${freq.occurrences.map((o) => `${o.nth}:${o.weekday}`).join(",")}`;
|
|
2871
2924
|
case "custom":
|
|
2872
2925
|
return `custom:${freq.cron}`;
|
|
2873
2926
|
}
|
|
@@ -2897,23 +2950,26 @@ function routinesListCommand() {
|
|
|
2897
2950
|
});
|
|
2898
2951
|
}
|
|
2899
2952
|
var HHMM2 = /^([01]\d|2[0-3]):[0-5]\d$/;
|
|
2900
|
-
var
|
|
2953
|
+
var DATE_RE5 = /^\d{4}-\d{2}-\d{2}$/;
|
|
2901
2954
|
var ROUTINE_STATUSES = ["active", "paused", "archived"];
|
|
2902
2955
|
function isRoutineStatus(value) {
|
|
2903
2956
|
return ROUTINE_STATUSES.includes(value);
|
|
2904
2957
|
}
|
|
2905
2958
|
function routinesUpdateCommand() {
|
|
2906
|
-
return new Command("update").description("\u30EB\u30FC\u30C1\u30F3\u3092\u90E8\u5206\u66F4\u65B0\u3059\u308B (\u672A\u6765 planned task \u306B\u81EA\u52D5\u4F1D\u64AD)").argument("<id-prefix>", "\u30EB\u30FC\u30C1\u30F3 id \u306E\u5148\u982D 4 \u6587\u5B57\u4EE5\u4E0A").option("--title <text>", "\u30BF\u30A4\u30C8\u30EB\u5909\u66F4").option("--description <text>", "\u8A73\u7D30\u8AAC\u660E").option("--clear-description", "\u8A73\u7D30\u8AAC\u660E\u3092\u7A7A\u306B\u3059\u308B").option("-e, --estimated <duration>", "\u898B\u7A4D\u3082\u308A\u6642\u9593 (30m / 1h / 1h30m)").option(
|
|
2959
|
+
return new Command("update").description("\u30EB\u30FC\u30C1\u30F3\u3092\u90E8\u5206\u66F4\u65B0\u3059\u308B (\u672A\u6765 planned task \u306B\u81EA\u52D5\u4F1D\u64AD)").argument("<id-prefix>", "\u30EB\u30FC\u30C1\u30F3 id \u306E\u5148\u982D 4 \u6587\u5B57\u4EE5\u4E0A").option("--title <text>", "\u30BF\u30A4\u30C8\u30EB\u5909\u66F4").option("--description <text>", "\u8A73\u7D30\u8AAC\u660E").option("--clear-description", "\u8A73\u7D30\u8AAC\u660E\u3092\u7A7A\u306B\u3059\u308B").option("-e, --estimated <duration>", "\u898B\u7A4D\u3082\u308A\u6642\u9593 (30m / 1h / 1h30m)").option(
|
|
2960
|
+
"--frequency <spec>",
|
|
2961
|
+
"\u983B\u5EA6: daily / weekly:1,3,5 / biweekly:1,5@2026-06-22 / monthly:1,15 / nth-weekday:2:5,4:5 / custom:CRON"
|
|
2962
|
+
).option("--project <id-prefix>", "\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8 id (prefix)").option("--clear-project", "\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u3092\u5916\u3059").option("--context <id-prefix>", "\u30B3\u30F3\u30C6\u30AD\u30B9\u30C8 id (prefix)").option("--clear-context", "\u30B3\u30F3\u30C6\u30AD\u30B9\u30C8\u3092\u5916\u3059").option("--tag <id-prefix>", "\u30BF\u30B0 id (prefix)\u3002\u8907\u6570\u6307\u5B9A\u53EF", collectTag2, []).option("--no-tags", "\u30BF\u30B0\u3092\u5168\u90E8\u5916\u3059").option("--scheduled <HH:MM>", "\u30C7\u30D5\u30A9\u30EB\u30C8\u4E88\u5B9A\u958B\u59CB\u6642\u523B").option("--clear-scheduled", "\u4E88\u5B9A\u958B\u59CB\u6642\u523B\u3092\u5916\u3059").option("--auto-expand", "Today \u81EA\u52D5\u5C55\u958B\u3092\u6709\u52B9\u5316").option("--no-auto-expand", "Today \u81EA\u52D5\u5C55\u958B\u3092\u7121\u52B9\u5316").option("--start-date <YYYY-MM-DD>", "\u6709\u52B9\u671F\u9593\u958B\u59CB\u65E5").option("--clear-start-date", "\u958B\u59CB\u65E5\u3092\u5916\u3059").option("--end-date <YYYY-MM-DD>", "\u6709\u52B9\u671F\u9593\u7D42\u4E86\u65E5").option("--clear-end-date", "\u7D42\u4E86\u65E5\u3092\u5916\u3059").option("--status <status>", "status (active / paused / archived)").action(async (prefix, opts) => {
|
|
2907
2963
|
const ctx = await createAppContext();
|
|
2908
2964
|
const all = await ctx.repos.routine.findAll();
|
|
2909
2965
|
const id = resolveResourceIdByPrefix(all, prefix, "routine");
|
|
2910
2966
|
if (opts.scheduled !== void 0 && !HHMM2.test(opts.scheduled)) {
|
|
2911
2967
|
throw new CliError(`--scheduled must be HH:MM (got "${opts.scheduled}").`);
|
|
2912
2968
|
}
|
|
2913
|
-
if (opts.startDate !== void 0 && !
|
|
2969
|
+
if (opts.startDate !== void 0 && !DATE_RE5.test(opts.startDate)) {
|
|
2914
2970
|
throw new CliError(`--start-date must be YYYY-MM-DD (got "${opts.startDate}").`);
|
|
2915
2971
|
}
|
|
2916
|
-
if (opts.endDate !== void 0 && !
|
|
2972
|
+
if (opts.endDate !== void 0 && !DATE_RE5.test(opts.endDate)) {
|
|
2917
2973
|
throw new CliError(`--end-date must be YYYY-MM-DD (got "${opts.endDate}").`);
|
|
2918
2974
|
}
|
|
2919
2975
|
if (opts.status !== void 0 && !isRoutineStatus(opts.status)) {
|
|
@@ -3212,9 +3268,15 @@ function tasksInterruptCommand() {
|
|
|
3212
3268
|
})
|
|
3213
3269
|
);
|
|
3214
3270
|
console.log(`\u26A1 Interrupted:`);
|
|
3215
|
-
console.log(
|
|
3216
|
-
|
|
3217
|
-
|
|
3271
|
+
console.log(
|
|
3272
|
+
` \u2713 finished: ${shortenId(result.finishedOriginal.id)} ${result.finishedOriginal.title}`
|
|
3273
|
+
);
|
|
3274
|
+
console.log(
|
|
3275
|
+
` \u25B6 interruption: ${shortenId(result.interruption.id)} ${result.interruption.title}`
|
|
3276
|
+
);
|
|
3277
|
+
console.log(
|
|
3278
|
+
` \u23F1 continuation: ${shortenId(result.continuation.id)} ${result.continuation.title}`
|
|
3279
|
+
);
|
|
3218
3280
|
});
|
|
3219
3281
|
}
|
|
3220
3282
|
function collectTag4(value, prev) {
|
|
@@ -3271,20 +3333,30 @@ function tasksParallelCommand() {
|
|
|
3271
3333
|
})
|
|
3272
3334
|
);
|
|
3273
3335
|
const icon = task.status === "running" ? "\u25B6" : "+";
|
|
3274
|
-
console.log(
|
|
3336
|
+
console.log(
|
|
3337
|
+
`${icon} Parallel task: ${shortenId(task.id)} ${task.title} (status=${task.status})`
|
|
3338
|
+
);
|
|
3275
3339
|
});
|
|
3276
3340
|
}
|
|
3277
3341
|
function collectTag5(value, prev) {
|
|
3278
3342
|
return [...prev, value];
|
|
3279
3343
|
}
|
|
3280
3344
|
function tasksPauseCommand() {
|
|
3281
|
-
return new Command("pause").description("\u8A08\u6E2C\u4E2D\
|
|
3345
|
+
return new Command("pause").description("\u8A08\u6E2C\u4E2D\u30BF\u30B9\u30AF\u3092\u4E2D\u65AD\u3057\u3066\u6B8B\u4F5C\u696D\u3092\u5225\u30BF\u30B9\u30AF\u306B\u5206\u5272\u3059\u308B").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)").action(async (prefix, opts) => {
|
|
3282
3346
|
const ctx = await createAppContext();
|
|
3283
3347
|
const date = opts.date ?? todayDateString();
|
|
3284
3348
|
const tasks = await ctx.repos.task.findByDate(date);
|
|
3285
3349
|
const taskId = resolveTaskIdByPrefix(tasks, prefix);
|
|
3286
|
-
const
|
|
3287
|
-
|
|
3350
|
+
const result = await wrapDomainErrors(
|
|
3351
|
+
() => pauseTaskWithAudit(ctx.repos.task, ctx.repos.taskNote, taskId)
|
|
3352
|
+
);
|
|
3353
|
+
console.log(`\u23F8 Paused (split):`);
|
|
3354
|
+
console.log(
|
|
3355
|
+
` \u2713 finished: ${shortenId(result.finishedOriginal.id)} ${result.finishedOriginal.title}`
|
|
3356
|
+
);
|
|
3357
|
+
console.log(
|
|
3358
|
+
` \u23F1 continuation: ${shortenId(result.continuation.id)} ${result.continuation.title}`
|
|
3359
|
+
);
|
|
3288
3360
|
});
|
|
3289
3361
|
}
|
|
3290
3362
|
function tasksRecentCommand() {
|
|
@@ -3338,7 +3410,9 @@ function tasksReorderCommand() {
|
|
|
3338
3410
|
if (opts.clearScheduled) patch.scheduledStartTime = void 0;
|
|
3339
3411
|
else if (opts.scheduled !== void 0) patch.scheduledStartTime = opts.scheduled;
|
|
3340
3412
|
const updated = await ctx.repos.task.update(taskId, patch);
|
|
3341
|
-
console.log(
|
|
3413
|
+
console.log(
|
|
3414
|
+
`\u2195 Reordered: ${shortenId(updated.id)} ${updated.title} (order_index=${updated.orderIndex})`
|
|
3415
|
+
);
|
|
3342
3416
|
});
|
|
3343
3417
|
}
|
|
3344
3418
|
var HHMMSS = /^([01]\d|2[0-3]):[0-5]\d(?::[0-5]\d)?$/;
|
|
@@ -3461,7 +3535,12 @@ async function buildPatch(ctx, opts) {
|
|
|
3461
3535
|
return patch;
|
|
3462
3536
|
}
|
|
3463
3537
|
function tasksUpdateCommand() {
|
|
3464
|
-
return new Command("update").description("\u30BF\u30B9\u30AF\u306E\u30D5\u30A3\u30FC\u30EB\u30C9\u3092\u90E8\u5206\u66F4\u65B0\u3059\u308B").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("--title <text>", "\u30BF\u30A4\u30C8\u30EB\u5909\u66F4").option("--description <text>", "\u8A73\u7D30\u8AAC\u660E").option("--clear-description", "\u8A73\u7D30\u8AAC\u660E\u3092\u7A7A\u306B\u3059\u308B").option("-e, --estimated <duration>", '\u898B\u7A4D\u3082\u308A\u6642\u9593 ("30m" / "1h" / "1h30m")').option("--project <id-prefix>", "\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8 id (prefix)").option("--clear-project", "\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u3092\u5916\u3059").option("--context <id-prefix>", "\u30B3\u30F3\u30C6\u30AD\u30B9\u30C8 id (prefix)").option("--clear-context", "\u30B3\u30F3\u30C6\u30AD\u30B9\u30C8\u3092\u5916\u3059").option(
|
|
3538
|
+
return new Command("update").description("\u30BF\u30B9\u30AF\u306E\u30D5\u30A3\u30FC\u30EB\u30C9\u3092\u90E8\u5206\u66F4\u65B0\u3059\u308B").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("--title <text>", "\u30BF\u30A4\u30C8\u30EB\u5909\u66F4").option("--description <text>", "\u8A73\u7D30\u8AAC\u660E").option("--clear-description", "\u8A73\u7D30\u8AAC\u660E\u3092\u7A7A\u306B\u3059\u308B").option("-e, --estimated <duration>", '\u898B\u7A4D\u3082\u308A\u6642\u9593 ("30m" / "1h" / "1h30m")').option("--project <id-prefix>", "\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8 id (prefix)").option("--clear-project", "\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u3092\u5916\u3059").option("--context <id-prefix>", "\u30B3\u30F3\u30C6\u30AD\u30B9\u30C8 id (prefix)").option("--clear-context", "\u30B3\u30F3\u30C6\u30AD\u30B9\u30C8\u3092\u5916\u3059").option(
|
|
3539
|
+
"--tag <id-prefix>",
|
|
3540
|
+
"\u30BF\u30B0 id (prefix)\u3002\u8907\u6570\u6307\u5B9A\u53EF\u3002\u6307\u5B9A\u3057\u305F\u30BB\u30C3\u30C8\u3067\u5168\u7F6E\u63DB",
|
|
3541
|
+
collectTag6,
|
|
3542
|
+
[]
|
|
3543
|
+
).option("--no-tags", "\u30BF\u30B0\u3092\u5168\u90E8\u5916\u3059").option("--scheduled <HH:MM>", "\u4E88\u5B9A\u958B\u59CB\u6642\u523B").option("--clear-scheduled", "\u4E88\u5B9A\u958B\u59CB\u6642\u523B\u3092\u5916\u3059").option("--notify", "\u4E88\u5B9A\u6642\u523B\u901A\u77E5\u3092 ON").option("--no-notify", "\u4E88\u5B9A\u6642\u523B\u901A\u77E5\u3092 OFF").option("--background", "\u30D0\u30C3\u30AF\u30B0\u30E9\u30A6\u30F3\u30C9\u30BF\u30B9\u30AF\u306B\u8A2D\u5B9A").option("--no-background", "\u30D0\u30C3\u30AF\u30B0\u30E9\u30A6\u30F3\u30C9\u30BF\u30B9\u30AF\u3092\u89E3\u9664").action(async (prefix, opts) => {
|
|
3465
3544
|
const ctx = await createAppContext();
|
|
3466
3545
|
const date = opts.date ?? todayDateString();
|
|
3467
3546
|
const tasks = await ctx.repos.task.findByDate(date);
|
|
@@ -3493,7 +3572,7 @@ function timeBlocksArchiveCommand() {
|
|
|
3493
3572
|
});
|
|
3494
3573
|
}
|
|
3495
3574
|
var HHMM4 = /^([01]\d|2[0-3]):[0-5]\d$/;
|
|
3496
|
-
var
|
|
3575
|
+
var DATE_RE6 = /^\d{4}-\d{2}-\d{2}$/;
|
|
3497
3576
|
function timeBlocksCreateCommand() {
|
|
3498
3577
|
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) => {
|
|
3499
3578
|
const ctx = await createAppContext();
|
|
@@ -3504,10 +3583,10 @@ function timeBlocksCreateCommand() {
|
|
|
3504
3583
|
throw new CliError(`--end-time must be HH:MM (got "${opts.endTime}").`);
|
|
3505
3584
|
}
|
|
3506
3585
|
const weekdays = parseWeekdaysList(opts.weekdays);
|
|
3507
|
-
if (opts.startDate !== void 0 && !
|
|
3586
|
+
if (opts.startDate !== void 0 && !DATE_RE6.test(opts.startDate)) {
|
|
3508
3587
|
throw new CliError(`--start-date must be YYYY-MM-DD (got "${opts.startDate}").`);
|
|
3509
3588
|
}
|
|
3510
|
-
if (opts.endDate !== void 0 && !
|
|
3589
|
+
if (opts.endDate !== void 0 && !DATE_RE6.test(opts.endDate)) {
|
|
3511
3590
|
throw new CliError(`--end-date must be YYYY-MM-DD (got "${opts.endDate}").`);
|
|
3512
3591
|
}
|
|
3513
3592
|
let projectId;
|
|
@@ -3596,7 +3675,7 @@ function timeBlocksListCommand() {
|
|
|
3596
3675
|
});
|
|
3597
3676
|
}
|
|
3598
3677
|
var HHMM5 = /^([01]\d|2[0-3]):[0-5]\d$/;
|
|
3599
|
-
var
|
|
3678
|
+
var DATE_RE7 = /^\d{4}-\d{2}-\d{2}$/;
|
|
3600
3679
|
var TIME_BLOCK_STATUSES = ["active", "archived"];
|
|
3601
3680
|
function isTimeBlockStatus(value) {
|
|
3602
3681
|
return TIME_BLOCK_STATUSES.includes(value);
|
|
@@ -3612,10 +3691,10 @@ function timeBlocksUpdateCommand() {
|
|
|
3612
3691
|
if (opts.endTime !== void 0 && !HHMM5.test(opts.endTime)) {
|
|
3613
3692
|
throw new CliError(`--end-time must be HH:MM (got "${opts.endTime}").`);
|
|
3614
3693
|
}
|
|
3615
|
-
if (opts.startDate !== void 0 && !
|
|
3694
|
+
if (opts.startDate !== void 0 && !DATE_RE7.test(opts.startDate)) {
|
|
3616
3695
|
throw new CliError(`--start-date must be YYYY-MM-DD (got "${opts.startDate}").`);
|
|
3617
3696
|
}
|
|
3618
|
-
if (opts.endDate !== void 0 && !
|
|
3697
|
+
if (opts.endDate !== void 0 && !DATE_RE7.test(opts.endDate)) {
|
|
3619
3698
|
throw new CliError(`--end-date must be YYYY-MM-DD (got "${opts.endDate}").`);
|
|
3620
3699
|
}
|
|
3621
3700
|
if (opts.status !== void 0 && !isTimeBlockStatus(opts.status)) {
|
|
@@ -3724,7 +3803,7 @@ function whoamiCommand() {
|
|
|
3724
3803
|
|
|
3725
3804
|
// src/index.ts
|
|
3726
3805
|
var program = new Command();
|
|
3727
|
-
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.
|
|
3806
|
+
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.6.0" );
|
|
3728
3807
|
program.addCommand(loginCommand());
|
|
3729
3808
|
program.addCommand(whoamiCommand());
|
|
3730
3809
|
program.addCommand(logoutCommand());
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fukuchang/taskchute-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "TaskChute CLI — E2EE 暗号化された TaskChute タスクをターミナルから操作するための CLI",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "fukuchang",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"e2ee"
|
|
22
22
|
],
|
|
23
23
|
"engines": {
|
|
24
|
-
"node": "
|
|
24
|
+
"node": "^22.23.4 || ^24.22.1 || ^26.12.1"
|
|
25
25
|
},
|
|
26
26
|
"type": "module",
|
|
27
27
|
"main": "./dist/index.js",
|
|
@@ -46,14 +46,14 @@
|
|
|
46
46
|
"hash-wasm": "^4.12.0"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
|
+
"@taskchute/crypto": "0.0.0",
|
|
50
|
+
"@taskchute/domain": "0.0.0",
|
|
51
|
+
"@taskchute/infra-supabase": "0.0.0",
|
|
52
|
+
"@taskchute/node-shared": "0.0.0",
|
|
49
53
|
"@types/node": "^24.12.3",
|
|
50
54
|
"tsup": "^8.5.1",
|
|
51
55
|
"tsx": "^4.20.0",
|
|
52
|
-
"typescript": "~6.0.2"
|
|
53
|
-
"@taskchute/domain": "0.0.0",
|
|
54
|
-
"@taskchute/infra-supabase": "0.0.0",
|
|
55
|
-
"@taskchute/crypto": "0.0.0",
|
|
56
|
-
"@taskchute/node-shared": "0.0.0"
|
|
56
|
+
"typescript": "~6.0.2"
|
|
57
57
|
},
|
|
58
58
|
"scripts": {
|
|
59
59
|
"start": "tsx src/index.ts",
|