@fukuchang/taskchute-mcp 0.3.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 +36 -29
- package/dist/index.js +177 -106
- package/package.json +7 -7
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.6.0 (2026-10-22) — Node.js セキュリティ修正版を要求
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
|
|
7
|
+
- 対応 Node.js を 22.23.4 以降の22系、24.22.1 以降の24系、26.12.1 以降の26系に変更。
|
|
8
|
+
- Node.js 20 のサポートを終了し、ビルド対象を Node.js 22 に変更。
|
|
9
|
+
- MCP パッケージの更新とは別に、起動に使う Node.js 本体を更新し、MCP サーバーを再起動する必要があります。
|
|
10
|
+
|
|
3
11
|
## 0.3.0 (2026-05-28) — 実績時刻の秒精度サポート
|
|
4
12
|
|
|
5
13
|
### Added
|
|
@@ -95,7 +103,7 @@ PR2 で露出した `repos.auditingTask` 経由で `routine-propagation` cause
|
|
|
95
103
|
(`daily` / `weekly` + weekdays / `monthly` + days / `custom` + cron)
|
|
96
104
|
- update_routine は `pickPropagationTargets` で未来 planned task を絞り込み、
|
|
97
105
|
`buildRoutineTaskPatch` で patch を構築、`auditingTask.updateWithCause(id,
|
|
98
|
-
|
|
106
|
+
patch, 'routine-propagation')` で伝播。`propagated_task_count` を返す
|
|
99
107
|
- 過去日 / running / completed / skipped / 別 routine_id は保護される
|
|
100
108
|
- `auditingTask` が undefined の時 (テスト等) は通常 update に fallback
|
|
101
109
|
- `create_time_block` / `update_time_block` / `archive_time_block` /
|
|
@@ -182,9 +190,9 @@ update_task のフル機能が初めて実用化される。
|
|
|
182
190
|
Web 側 `useAuditingTaskRepository` は既存の `instanceof` 判定で動き続ける
|
|
183
191
|
(互換)
|
|
184
192
|
- `test-helpers.ts` に `InMemoryRoutineRepository` / `InMemoryTimeBlockRepository`
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
193
|
+
- `makeRoutine` / `makeTimeBlock` を追加。`makeMockAppContext` に
|
|
194
|
+
`routineRepo` / `timeBlockRepo` opts と `refreshLookups: () => Promise.resolve()`
|
|
195
|
+
既定実装を追加
|
|
188
196
|
|
|
189
197
|
### PR1: Task CRUD パリティ
|
|
190
198
|
|
package/README.md
CHANGED
|
@@ -10,6 +10,15 @@ E2EE 暗号化された TaskChute タスクを Claude Desktop / Claude Code 等
|
|
|
10
10
|
> も有効。初回バージョン (0.1.2) は CLI と歩調を揃えるためのもので、以降は
|
|
11
11
|
> 独立 SemVer。
|
|
12
12
|
|
|
13
|
+
## 必要な Node.js
|
|
14
|
+
|
|
15
|
+
Node.js 22.23.4 以降の22系 / 24.22.1 以降の24系 / 26.12.1 以降の26系に対応します。
|
|
16
|
+
24系を推奨し、各系統の最新パッチ版を使用してください。
|
|
17
|
+
[2026年10月のセキュリティ修正](https://nodejs.org/en/blog/vulnerability/october-2026-security-releases)を含むバージョンが必要です。
|
|
18
|
+
|
|
19
|
+
MCP パッケージの更新だけでは Node.js 本体は更新されません。
|
|
20
|
+
MCP クライアントが起動に使う Node.js を更新し、MCP サーバーを再起動してください。
|
|
21
|
+
|
|
13
22
|
## インストール
|
|
14
23
|
|
|
15
24
|
```bash
|
|
@@ -21,32 +30,32 @@ npm i -g @fukuchang/taskchute-mcp
|
|
|
21
30
|
|
|
22
31
|
## 提供 tool
|
|
23
32
|
|
|
24
|
-
| tool
|
|
25
|
-
|
|
|
26
|
-
| `list_tasks`
|
|
27
|
-
| `add_task`
|
|
28
|
-
| `update_task`
|
|
29
|
-
| `delete_task`
|
|
30
|
-
| `start_task`
|
|
31
|
-
| `pause_task`
|
|
32
|
-
| `done_task`
|
|
33
|
-
| `skip_task`
|
|
34
|
-
| `reopen_task`
|
|
35
|
-
| `list_projects`
|
|
36
|
-
| `list_tags`
|
|
37
|
-
| `list_contexts`
|
|
38
|
-
| `list_routines`
|
|
39
|
-
| `list_time_blocks`
|
|
40
|
-
| `list_recent_tasks`
|
|
41
|
-
| `create_project` / `update_project` / `archive_project` / `delete_project`
|
|
42
|
-
| `create_tag` / `update_tag` / `archive_tag` / `delete_tag`
|
|
43
|
-
| `create_context` / `update_context` / `archive_context` / `delete_context`
|
|
44
|
-
| `create_routine` / `update_routine` / `archive_routine` / `delete_routine`
|
|
45
|
-
| `create_time_block` / `update_time_block` / `archive_time_block` / `delete_time_block` | タイムブロック CRUD。`weekdays` は 0=日曜~6=土曜の配列 (空不可)
|
|
46
|
-
| `set_actual_times`
|
|
47
|
-
| `interrupt_task`
|
|
48
|
-
| `add_parallel_task`
|
|
49
|
-
| `reorder_task`
|
|
33
|
+
| tool | 説明 |
|
|
34
|
+
| -------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
35
|
+
| `list_tasks` | 指定日 (省略時は今日) のタスク一覧を返す |
|
|
36
|
+
| `add_task` | タスクを追加する (title, date?, estimated_seconds?, description?, project_id?, context_id?, tag_ids?, scheduled_start_time?, notify_at_scheduled_start?, is_background?) |
|
|
37
|
+
| `update_task` | タスクのフィールドを部分更新する (タイトル / 詳細 / 見積もり / 予定時刻 / project / context / tags / 通知 / バックグラウンドフラグ)。`null` で明示クリア、`tag_ids` は全置換 |
|
|
38
|
+
| `delete_task` | タスクを削除する (Routine 由来タスクを今日以降に消すと `routine_expansions` に skip 記録、`hard_delete: true` で物理削除のみ) |
|
|
39
|
+
| `start_task` | タスクの計測を開始する (status → running) |
|
|
40
|
+
| `pause_task` | タスクを一時停止する (status → paused) |
|
|
41
|
+
| `done_task` | タスクを完了する (status → completed) |
|
|
42
|
+
| `skip_task` | タスクをスキップする (status → skipped)。reopen で planned に戻せる |
|
|
43
|
+
| `reopen_task` | completed / skipped を planned に戻す |
|
|
44
|
+
| `list_projects` | プロジェクト一覧 (status filter / limit 既定 100)。add/update_task の `project_id` を引くために使う |
|
|
45
|
+
| `list_tags` | タグ一覧。`tag_ids` を引くために使う |
|
|
46
|
+
| `list_contexts` | コンテキスト一覧。`context_id` を引くために使う |
|
|
47
|
+
| `list_routines` | ルーチン (繰り返しタスクテンプレート) 一覧。`frequency` は discriminated union (`{type: 'daily'}` / `{type: 'weekly', weekdays: [...]}` 等) のまま |
|
|
48
|
+
| `list_time_blocks` | タイムブロック (時間帯テンプレート) 一覧 |
|
|
49
|
+
| `list_recent_tasks` | 過去 N 日 (既定 90) のタスク。`query` で fuzzy match (ひらがな↔カタカナ正規化込み) 検索可能 |
|
|
50
|
+
| `create_project` / `update_project` / `archive_project` / `delete_project` | プロジェクト CRUD。`update` は partial / null クリア。`archive` はタスクの project_id を維持、`delete` は NULL 化 (ON DELETE SET NULL) |
|
|
51
|
+
| `create_tag` / `update_tag` / `archive_tag` / `delete_tag` | タグ CRUD。`delete` は tag_ids 配列に孤児 id を残すので注意 (UI 表示時に「不明なタグ」扱い) |
|
|
52
|
+
| `create_context` / `update_context` / `archive_context` / `delete_context` | コンテキスト CRUD。`delete` はタスクの context_id を NULL 化 |
|
|
53
|
+
| `create_routine` / `update_routine` / `archive_routine` / `delete_routine` | ルーチン CRUD。`update_routine` は **未来 planned task に自動伝播** (title / estimated / project / context / tags / scheduled_start_time)。手動編集も上書きされる点に注意。`propagated_task_count` を返す |
|
|
54
|
+
| `create_time_block` / `update_time_block` / `archive_time_block` / `delete_time_block` | タイムブロック CRUD。`weekdays` は 0=日曜~6=土曜の配列 (空不可) |
|
|
55
|
+
| `set_actual_times` | 実績開始/終了時刻を直接指定 (Play 押し忘れリカバリ用)。start+end → completed、start のみ → running、両方 null → planned リセット、end < start → 日跨ぎセッション |
|
|
56
|
+
| `interrupt_task` | 走行中タスクを「割り込み」で分割。1 呼び出しで 3 タスクを生成 (元タスク完了 / 割り込み running / 残作業 planned) + split note 3 件自動記録 |
|
|
57
|
+
| `add_parallel_task` | 進行中タスクと並行する新規タスクを追加。既定で running 状態 + 即計測開始 |
|
|
58
|
+
| `reorder_task` | 並び順 (order_index) と所属タイムブロックを直接指定 (慣習 10 単位) |
|
|
50
59
|
|
|
51
60
|
入力 `id` は full UUID。`list_tasks` の結果から取って各 tool に渡す想定。
|
|
52
61
|
`estimated_seconds` は秒数の整数 (CLI と違って文字列パースはしない)。
|
|
@@ -98,9 +107,7 @@ cp .mcp.json.example .mcp.json # repo root で
|
|
|
98
107
|
"mcpServers": {
|
|
99
108
|
"taskchute": {
|
|
100
109
|
"command": "node",
|
|
101
|
-
"args": [
|
|
102
|
-
"/path/to/TaskChute/packages/mcp/dist/index.js"
|
|
103
|
-
],
|
|
110
|
+
"args": ["/path/to/TaskChute/packages/mcp/dist/index.js"],
|
|
104
111
|
"env": {
|
|
105
112
|
"TASKCHUTE_SUPABASE_URL": "https://YOUR_PROJECT.supabase.co",
|
|
106
113
|
"TASKCHUTE_SUPABASE_PUBLISHABLE_KEY": "sb_publishable_..."
|
package/dist/index.js
CHANGED
|
@@ -23,9 +23,7 @@ function readSupabaseConfigFromEnv() {
|
|
|
23
23
|
const url = process.env.TASKCHUTE_SUPABASE_URL;
|
|
24
24
|
const publishableKey = process.env.TASKCHUTE_SUPABASE_PUBLISHABLE_KEY;
|
|
25
25
|
if (!url || !publishableKey) {
|
|
26
|
-
throw new Error(
|
|
27
|
-
`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
|
+
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`);
|
|
29
27
|
}
|
|
30
28
|
return { url, publishableKey };
|
|
31
29
|
}
|
|
@@ -204,13 +202,10 @@ async function deriveKeyMaterial(password, salt, params = DEFAULT_ARGON2ID_PARAM
|
|
|
204
202
|
return copyToArrayBuffer(hash);
|
|
205
203
|
}
|
|
206
204
|
async function importKEK(material) {
|
|
207
|
-
return crypto.subtle.importKey(
|
|
208
|
-
"
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
false,
|
|
212
|
-
["wrapKey", "unwrapKey"]
|
|
213
|
-
);
|
|
205
|
+
return crypto.subtle.importKey("raw", material, { name: "AES-GCM" }, false, [
|
|
206
|
+
"wrapKey",
|
|
207
|
+
"unwrapKey"
|
|
208
|
+
]);
|
|
214
209
|
}
|
|
215
210
|
async function deriveKEK(password, salt, params = DEFAULT_ARGON2ID_PARAMS) {
|
|
216
211
|
const material = await deriveKeyMaterial(password, salt, params);
|
|
@@ -218,14 +213,14 @@ async function deriveKEK(password, salt, params = DEFAULT_ARGON2ID_PARAMS) {
|
|
|
218
213
|
}
|
|
219
214
|
|
|
220
215
|
// ../crypto/src/wrap.ts
|
|
221
|
-
async function unwrapMasterKey(wrapped, iv, kek) {
|
|
216
|
+
async function unwrapMasterKey(wrapped, iv, kek, options = {}) {
|
|
222
217
|
return crypto.subtle.unwrapKey(
|
|
223
218
|
"raw",
|
|
224
219
|
wrapped,
|
|
225
220
|
kek,
|
|
226
221
|
{ name: "AES-GCM", iv },
|
|
227
222
|
{ name: "AES-GCM", length: 256 },
|
|
228
|
-
false,
|
|
223
|
+
options.extractable ?? false,
|
|
229
224
|
["encrypt", "decrypt"]
|
|
230
225
|
);
|
|
231
226
|
}
|
|
@@ -414,11 +409,7 @@ async function unlockMasterKey(snapshot, method) {
|
|
|
414
409
|
snapshot.passphrase.kdfParams
|
|
415
410
|
);
|
|
416
411
|
try {
|
|
417
|
-
return await unwrapMasterKey(
|
|
418
|
-
snapshot.passphrase.wrapped,
|
|
419
|
-
snapshot.passphrase.iv,
|
|
420
|
-
kek
|
|
421
|
-
);
|
|
412
|
+
return await unwrapMasterKey(snapshot.passphrase.wrapped, snapshot.passphrase.iv, kek);
|
|
422
413
|
} catch {
|
|
423
414
|
throw new UnlockError("Wrong passphrase", "wrong-key");
|
|
424
415
|
}
|
|
@@ -434,11 +425,7 @@ async function unlockMasterKey(snapshot, method) {
|
|
|
434
425
|
);
|
|
435
426
|
const kek = await importKEK(material);
|
|
436
427
|
try {
|
|
437
|
-
return await unwrapMasterKey(
|
|
438
|
-
snapshot.recovery.wrapped,
|
|
439
|
-
snapshot.recovery.iv,
|
|
440
|
-
kek
|
|
441
|
-
);
|
|
428
|
+
return await unwrapMasterKey(snapshot.recovery.wrapped, snapshot.recovery.iv, kek);
|
|
442
429
|
} catch {
|
|
443
430
|
throw new UnlockError("Wrong recovery key", "wrong-key");
|
|
444
431
|
}
|
|
@@ -492,13 +479,10 @@ function dateToIsoOrNull(date) {
|
|
|
492
479
|
// ../infra-supabase/src/supabase-context-repository.ts
|
|
493
480
|
async function rowToContext(row, cryptoSession) {
|
|
494
481
|
const [name, description] = await Promise.all([
|
|
495
|
-
decryptRequiredTextFromWire(
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
row.key_id,
|
|
500
|
-
{ entityId: row.id, field: "name" }
|
|
501
|
-
),
|
|
482
|
+
decryptRequiredTextFromWire(cryptoSession, row.name_ciphertext, row.name_iv, row.key_id, {
|
|
483
|
+
entityId: row.id,
|
|
484
|
+
field: "name"
|
|
485
|
+
}),
|
|
502
486
|
decryptOptionalTextFromWire(
|
|
503
487
|
cryptoSession,
|
|
504
488
|
row.description_ciphertext,
|
|
@@ -511,6 +495,7 @@ async function rowToContext(row, cryptoSession) {
|
|
|
511
495
|
id: row.id,
|
|
512
496
|
name,
|
|
513
497
|
status: row.status,
|
|
498
|
+
notifyAtScheduledStart: row.notify_at_scheduled_start,
|
|
514
499
|
createdAt: new Date(row.created_at),
|
|
515
500
|
updatedAt: new Date(row.updated_at)
|
|
516
501
|
};
|
|
@@ -524,6 +509,9 @@ function nonEncryptedFieldsToRow(input) {
|
|
|
524
509
|
if ("icon" in input) row.icon = input.icon ?? null;
|
|
525
510
|
if ("color" in input) row.color = input.color ?? null;
|
|
526
511
|
if ("status" in input && input.status !== void 0) row.status = input.status;
|
|
512
|
+
if ("notifyAtScheduledStart" in input && input.notifyAtScheduledStart !== void 0) {
|
|
513
|
+
row.notify_at_scheduled_start = input.notifyAtScheduledStart;
|
|
514
|
+
}
|
|
527
515
|
return row;
|
|
528
516
|
}
|
|
529
517
|
async function encryptContextFieldsInto(row, entityId, patch, cryptoSession) {
|
|
@@ -611,13 +599,10 @@ var SupabaseContextRepository = class {
|
|
|
611
599
|
// ../infra-supabase/src/supabase-project-repository.ts
|
|
612
600
|
async function rowToProject(row, cryptoSession) {
|
|
613
601
|
const [name, description] = await Promise.all([
|
|
614
|
-
decryptRequiredTextFromWire(
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
row.key_id,
|
|
619
|
-
{ entityId: row.id, field: "name" }
|
|
620
|
-
),
|
|
602
|
+
decryptRequiredTextFromWire(cryptoSession, row.name_ciphertext, row.name_iv, row.key_id, {
|
|
603
|
+
entityId: row.id,
|
|
604
|
+
field: "name"
|
|
605
|
+
}),
|
|
621
606
|
decryptOptionalTextFromWire(
|
|
622
607
|
cryptoSession,
|
|
623
608
|
row.description_ciphertext,
|
|
@@ -771,13 +756,10 @@ var SupabaseRoutineExpansionRepository = class {
|
|
|
771
756
|
// ../infra-supabase/src/supabase-routine-repository.ts
|
|
772
757
|
async function rowToRoutine(row, cryptoSession) {
|
|
773
758
|
const [title, description] = await Promise.all([
|
|
774
|
-
decryptRequiredTextFromWire(
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
row.key_id,
|
|
779
|
-
{ entityId: row.id, field: "title" }
|
|
780
|
-
),
|
|
759
|
+
decryptRequiredTextFromWire(cryptoSession, row.title_ciphertext, row.title_iv, row.key_id, {
|
|
760
|
+
entityId: row.id,
|
|
761
|
+
field: "title"
|
|
762
|
+
}),
|
|
781
763
|
decryptOptionalTextFromWire(
|
|
782
764
|
cryptoSession,
|
|
783
765
|
row.description_ciphertext,
|
|
@@ -918,13 +900,10 @@ var SupabaseRoutineRepository = class {
|
|
|
918
900
|
// ../infra-supabase/src/supabase-tag-repository.ts
|
|
919
901
|
async function rowToTag(row, cryptoSession) {
|
|
920
902
|
const [name, description] = await Promise.all([
|
|
921
|
-
decryptRequiredTextFromWire(
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
row.key_id,
|
|
926
|
-
{ entityId: row.id, field: "name" }
|
|
927
|
-
),
|
|
903
|
+
decryptRequiredTextFromWire(cryptoSession, row.name_ciphertext, row.name_iv, row.key_id, {
|
|
904
|
+
entityId: row.id,
|
|
905
|
+
field: "name"
|
|
906
|
+
}),
|
|
928
907
|
decryptOptionalTextFromWire(
|
|
929
908
|
cryptoSession,
|
|
930
909
|
row.description_ciphertext,
|
|
@@ -1035,13 +1014,10 @@ var SupabaseTagRepository = class {
|
|
|
1035
1014
|
// ../infra-supabase/src/supabase-task-note-repository.ts
|
|
1036
1015
|
async function rowToTaskNote(row, cryptoSession) {
|
|
1037
1016
|
const [body, metaJson, historyJson] = await Promise.all([
|
|
1038
|
-
decryptRequiredTextFromWire(
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
row.key_id,
|
|
1043
|
-
{ entityId: row.id, field: "body" }
|
|
1044
|
-
),
|
|
1017
|
+
decryptRequiredTextFromWire(cryptoSession, row.body_ciphertext, row.body_iv, row.key_id, {
|
|
1018
|
+
entityId: row.id,
|
|
1019
|
+
field: "body"
|
|
1020
|
+
}),
|
|
1045
1021
|
decryptOptionalTextFromWire(
|
|
1046
1022
|
cryptoSession,
|
|
1047
1023
|
row.metadata_ciphertext,
|
|
@@ -1132,9 +1108,7 @@ async function encryptTaskNoteFieldsInto(row, entityId, patch, cryptoSession) {
|
|
|
1132
1108
|
if (touched) {
|
|
1133
1109
|
const keyId = cryptoSession.getCurrentKeyId();
|
|
1134
1110
|
if (keyId === null) {
|
|
1135
|
-
throw new Error(
|
|
1136
|
-
"SupabaseTaskNoteRepository: session became locked mid-write"
|
|
1137
|
-
);
|
|
1111
|
+
throw new Error("SupabaseTaskNoteRepository: session became locked mid-write");
|
|
1138
1112
|
}
|
|
1139
1113
|
row.key_id = keyId;
|
|
1140
1114
|
}
|
|
@@ -1161,9 +1135,7 @@ var SupabaseTaskNoteRepository = class {
|
|
|
1161
1135
|
if (taskIds.length === 0) return result;
|
|
1162
1136
|
const { data, error } = await this.supabase.from("task_notes").select("*").in("task_id", taskIds).order("created_at", { ascending: true });
|
|
1163
1137
|
if (error) throw error;
|
|
1164
|
-
const decoded = await Promise.all(
|
|
1165
|
-
data.map((row) => rowToTaskNote(row, this.cryptoSession))
|
|
1166
|
-
);
|
|
1138
|
+
const decoded = await Promise.all(data.map((row) => rowToTaskNote(row, this.cryptoSession)));
|
|
1167
1139
|
for (const note of decoded) {
|
|
1168
1140
|
const bucket = result.get(note.taskId);
|
|
1169
1141
|
if (bucket) bucket.push(note);
|
|
@@ -1212,13 +1184,10 @@ var SupabaseTaskNoteRepository = class {
|
|
|
1212
1184
|
// ../infra-supabase/src/supabase-task-repository.ts
|
|
1213
1185
|
async function rowToTask(row, cryptoSession) {
|
|
1214
1186
|
const [title, description] = await Promise.all([
|
|
1215
|
-
decryptRequiredTextFromWire(
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
row.key_id,
|
|
1220
|
-
{ entityId: row.id, field: "title" }
|
|
1221
|
-
),
|
|
1187
|
+
decryptRequiredTextFromWire(cryptoSession, row.title_ciphertext, row.title_iv, row.key_id, {
|
|
1188
|
+
entityId: row.id,
|
|
1189
|
+
field: "title"
|
|
1190
|
+
}),
|
|
1222
1191
|
decryptOptionalTextFromWire(
|
|
1223
1192
|
cryptoSession,
|
|
1224
1193
|
row.description_ciphertext,
|
|
@@ -1662,20 +1631,41 @@ async function startTask(repo, taskId) {
|
|
|
1662
1631
|
return repo.update(taskId, patch);
|
|
1663
1632
|
}
|
|
1664
1633
|
async function pauseTask(repo, taskId) {
|
|
1665
|
-
const
|
|
1666
|
-
if (!
|
|
1667
|
-
if (!
|
|
1634
|
+
const target = await repo.findById(taskId);
|
|
1635
|
+
if (!target) throw new Error(`Task not found: ${taskId}`);
|
|
1636
|
+
if (!target.currentSession) {
|
|
1668
1637
|
throw new Error(`Task is not currently running: ${taskId}`);
|
|
1669
1638
|
}
|
|
1670
|
-
const
|
|
1671
|
-
|
|
1672
|
-
|
|
1639
|
+
const now = /* @__PURE__ */ new Date();
|
|
1640
|
+
const closedSession = {
|
|
1641
|
+
startedAt: target.currentSession.startedAt,
|
|
1642
|
+
finishedAt: now
|
|
1673
1643
|
};
|
|
1674
|
-
|
|
1675
|
-
status: "
|
|
1676
|
-
sessions: [...
|
|
1644
|
+
const finishedOriginal = await repo.update(target.id, {
|
|
1645
|
+
status: "completed",
|
|
1646
|
+
sessions: [...target.sessions, closedSession],
|
|
1677
1647
|
currentSession: void 0
|
|
1678
1648
|
});
|
|
1649
|
+
const elapsed = calculateActualSeconds(finishedOriginal);
|
|
1650
|
+
const continuation = await repo.create({
|
|
1651
|
+
date: target.date,
|
|
1652
|
+
title: target.title,
|
|
1653
|
+
description: target.description,
|
|
1654
|
+
estimatedSeconds: Math.max(0, Math.floor(target.estimatedSeconds - elapsed)),
|
|
1655
|
+
sessions: [],
|
|
1656
|
+
status: "planned",
|
|
1657
|
+
orderIndex: target.orderIndex + 1,
|
|
1658
|
+
splitFromTaskId: target.id,
|
|
1659
|
+
projectId: target.projectId,
|
|
1660
|
+
contextId: target.contextId,
|
|
1661
|
+
// continuation は「中断したタスクの残り」。中断時刻を予定時刻にすることで
|
|
1662
|
+
// ソート上は元タスクの直後 (effectiveTime 同値 + orderIndex 順) に並び、
|
|
1663
|
+
// TimeBlock 境界をまたいだ場合も自然に新しいブロックへ追随する。
|
|
1664
|
+
scheduledStartTime: toTimeString(now),
|
|
1665
|
+
tagIds: target.tagIds,
|
|
1666
|
+
isBackground: target.isBackground
|
|
1667
|
+
});
|
|
1668
|
+
return { finishedOriginal, continuation };
|
|
1679
1669
|
}
|
|
1680
1670
|
async function skipTask(repo, taskId) {
|
|
1681
1671
|
const task = await repo.findById(taskId);
|
|
@@ -1830,19 +1820,13 @@ function buildSetActualTimesPatch(task, input) {
|
|
|
1830
1820
|
// ../domain/src/task-reorder.ts
|
|
1831
1821
|
function computeNextOrderIndex(existingTasks) {
|
|
1832
1822
|
if (existingTasks.length === 0) return 10;
|
|
1833
|
-
const max = existingTasks.reduce(
|
|
1834
|
-
(acc, t) => t.orderIndex > acc ? t.orderIndex : acc,
|
|
1835
|
-
0
|
|
1836
|
-
);
|
|
1823
|
+
const max = existingTasks.reduce((acc, t) => t.orderIndex > acc ? t.orderIndex : acc, 0);
|
|
1837
1824
|
return max + 10;
|
|
1838
1825
|
}
|
|
1839
1826
|
|
|
1840
1827
|
// ../domain/src/task-suggest.ts
|
|
1841
1828
|
function normalizeForSearch(str) {
|
|
1842
|
-
return str.trim().toLowerCase().replace(
|
|
1843
|
-
/[ぁ-ゖ]/g,
|
|
1844
|
-
(ch) => String.fromCharCode(ch.charCodeAt(0) + 96)
|
|
1845
|
-
);
|
|
1829
|
+
return str.trim().toLowerCase().replace(/[ぁ-ゖ]/g, (ch) => String.fromCharCode(ch.charCodeAt(0) + 96));
|
|
1846
1830
|
}
|
|
1847
1831
|
function fuzzyScore(query, target) {
|
|
1848
1832
|
const q = query;
|
|
@@ -1905,7 +1889,6 @@ function findSimilarTasks(query, tasks, limit = 5) {
|
|
|
1905
1889
|
function describeStatusTransition(from, to) {
|
|
1906
1890
|
if (from === "planned" && to === "running") return "\u958B\u59CB";
|
|
1907
1891
|
if (from === "paused" && to === "running") return "\u518D\u958B";
|
|
1908
|
-
if (from === "running" && to === "paused") return "\u4E00\u6642\u505C\u6B62";
|
|
1909
1892
|
if (to === "completed") return "\u5B8C\u4E86";
|
|
1910
1893
|
if (to === "skipped") return "\u30B9\u30AD\u30C3\u30D7";
|
|
1911
1894
|
if (to === "planned") return "\u672A\u7740\u624B\u306B\u623B\u3059";
|
|
@@ -2155,6 +2138,42 @@ async function interruptTaskWithAudit(taskRepo, taskNoteRepo, params) {
|
|
|
2155
2138
|
}
|
|
2156
2139
|
return result;
|
|
2157
2140
|
}
|
|
2141
|
+
async function pauseTaskWithAudit(taskRepo, taskNoteRepo, taskId) {
|
|
2142
|
+
const result = await pauseTask(taskRepo, taskId);
|
|
2143
|
+
const notes = buildPauseSplitNoteInputs(result.finishedOriginal, result.continuation);
|
|
2144
|
+
try {
|
|
2145
|
+
await Promise.all(notes.map((input) => taskNoteRepo.create(input)));
|
|
2146
|
+
} catch (err) {
|
|
2147
|
+
console.error("[audit] failed to record pause split notes", {
|
|
2148
|
+
sourceTaskId: result.finishedOriginal.id,
|
|
2149
|
+
err
|
|
2150
|
+
});
|
|
2151
|
+
}
|
|
2152
|
+
return result;
|
|
2153
|
+
}
|
|
2154
|
+
function buildPauseSplitNoteInputs(source, continuation) {
|
|
2155
|
+
const sourceTitle = titleOrPlaceholder(source);
|
|
2156
|
+
return [
|
|
2157
|
+
{
|
|
2158
|
+
taskId: source.id,
|
|
2159
|
+
type: "split",
|
|
2160
|
+
body: `\u4E2D\u65AD\u3057\u3066\u6B8B\u4F5C\u696D\u3092\u5206\u5272\u3002\u7D9A\u304D\u306F\u5225\u30BF\u30B9\u30AF\u3067\u7D99\u7D9A\u3002`,
|
|
2161
|
+
metadata: {
|
|
2162
|
+
event: "pause-source",
|
|
2163
|
+
continuationTaskId: continuation.id
|
|
2164
|
+
}
|
|
2165
|
+
},
|
|
2166
|
+
{
|
|
2167
|
+
taskId: continuation.id,
|
|
2168
|
+
type: "split",
|
|
2169
|
+
body: `\u300C${sourceTitle}\u300D\u306E\u6B8B\u4F5C\u696D\u3068\u3057\u3066\u7D99\u7D9A\u3002`,
|
|
2170
|
+
metadata: {
|
|
2171
|
+
event: "pause-continuation",
|
|
2172
|
+
sourceTaskId: source.id
|
|
2173
|
+
}
|
|
2174
|
+
}
|
|
2175
|
+
];
|
|
2176
|
+
}
|
|
2158
2177
|
function buildSplitNoteInputs(source, interruption, continuation) {
|
|
2159
2178
|
const sourceTitle = titleOrPlaceholder(source);
|
|
2160
2179
|
const interruptionTitle = titleOrPlaceholder(interruption);
|
|
@@ -2191,8 +2210,6 @@ function buildSplitNoteInputs(source, interruption, continuation) {
|
|
|
2191
2210
|
}
|
|
2192
2211
|
];
|
|
2193
2212
|
}
|
|
2194
|
-
|
|
2195
|
-
// ../domain/src/routine-expansion.ts
|
|
2196
2213
|
function buildRoutineTaskPatch(routine) {
|
|
2197
2214
|
return {
|
|
2198
2215
|
title: routine.title,
|
|
@@ -2210,6 +2227,14 @@ function pickPropagationTargets(routine, tasks, today) {
|
|
|
2210
2227
|
);
|
|
2211
2228
|
}
|
|
2212
2229
|
|
|
2230
|
+
// ../domain/src/context-task-inheritance.ts
|
|
2231
|
+
function applyContextNotificationInheritance(input, context) {
|
|
2232
|
+
if (context?.notifyAtScheduledStart !== true) return input;
|
|
2233
|
+
const current = input.notifyAtScheduledStart;
|
|
2234
|
+
if (current !== void 0) return input;
|
|
2235
|
+
return { ...input, notifyAtScheduledStart: true };
|
|
2236
|
+
}
|
|
2237
|
+
|
|
2213
2238
|
// ../infra-supabase/src/audit-task-repository.ts
|
|
2214
2239
|
var AuditingTaskRepository = class {
|
|
2215
2240
|
// `erasableSyntaxOnly` 配下では parameter property が使えないので、明示的に
|
|
@@ -2499,13 +2524,17 @@ async function addTaskHandler(ctx, input) {
|
|
|
2499
2524
|
const project = await ctx.repos.project.findById(input.project_id);
|
|
2500
2525
|
if (!project) throw new McpToolError(`Project not found: ${input.project_id}`);
|
|
2501
2526
|
}
|
|
2527
|
+
let context = null;
|
|
2502
2528
|
if (input.context_id) {
|
|
2503
|
-
|
|
2529
|
+
context = await ctx.repos.context.findById(input.context_id);
|
|
2504
2530
|
if (!context) throw new McpToolError(`Context not found: ${input.context_id}`);
|
|
2505
2531
|
}
|
|
2506
2532
|
if (input.tag_ids && input.tag_ids.length > 0) {
|
|
2507
2533
|
const checked = await Promise.all(
|
|
2508
|
-
input.tag_ids.map(async (id) => ({
|
|
2534
|
+
input.tag_ids.map(async (id) => ({
|
|
2535
|
+
id,
|
|
2536
|
+
exists: await ctx.repos.tag.findById(id) !== null
|
|
2537
|
+
}))
|
|
2509
2538
|
);
|
|
2510
2539
|
const missing = checked.filter((c) => !c.exists).map((c) => c.id);
|
|
2511
2540
|
if (missing.length > 0) {
|
|
@@ -2516,7 +2545,7 @@ async function addTaskHandler(ctx, input) {
|
|
|
2516
2545
|
const estimatedSeconds = input.estimated_seconds ?? DEFAULT_ESTIMATED_SECONDS;
|
|
2517
2546
|
const existing = await ctx.repos.task.findByDate(date);
|
|
2518
2547
|
const orderIndex = computeNextOrderIndex(existing);
|
|
2519
|
-
const
|
|
2548
|
+
const createInput = {
|
|
2520
2549
|
date,
|
|
2521
2550
|
title: input.title,
|
|
2522
2551
|
description: input.description,
|
|
@@ -2530,7 +2559,10 @@ async function addTaskHandler(ctx, input) {
|
|
|
2530
2559
|
scheduledStartTime: input.scheduled_start_time,
|
|
2531
2560
|
notifyAtScheduledStart: input.notify_at_scheduled_start,
|
|
2532
2561
|
isBackground: input.is_background
|
|
2533
|
-
}
|
|
2562
|
+
};
|
|
2563
|
+
const task = await ctx.repos.task.create(
|
|
2564
|
+
applyContextNotificationInheritance(createInput, context)
|
|
2565
|
+
);
|
|
2534
2566
|
return { task: taskSummaryFrom(task) };
|
|
2535
2567
|
}
|
|
2536
2568
|
|
|
@@ -2540,6 +2572,7 @@ function contextSummaryFrom(context) {
|
|
|
2540
2572
|
id: context.id,
|
|
2541
2573
|
name: context.name,
|
|
2542
2574
|
status: context.status,
|
|
2575
|
+
notify_at_scheduled_start: context.notifyAtScheduledStart ?? false,
|
|
2543
2576
|
created_at: context.createdAt.toISOString(),
|
|
2544
2577
|
updated_at: context.updatedAt.toISOString()
|
|
2545
2578
|
};
|
|
@@ -2701,7 +2734,8 @@ var createContextInputSchema = z.object({
|
|
|
2701
2734
|
name: z.string().min(1, "name must not be empty"),
|
|
2702
2735
|
description: z.string().optional(),
|
|
2703
2736
|
icon: z.string().optional().describe("\u7D75\u6587\u5B57\u307E\u305F\u306F\u30A2\u30A4\u30B3\u30F3\u540D (\u4F8B: \u{1F3E0} / mdi-home)"),
|
|
2704
|
-
color: z.string().optional().describe("UI \u8272 (\u4F8B: #ff6b6b)")
|
|
2737
|
+
color: z.string().optional().describe("UI \u8272 (\u4F8B: #ff6b6b)"),
|
|
2738
|
+
notify_at_scheduled_start: z.boolean().optional().describe("true \u306A\u3089\u3001\u3053\u306E\u30B3\u30F3\u30C6\u30AD\u30B9\u30C8\u3092\u4ED8\u3051\u305F\u65B0\u898F/\u66F4\u65B0\u30BF\u30B9\u30AF\u306E\u901A\u77E5\u3092 ON \u306B\u81EA\u52D5\u7D99\u627F\u3059\u308B")
|
|
2705
2739
|
});
|
|
2706
2740
|
async function createContextHandler(ctx, input) {
|
|
2707
2741
|
const createInput = {
|
|
@@ -2711,6 +2745,9 @@ async function createContextHandler(ctx, input) {
|
|
|
2711
2745
|
if (input.description !== void 0) createInput.description = input.description;
|
|
2712
2746
|
if (input.icon !== void 0) createInput.icon = input.icon;
|
|
2713
2747
|
if (input.color !== void 0) createInput.color = input.color;
|
|
2748
|
+
if (input.notify_at_scheduled_start !== void 0) {
|
|
2749
|
+
createInput.notifyAtScheduledStart = input.notify_at_scheduled_start;
|
|
2750
|
+
}
|
|
2714
2751
|
const context = await ctx.repos.context.create(createInput);
|
|
2715
2752
|
return { context: contextSummaryFrom(context) };
|
|
2716
2753
|
}
|
|
@@ -2749,12 +2786,23 @@ var frequencySchema = z.discriminatedUnion("type", [
|
|
|
2749
2786
|
z.object({ type: z.literal("daily") }),
|
|
2750
2787
|
z.object({
|
|
2751
2788
|
type: z.literal("weekly"),
|
|
2752
|
-
weekdays: z.array(z.number().int().min(0).max(6)).min(1, "at least 1 weekday")
|
|
2789
|
+
weekdays: z.array(z.number().int().min(0).max(6)).min(1, "at least 1 weekday"),
|
|
2790
|
+
intervalWeeks: z.number().int().min(1).max(4).optional().describe("1=\u6BCE\u9031 (default) / 2=\u9694\u9031 / 3+ \u3082\u540C\u69D8\u3002anchorDate \u3068\u7D44\u3067\u4F7F\u3046"),
|
|
2791
|
+
anchorDate: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional().describe("intervalWeeks>=2 \u306E\u57FA\u6E96\u9031\u3092\u542B\u3080\u65E5\u4ED8\u3002\u4F8B: 2026-06-22")
|
|
2753
2792
|
}),
|
|
2754
2793
|
z.object({
|
|
2755
2794
|
type: z.literal("monthly"),
|
|
2756
2795
|
days: z.array(z.number().int().min(1).max(31)).min(1, "at least 1 day")
|
|
2757
2796
|
}),
|
|
2797
|
+
z.object({
|
|
2798
|
+
type: z.literal("monthly-nth-weekday"),
|
|
2799
|
+
occurrences: z.array(
|
|
2800
|
+
z.object({
|
|
2801
|
+
nth: z.number().int().refine((n) => n >= 1 && n <= 5 || n === -1, "nth must be 1-5 or -1 (last)"),
|
|
2802
|
+
weekday: z.number().int().min(0).max(6)
|
|
2803
|
+
})
|
|
2804
|
+
).min(1, "at least 1 occurrence").describe("\u4F8B: [{nth:2,weekday:5},{nth:4,weekday:5}] \u3067\u7B2C2\u30FB\u7B2C4\u91D1\u66DC\u3002nth=-1 \u306F\u6700\u7D42\u9031")
|
|
2805
|
+
}),
|
|
2758
2806
|
z.object({ type: z.literal("custom"), cron: z.string().min(1) })
|
|
2759
2807
|
]);
|
|
2760
2808
|
var createRoutineInputSchema = z.object({
|
|
@@ -3105,12 +3153,13 @@ var pauseTaskInputSchema = z.object({
|
|
|
3105
3153
|
id: z.string().uuid({ message: "id must be a UUID" })
|
|
3106
3154
|
});
|
|
3107
3155
|
async function pauseTaskHandler(ctx, input) {
|
|
3108
|
-
const
|
|
3109
|
-
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3156
|
+
const result = await wrapDomainErrors(
|
|
3157
|
+
() => pauseTaskWithAudit(ctx.repos.task, ctx.repos.taskNote, input.id)
|
|
3158
|
+
);
|
|
3159
|
+
return {
|
|
3160
|
+
finished_original: taskSummaryFrom(result.finishedOriginal),
|
|
3161
|
+
continuation: taskSummaryFrom(result.continuation)
|
|
3162
|
+
};
|
|
3114
3163
|
}
|
|
3115
3164
|
var reopenTaskInputSchema = z.object({
|
|
3116
3165
|
id: z.string().uuid({ message: "id must be a UUID" })
|
|
@@ -3192,7 +3241,8 @@ var updateContextInputSchema = z.object({
|
|
|
3192
3241
|
description: z.union([z.string(), z.null()]).optional(),
|
|
3193
3242
|
icon: z.union([z.string(), z.null()]).optional(),
|
|
3194
3243
|
color: z.union([z.string(), z.null()]).optional(),
|
|
3195
|
-
status: z.enum(["active", "archived"]).optional().describe("archived \u5316\u306F archive_context tool \u63A8\u5968")
|
|
3244
|
+
status: z.enum(["active", "archived"]).optional().describe("archived \u5316\u306F archive_context tool \u63A8\u5968"),
|
|
3245
|
+
notify_at_scheduled_start: z.boolean().optional().describe("true \u306A\u3089\u3001\u3053\u306E\u30B3\u30F3\u30C6\u30AD\u30B9\u30C8\u3092\u4ED8\u3051\u305F\u65B0\u898F/\u66F4\u65B0\u30BF\u30B9\u30AF\u306E\u901A\u77E5\u3092 ON \u306B\u81EA\u52D5\u7D99\u627F\u3059\u308B")
|
|
3196
3246
|
});
|
|
3197
3247
|
async function updateContextHandler(ctx, input) {
|
|
3198
3248
|
const existing = await ctx.repos.context.findById(input.id);
|
|
@@ -3211,6 +3261,9 @@ async function updateContextHandler(ctx, input) {
|
|
|
3211
3261
|
patch.color = input.color === null ? void 0 : input.color;
|
|
3212
3262
|
}
|
|
3213
3263
|
if (input.status !== void 0) patch.status = input.status;
|
|
3264
|
+
if (input.notify_at_scheduled_start !== void 0) {
|
|
3265
|
+
patch.notifyAtScheduledStart = input.notify_at_scheduled_start;
|
|
3266
|
+
}
|
|
3214
3267
|
const updated = await ctx.repos.context.update(input.id, patch);
|
|
3215
3268
|
return { context: contextSummaryFrom(updated) };
|
|
3216
3269
|
}
|
|
@@ -3267,12 +3320,23 @@ var frequencySchema2 = z.discriminatedUnion("type", [
|
|
|
3267
3320
|
z.object({ type: z.literal("daily") }),
|
|
3268
3321
|
z.object({
|
|
3269
3322
|
type: z.literal("weekly"),
|
|
3270
|
-
weekdays: z.array(z.number().int().min(0).max(6)).min(1)
|
|
3323
|
+
weekdays: z.array(z.number().int().min(0).max(6)).min(1),
|
|
3324
|
+
intervalWeeks: z.number().int().min(1).max(4).optional(),
|
|
3325
|
+
anchorDate: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional()
|
|
3271
3326
|
}),
|
|
3272
3327
|
z.object({
|
|
3273
3328
|
type: z.literal("monthly"),
|
|
3274
3329
|
days: z.array(z.number().int().min(1).max(31)).min(1)
|
|
3275
3330
|
}),
|
|
3331
|
+
z.object({
|
|
3332
|
+
type: z.literal("monthly-nth-weekday"),
|
|
3333
|
+
occurrences: z.array(
|
|
3334
|
+
z.object({
|
|
3335
|
+
nth: z.number().int().refine((n) => n >= 1 && n <= 5 || n === -1, "nth must be 1-5 or -1 (last)"),
|
|
3336
|
+
weekday: z.number().int().min(0).max(6)
|
|
3337
|
+
})
|
|
3338
|
+
).min(1)
|
|
3339
|
+
}),
|
|
3276
3340
|
z.object({ type: z.literal("custom"), cron: z.string().min(1) })
|
|
3277
3341
|
]);
|
|
3278
3342
|
var updateRoutineInputSchema = z.object({
|
|
@@ -3350,9 +3414,11 @@ async function updateRoutineHandler(ctx, input) {
|
|
|
3350
3414
|
);
|
|
3351
3415
|
}
|
|
3352
3416
|
const taskPatch = buildRoutineTaskPatch(updated);
|
|
3417
|
+
const routineContext = updated.contextId ? await ctx.repos.context.findById(updated.contextId) : null;
|
|
3418
|
+
const propagatedPatch = applyContextNotificationInheritance(taskPatch, routineContext);
|
|
3353
3419
|
await Promise.all(
|
|
3354
3420
|
targets.map(
|
|
3355
|
-
(task) => auditingTask.updateWithCause(task.id,
|
|
3421
|
+
(task) => auditingTask.updateWithCause(task.id, propagatedPatch, "routine-propagation")
|
|
3356
3422
|
)
|
|
3357
3423
|
);
|
|
3358
3424
|
}
|
|
@@ -3446,6 +3512,11 @@ async function updateTaskHandler(ctx, input) {
|
|
|
3446
3512
|
patch.notifyAtScheduledStart = input.notify_at_scheduled_start;
|
|
3447
3513
|
}
|
|
3448
3514
|
if (input.is_background !== void 0) patch.isBackground = input.is_background;
|
|
3515
|
+
const newContextId = patch.contextId;
|
|
3516
|
+
if (newContextId) {
|
|
3517
|
+
const newContext = await ctx.repos.context.findById(newContextId);
|
|
3518
|
+
Object.assign(patch, applyContextNotificationInheritance(patch, newContext));
|
|
3519
|
+
}
|
|
3449
3520
|
const updated = await ctx.repos.task.update(input.id, patch);
|
|
3450
3521
|
return { task: taskSummaryFrom(updated) };
|
|
3451
3522
|
}
|
|
@@ -3900,7 +3971,7 @@ function createMcpServer(opts) {
|
|
|
3900
3971
|
|
|
3901
3972
|
// src/index.ts
|
|
3902
3973
|
async function main() {
|
|
3903
|
-
const version = "0.
|
|
3974
|
+
const version = "0.6.0" ;
|
|
3904
3975
|
const server = createMcpServer({ version });
|
|
3905
3976
|
const transport = new StdioServerTransport();
|
|
3906
3977
|
await server.connect(transport);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fukuchang/taskchute-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "TaskChute MCP server — Model Context Protocol 経由で E2EE 暗号化された TaskChute タスクを Claude / 各種 MCP クライアントから操作する",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "fukuchang",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"e2ee"
|
|
24
24
|
],
|
|
25
25
|
"engines": {
|
|
26
|
-
"node": "
|
|
26
|
+
"node": "^22.23.4 || ^24.22.1 || ^26.12.1"
|
|
27
27
|
},
|
|
28
28
|
"type": "module",
|
|
29
29
|
"main": "./dist/index.js",
|
|
@@ -48,14 +48,14 @@
|
|
|
48
48
|
"zod": "^4.0.0"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
|
-
"@types/node": "^24.12.3",
|
|
52
|
-
"tsup": "^8.5.1",
|
|
53
|
-
"tsx": "^4.20.0",
|
|
54
|
-
"typescript": "~6.0.2",
|
|
55
51
|
"@taskchute/crypto": "0.0.0",
|
|
56
52
|
"@taskchute/domain": "0.0.0",
|
|
57
53
|
"@taskchute/infra-supabase": "0.0.0",
|
|
58
|
-
"@taskchute/node-shared": "0.0.0"
|
|
54
|
+
"@taskchute/node-shared": "0.0.0",
|
|
55
|
+
"@types/node": "^24.12.3",
|
|
56
|
+
"tsup": "^8.5.1",
|
|
57
|
+
"tsx": "^4.20.0",
|
|
58
|
+
"typescript": "~6.0.2"
|
|
59
59
|
},
|
|
60
60
|
"scripts": {
|
|
61
61
|
"start": "tsx src/index.ts",
|