@fukuchang/taskchute-mcp 0.1.2 → 0.2.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/dist/index.js CHANGED
@@ -1,13 +1,80 @@
1
1
  #!/usr/bin/env node
2
2
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
3
3
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
4
+ import { Entry } from '@napi-rs/keyring';
4
5
  import { argon2id } from 'hash-wasm';
5
6
  import '@scure/bip39';
6
7
  import '@scure/bip39/wordlists/english.js';
7
8
  import { createClient } from '@supabase/supabase-js';
8
- import { Entry } from '@napi-rs/keyring';
9
9
  import { z } from 'zod';
10
10
 
11
+ // ../node-shared/src/app-context-error.ts
12
+ var AppContextError = class extends Error {
13
+ constructor(message) {
14
+ super(message);
15
+ this.name = "AppContextError";
16
+ }
17
+ };
18
+
19
+ // ../node-shared/src/config.ts
20
+ var ENV_URL = "TASKCHUTE_SUPABASE_URL";
21
+ var ENV_KEY = "TASKCHUTE_SUPABASE_PUBLISHABLE_KEY";
22
+ function readSupabaseConfigFromEnv() {
23
+ const url = process.env.TASKCHUTE_SUPABASE_URL;
24
+ const publishableKey = process.env.TASKCHUTE_SUPABASE_PUBLISHABLE_KEY;
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
+ );
29
+ }
30
+ return { url, publishableKey };
31
+ }
32
+ var KEYRING_SERVICE = "taskchute-cli";
33
+ var KEYRING_ACCOUNT = "passphrase";
34
+ var KeyringPassphraseStore = class {
35
+ get() {
36
+ try {
37
+ const entry = new Entry(KEYRING_SERVICE, KEYRING_ACCOUNT);
38
+ return entry.getPassword();
39
+ } catch {
40
+ return null;
41
+ }
42
+ }
43
+ set(passphrase) {
44
+ const entry = new Entry(KEYRING_SERVICE, KEYRING_ACCOUNT);
45
+ entry.setPassword(passphrase);
46
+ }
47
+ clear() {
48
+ try {
49
+ const entry = new Entry(KEYRING_SERVICE, KEYRING_ACCOUNT);
50
+ entry.deletePassword();
51
+ } catch {
52
+ }
53
+ }
54
+ };
55
+ var KEYRING_SERVICE2 = "taskchute-cli";
56
+ var KeyringSessionStore = class {
57
+ getItem(key) {
58
+ try {
59
+ const entry = new Entry(KEYRING_SERVICE2, key);
60
+ return entry.getPassword();
61
+ } catch {
62
+ return null;
63
+ }
64
+ }
65
+ setItem(key, value) {
66
+ const entry = new Entry(KEYRING_SERVICE2, key);
67
+ entry.setPassword(value);
68
+ }
69
+ removeItem(key) {
70
+ try {
71
+ const entry = new Entry(KEYRING_SERVICE2, key);
72
+ entry.deletePassword();
73
+ } catch {
74
+ }
75
+ }
76
+ };
77
+
11
78
  // ../crypto/src/bytes.ts
12
79
  function allocBytes(length) {
13
80
  return new Uint8Array(new ArrayBuffer(length));
@@ -668,6 +735,39 @@ var SupabaseProjectRepository = class {
668
735
  }
669
736
  };
670
737
 
738
+ // ../infra-supabase/src/supabase-routine-expansion-repository.ts
739
+ function rowToExpansion(row) {
740
+ return {
741
+ routineId: row.routine_id,
742
+ date: row.date,
743
+ status: row.status
744
+ };
745
+ }
746
+ var SupabaseRoutineExpansionRepository = class {
747
+ supabase;
748
+ constructor(supabase) {
749
+ this.supabase = supabase;
750
+ }
751
+ async findByDate(date) {
752
+ const { data, error } = await this.supabase.from("routine_expansions").select("*").eq("date", date);
753
+ if (error) throw error;
754
+ return data.map(rowToExpansion);
755
+ }
756
+ async recordExpanded(routineId, date) {
757
+ const userId = await getCurrentUserId(this.supabase);
758
+ const { error } = await this.supabase.from("routine_expansions").upsert(
759
+ {
760
+ user_id: userId,
761
+ routine_id: routineId,
762
+ date,
763
+ status: "expanded"
764
+ },
765
+ { onConflict: "routine_id,date", ignoreDuplicates: true }
766
+ );
767
+ if (error) throw error;
768
+ }
769
+ };
770
+
671
771
  // ../infra-supabase/src/supabase-routine-repository.ts
672
772
  async function rowToRoutine(row, cryptoSession) {
673
773
  const [title, description] = await Promise.all([
@@ -705,7 +805,6 @@ async function rowToRoutine(row, cryptoSession) {
705
805
  }
706
806
  if (row.start_date !== null) routine.startDate = row.start_date;
707
807
  if (row.end_date !== null) routine.endDate = row.end_date;
708
- if (row.last_expanded_date !== null) routine.lastExpandedDate = row.last_expanded_date;
709
808
  return routine;
710
809
  }
711
810
  function nonEncryptedFieldsToRow3(input) {
@@ -727,9 +826,6 @@ function nonEncryptedFieldsToRow3(input) {
727
826
  if ("autoExpand" in input && input.autoExpand !== void 0) {
728
827
  row.auto_expand = input.autoExpand;
729
828
  }
730
- if ("lastExpandedDate" in input) {
731
- row.last_expanded_date = input.lastExpandedDate ?? null;
732
- }
733
829
  if ("status" in input && input.status !== void 0) row.status = input.status;
734
830
  return row;
735
831
  }
@@ -1286,6 +1382,13 @@ var SupabaseTaskRepository = class {
1286
1382
  const { error } = await this.supabase.from("tasks").delete().eq("id", id);
1287
1383
  if (error) throw error;
1288
1384
  }
1385
+ async deleteWithRoutineSkip(id, today) {
1386
+ const { error } = await this.supabase.rpc("delete_task_and_skip_routine", {
1387
+ p_task_id: id,
1388
+ p_today: today
1389
+ });
1390
+ if (error) throw error;
1391
+ }
1289
1392
  };
1290
1393
 
1291
1394
  // ../infra-supabase/src/supabase-time-block-repository.ts
@@ -1495,6 +1598,13 @@ function toDateString(date) {
1495
1598
  const day = String(date.getDate()).padStart(2, "0");
1496
1599
  return `${year}-${month}-${day}`;
1497
1600
  }
1601
+ function fromDateString(dateString) {
1602
+ const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(dateString);
1603
+ if (!match || !match[1] || !match[2] || !match[3]) {
1604
+ throw new Error(`Invalid date string: ${dateString}`);
1605
+ }
1606
+ return new Date(Number(match[1]), Number(match[2]) - 1, Number(match[3]));
1607
+ }
1498
1608
  function todayDateString() {
1499
1609
  return toDateString(/* @__PURE__ */ new Date());
1500
1610
  }
@@ -1504,7 +1614,32 @@ function toTimeString(date) {
1504
1614
  return `${hours}:${minutes}`;
1505
1615
  }
1506
1616
 
1617
+ // ../domain/src/task-utils.ts
1618
+ function sessionDurationSeconds(session) {
1619
+ return (session.finishedAt.getTime() - session.startedAt.getTime()) / 1e3;
1620
+ }
1621
+ function completedSessionsTotalSeconds(task) {
1622
+ return task.sessions.reduce((sum, s) => sum + sessionDurationSeconds(s), 0);
1623
+ }
1624
+ function calculateActualSeconds(task, now = /* @__PURE__ */ new Date()) {
1625
+ const completed = completedSessionsTotalSeconds(task);
1626
+ if (task.currentSession) {
1627
+ return completed + (now.getTime() - task.currentSession.startedAt.getTime()) / 1e3;
1628
+ }
1629
+ return completed;
1630
+ }
1631
+
1507
1632
  // ../domain/src/task-operations.ts
1633
+ var TIME_RE = /^([01]\d|2[0-3]):([0-5]\d)$/;
1634
+ function parseTimeOnDate(dateString, timeString) {
1635
+ const m = TIME_RE.exec(timeString);
1636
+ if (!m || !m[1] || !m[2]) {
1637
+ throw new Error(`Invalid time string: ${timeString}`);
1638
+ }
1639
+ const base = fromDateString(dateString);
1640
+ base.setHours(Number(m[1]), Number(m[2]), 0, 0);
1641
+ return base;
1642
+ }
1508
1643
  async function startTask(repo, taskId) {
1509
1644
  const task = await repo.findById(taskId);
1510
1645
  if (!task) throw new Error(`Task not found: ${taskId}`);
@@ -1535,6 +1670,30 @@ async function pauseTask(repo, taskId) {
1535
1670
  currentSession: void 0
1536
1671
  });
1537
1672
  }
1673
+ async function skipTask(repo, taskId) {
1674
+ const task = await repo.findById(taskId);
1675
+ if (!task) throw new Error(`Task not found: ${taskId}`);
1676
+ const updates = { status: "skipped" };
1677
+ if (task.currentSession) {
1678
+ updates.sessions = [
1679
+ ...task.sessions,
1680
+ {
1681
+ startedAt: task.currentSession.startedAt,
1682
+ finishedAt: /* @__PURE__ */ new Date()
1683
+ }
1684
+ ];
1685
+ updates.currentSession = void 0;
1686
+ }
1687
+ return repo.update(taskId, updates);
1688
+ }
1689
+ async function reopenTask(repo, taskId) {
1690
+ const task = await repo.findById(taskId);
1691
+ if (!task) throw new Error(`Task not found: ${taskId}`);
1692
+ if (task.status !== "completed" && task.status !== "skipped") {
1693
+ return task;
1694
+ }
1695
+ return repo.update(taskId, { status: "planned" });
1696
+ }
1538
1697
  async function completeTask(repo, taskId) {
1539
1698
  const task = await repo.findById(taskId);
1540
1699
  if (!task) throw new Error(`Task not found: ${taskId}`);
@@ -1551,6 +1710,115 @@ async function completeTask(repo, taskId) {
1551
1710
  }
1552
1711
  return repo.update(taskId, updates);
1553
1712
  }
1713
+ async function interruptTask(repo, params) {
1714
+ const target = await repo.findById(params.targetTaskId);
1715
+ if (!target) throw new Error(`Task not found: ${params.targetTaskId}`);
1716
+ if (!target.currentSession) {
1717
+ throw new Error(`Target task is not currently running`);
1718
+ }
1719
+ const now = /* @__PURE__ */ new Date();
1720
+ const closedSession = {
1721
+ startedAt: target.currentSession.startedAt,
1722
+ finishedAt: now
1723
+ };
1724
+ const finishedOriginal = await repo.update(target.id, {
1725
+ status: "completed",
1726
+ sessions: [...target.sessions, closedSession],
1727
+ currentSession: void 0
1728
+ });
1729
+ const elapsed = calculateActualSeconds(finishedOriginal);
1730
+ const interruptionStartedAt = /* @__PURE__ */ new Date();
1731
+ const interruption = await repo.create({
1732
+ date: target.date,
1733
+ title: params.interruption.title,
1734
+ description: params.interruption.description,
1735
+ estimatedSeconds: params.interruption.estimatedSeconds,
1736
+ sessions: [],
1737
+ currentSession: { startedAt: interruptionStartedAt },
1738
+ status: "running",
1739
+ orderIndex: target.orderIndex + 1,
1740
+ actualStartTime: toTimeString(interruptionStartedAt),
1741
+ projectId: params.interruption.projectId,
1742
+ contextId: params.interruption.contextId,
1743
+ tagIds: params.interruption.tagIds ?? []
1744
+ });
1745
+ const continuation = await repo.create({
1746
+ date: target.date,
1747
+ title: target.title,
1748
+ description: target.description,
1749
+ estimatedSeconds: Math.max(0, Math.floor(target.estimatedSeconds - elapsed)),
1750
+ sessions: [],
1751
+ status: "planned",
1752
+ orderIndex: target.orderIndex + 2,
1753
+ splitFromTaskId: target.id,
1754
+ projectId: target.projectId,
1755
+ contextId: target.contextId,
1756
+ // continuation は「割り込みが片付いた後にやる残作業」。割り込み発生時刻を
1757
+ // 予定時刻にすることで、(1) interruption と同じ effectiveTime になり、
1758
+ // ソート上 orderIndex で interruption の次に並ぶ、(2) 割り込みが TimeBlock
1759
+ // の境界をまたいだ場合は continuation も新しいブロックに移動する、という
1760
+ // 自然な挙動になる。
1761
+ scheduledStartTime: toTimeString(interruptionStartedAt),
1762
+ tagIds: target.tagIds,
1763
+ isBackground: target.isBackground
1764
+ });
1765
+ return { finishedOriginal, interruption, continuation };
1766
+ }
1767
+ async function addParallelTask(repo, input) {
1768
+ const startImmediately = input.startImmediately ?? true;
1769
+ const existing = await repo.findByDate(input.date);
1770
+ const maxOrder = existing.reduce((max, t) => t.orderIndex > max ? t.orderIndex : max, 0);
1771
+ const startedAt = startImmediately ? /* @__PURE__ */ new Date() : void 0;
1772
+ return repo.create({
1773
+ date: input.date,
1774
+ title: input.title,
1775
+ description: input.description,
1776
+ estimatedSeconds: input.estimatedSeconds,
1777
+ sessions: [],
1778
+ currentSession: startedAt ? { startedAt } : void 0,
1779
+ status: startImmediately ? "running" : "planned",
1780
+ orderIndex: maxOrder + 10,
1781
+ actualStartTime: startedAt ? toTimeString(startedAt) : void 0,
1782
+ projectId: input.projectId,
1783
+ contextId: input.contextId,
1784
+ tagIds: input.tagIds ?? [],
1785
+ isBackground: input.isBackground
1786
+ });
1787
+ }
1788
+ function buildSetActualTimesPatch(task, input) {
1789
+ const startTime = input.startTime?.trim() ? input.startTime.trim() : null;
1790
+ const endTime = input.endTime?.trim() ? input.endTime.trim() : null;
1791
+ if (startTime === null && endTime === null) {
1792
+ return {
1793
+ status: "planned",
1794
+ currentSession: void 0,
1795
+ sessions: [],
1796
+ actualStartTime: void 0
1797
+ };
1798
+ }
1799
+ if (startTime === null && endTime !== null) {
1800
+ throw new Error("endTime requires startTime");
1801
+ }
1802
+ const startedAt = parseTimeOnDate(task.date, startTime);
1803
+ if (endTime === null) {
1804
+ return {
1805
+ status: "running",
1806
+ currentSession: { startedAt },
1807
+ sessions: [],
1808
+ actualStartTime: startTime
1809
+ };
1810
+ }
1811
+ let finishedAt = parseTimeOnDate(task.date, endTime);
1812
+ if (finishedAt.getTime() < startedAt.getTime()) {
1813
+ finishedAt = new Date(finishedAt.getTime() + 24 * 60 * 60 * 1e3);
1814
+ }
1815
+ return {
1816
+ status: "completed",
1817
+ currentSession: void 0,
1818
+ sessions: [{ startedAt, finishedAt }],
1819
+ actualStartTime: startTime
1820
+ };
1821
+ }
1554
1822
 
1555
1823
  // ../domain/src/task-reorder.ts
1556
1824
  function computeNextOrderIndex(existingTasks) {
@@ -1562,6 +1830,70 @@ function computeNextOrderIndex(existingTasks) {
1562
1830
  return max + 10;
1563
1831
  }
1564
1832
 
1833
+ // ../domain/src/task-suggest.ts
1834
+ function normalizeForSearch(str) {
1835
+ return str.trim().toLowerCase().replace(
1836
+ /[ぁ-ゖ]/g,
1837
+ (ch) => String.fromCharCode(ch.charCodeAt(0) + 96)
1838
+ );
1839
+ }
1840
+ function fuzzyScore(query, target) {
1841
+ const q = query;
1842
+ const t = target;
1843
+ if (q.length === 0) return 0;
1844
+ if (t.length === 0) return null;
1845
+ let qi = 0;
1846
+ let score = 0;
1847
+ let lastMatchIndex = -2;
1848
+ let consecutiveRun = 0;
1849
+ for (let ti = 0; ti < t.length && qi < q.length; ti++) {
1850
+ if (t[ti] === q[qi]) {
1851
+ if (lastMatchIndex === ti - 1) {
1852
+ consecutiveRun += 1;
1853
+ score += 5 + consecutiveRun * 3;
1854
+ } else {
1855
+ consecutiveRun = 0;
1856
+ score += 2;
1857
+ }
1858
+ if (qi === 0) {
1859
+ score += Math.max(0, 8 - ti);
1860
+ }
1861
+ lastMatchIndex = ti;
1862
+ qi += 1;
1863
+ }
1864
+ }
1865
+ if (qi < q.length) return null;
1866
+ score -= Math.max(0, t.length - q.length) / 4;
1867
+ return score;
1868
+ }
1869
+ function findSimilarTasks(query, tasks, limit = 5) {
1870
+ const q = normalizeForSearch(query);
1871
+ if (q.length === 0) return [];
1872
+ const bestByTitle = /* @__PURE__ */ new Map();
1873
+ for (const task of tasks) {
1874
+ const titleTrimmed = task.title.trim();
1875
+ if (titleTrimmed.length === 0) continue;
1876
+ const titleNormalized = normalizeForSearch(titleTrimmed);
1877
+ const score = fuzzyScore(q, titleNormalized);
1878
+ if (score === null) continue;
1879
+ const existing = bestByTitle.get(titleNormalized);
1880
+ if (!existing) {
1881
+ bestByTitle.set(titleNormalized, { task, score });
1882
+ continue;
1883
+ }
1884
+ const existingNewer = existing.task.updatedAt.getTime();
1885
+ const candidateNewer = task.updatedAt.getTime();
1886
+ if (candidateNewer > existingNewer) {
1887
+ bestByTitle.set(titleNormalized, { task, score });
1888
+ }
1889
+ }
1890
+ const sorted = Array.from(bestByTitle.values()).sort((a, b) => {
1891
+ if (b.score !== a.score) return b.score - a.score;
1892
+ return b.task.updatedAt.getTime() - a.task.updatedAt.getTime();
1893
+ });
1894
+ return sorted.slice(0, limit).map((s) => s.task);
1895
+ }
1896
+
1565
1897
  // ../domain/src/task-change-log.ts
1566
1898
  function describeStatusTransition(from, to) {
1567
1899
  if (from === "planned" && to === "running") return "\u958B\u59CB";
@@ -1796,6 +2128,80 @@ function diffTasksToNoteInputs(before, after, lookups) {
1796
2128
  }
1797
2129
  return result;
1798
2130
  }
2131
+ function titleOrPlaceholder(task) {
2132
+ return task.title || "\u7121\u984C\u306E\u30BF\u30B9\u30AF";
2133
+ }
2134
+ async function interruptTaskWithAudit(taskRepo, taskNoteRepo, params) {
2135
+ const result = await interruptTask(taskRepo, params);
2136
+ const notes = buildSplitNoteInputs(
2137
+ result.finishedOriginal,
2138
+ result.interruption,
2139
+ result.continuation
2140
+ );
2141
+ try {
2142
+ await Promise.all(notes.map((input) => taskNoteRepo.create(input)));
2143
+ } catch (err) {
2144
+ console.error("[audit] failed to record split notes", {
2145
+ sourceTaskId: result.finishedOriginal.id,
2146
+ err
2147
+ });
2148
+ }
2149
+ return result;
2150
+ }
2151
+ function buildSplitNoteInputs(source, interruption, continuation) {
2152
+ const sourceTitle = titleOrPlaceholder(source);
2153
+ const interruptionTitle = titleOrPlaceholder(interruption);
2154
+ return [
2155
+ {
2156
+ taskId: source.id,
2157
+ type: "split",
2158
+ body: `\u300C${interruptionTitle}\u300D\u306E\u5272\u308A\u8FBC\u307F\u3067\u5206\u5272\u3002\u6B8B\u4F5C\u696D\u306F\u5225\u30BF\u30B9\u30AF\u3067\u7D99\u7D9A\u3002`,
2159
+ metadata: {
2160
+ event: "split-source",
2161
+ interruptionTaskId: interruption.id,
2162
+ continuationTaskId: continuation.id
2163
+ }
2164
+ },
2165
+ {
2166
+ taskId: interruption.id,
2167
+ type: "split",
2168
+ body: `\u300C${sourceTitle}\u300D\u306E\u4F5C\u696D\u4E2D\u306B\u5272\u308A\u8FBC\u307F\u3068\u3057\u3066\u8D77\u7968\u3002`,
2169
+ metadata: {
2170
+ event: "split-interruption",
2171
+ sourceTaskId: source.id,
2172
+ continuationTaskId: continuation.id
2173
+ }
2174
+ },
2175
+ {
2176
+ taskId: continuation.id,
2177
+ type: "split",
2178
+ body: `\u300C${sourceTitle}\u300D\u306E\u6B8B\u4F5C\u696D\u3068\u3057\u3066\u7D99\u7D9A\u3002`,
2179
+ metadata: {
2180
+ event: "split-continuation",
2181
+ sourceTaskId: source.id,
2182
+ interruptionTaskId: interruption.id
2183
+ }
2184
+ }
2185
+ ];
2186
+ }
2187
+
2188
+ // ../domain/src/routine-expansion.ts
2189
+ function buildRoutineTaskPatch(routine) {
2190
+ return {
2191
+ title: routine.title,
2192
+ description: routine.description,
2193
+ estimatedSeconds: routine.estimatedSeconds,
2194
+ projectId: routine.projectId,
2195
+ contextId: routine.contextId,
2196
+ tagIds: routine.tagIds,
2197
+ scheduledStartTime: routine.defaultScheduledStartTime
2198
+ };
2199
+ }
2200
+ function pickPropagationTargets(routine, tasks, today) {
2201
+ return tasks.filter(
2202
+ (t) => t.routineId === routine.id && t.date >= today && t.status === "planned"
2203
+ );
2204
+ }
1799
2205
 
1800
2206
  // ../infra-supabase/src/audit-task-repository.ts
1801
2207
  var AuditingTaskRepository = class {
@@ -1863,17 +2269,23 @@ var AuditingTaskRepository = class {
1863
2269
  delete(id) {
1864
2270
  return this.inner.delete(id);
1865
2271
  }
2272
+ deleteWithRoutineSkip(id, today) {
2273
+ return this.inner.deleteWithRoutineSkip(id, today);
2274
+ }
1866
2275
  };
1867
2276
 
1868
2277
  // ../infra-supabase/src/composition-root.ts
1869
2278
  function buildRepositories(supabaseClient, cryptoSession, getLookups) {
1870
2279
  const taskNote = new SupabaseTaskNoteRepository(supabaseClient, cryptoSession);
1871
2280
  const rawTask = new SupabaseTaskRepository(supabaseClient, cryptoSession);
1872
- const task = getLookups ? new AuditingTaskRepository(rawTask, taskNote, getLookups) : rawTask;
2281
+ const auditingTask = getLookups ? new AuditingTaskRepository(rawTask, taskNote, getLookups) : void 0;
2282
+ const task = auditingTask ?? rawTask;
1873
2283
  return {
1874
2284
  task,
2285
+ auditingTask,
1875
2286
  taskNote,
1876
2287
  routine: new SupabaseRoutineRepository(supabaseClient, cryptoSession),
2288
+ routineExpansion: new SupabaseRoutineExpansionRepository(supabaseClient),
1877
2289
  project: new SupabaseProjectRepository(supabaseClient, cryptoSession),
1878
2290
  tag: new SupabaseTagRepository(supabaseClient, cryptoSession),
1879
2291
  context: new SupabaseContextRepository(supabaseClient, cryptoSession),
@@ -1881,126 +2293,111 @@ function buildRepositories(supabaseClient, cryptoSession, getLookups) {
1881
2293
  };
1882
2294
  }
1883
2295
 
1884
- // src/config.ts
1885
- var ENV_URL = "TASKCHUTE_SUPABASE_URL";
1886
- var ENV_KEY = "TASKCHUTE_SUPABASE_PUBLISHABLE_KEY";
1887
- function readSupabaseConfigFromEnv() {
1888
- const url = process.env.TASKCHUTE_SUPABASE_URL;
1889
- const publishableKey = process.env.TASKCHUTE_SUPABASE_PUBLISHABLE_KEY;
1890
- if (!url || !publishableKey) {
1891
- throw new Error(
1892
- `Supabase \u306E\u74B0\u5883\u5909\u6570\u304C\u898B\u3064\u304B\u3089\u306A\u3044\u3002${ENV_URL} \u3068 ${ENV_KEY} \u3092 MCP server \u306E env \u306B\u8A2D\u5B9A\u3057\u3066\u306D\u301C`
1893
- );
1894
- }
1895
- return { url, publishableKey };
1896
- }
1897
- var KEYRING_SERVICE = "taskchute-cli";
1898
- var KEYRING_ACCOUNT = "passphrase";
1899
- var KeyringPassphraseStore = class {
1900
- get() {
1901
- try {
1902
- const entry = new Entry(KEYRING_SERVICE, KEYRING_ACCOUNT);
1903
- return entry.getPassword();
1904
- } catch {
1905
- return null;
1906
- }
1907
- }
1908
- set(passphrase) {
1909
- const entry = new Entry(KEYRING_SERVICE, KEYRING_ACCOUNT);
1910
- entry.setPassword(passphrase);
1911
- }
1912
- clear() {
1913
- try {
1914
- const entry = new Entry(KEYRING_SERVICE, KEYRING_ACCOUNT);
1915
- entry.deletePassword();
1916
- } catch {
1917
- }
1918
- }
1919
- };
1920
- var KEYRING_SERVICE2 = "taskchute-cli";
1921
- var KeyringSessionStore = class {
1922
- getItem(key) {
1923
- try {
1924
- const entry = new Entry(KEYRING_SERVICE2, key);
1925
- return entry.getPassword();
1926
- } catch {
1927
- return null;
1928
- }
1929
- }
1930
- setItem(key, value) {
1931
- const entry = new Entry(KEYRING_SERVICE2, key);
1932
- entry.setPassword(value);
1933
- }
1934
- removeItem(key) {
1935
- try {
1936
- const entry = new Entry(KEYRING_SERVICE2, key);
1937
- entry.deletePassword();
1938
- } catch {
1939
- }
1940
- }
1941
- };
1942
-
1943
- // src/crypto-context.ts
1944
- var McpToolError = class extends Error {
1945
- constructor(message) {
1946
- super(message);
1947
- this.name = "McpToolError";
1948
- }
1949
- };
1950
- function resolvePassphrase(store) {
2296
+ // ../node-shared/src/create-node-app-context.ts
2297
+ async function resolvePassphrase(store, promptPassphrase) {
1951
2298
  const envPw = process.env.TASKCHUTE_PASSPHRASE;
1952
2299
  if (envPw) return envPw;
1953
2300
  const stored = store.get();
1954
2301
  if (stored) return stored;
1955
- throw new McpToolError(
1956
- "No passphrase available. Set TASKCHUTE_PASSPHRASE env or run `taskchute unlock` from the CLI first."
1957
- );
2302
+ if (!promptPassphrase) {
2303
+ throw new AppContextError(
2304
+ "No passphrase available. Set TASKCHUTE_PASSPHRASE env or run `taskchute unlock` from the CLI first."
2305
+ );
2306
+ }
2307
+ return await promptPassphrase();
1958
2308
  }
1959
- async function createAppContext(opts = {}) {
2309
+ async function createNodeAppContext(opts = {}) {
1960
2310
  const passphraseStore = opts.passphraseStore ?? new KeyringPassphraseStore();
2311
+ const sessionStorage = opts.sessionStorage ?? new KeyringSessionStore();
1961
2312
  const supabase = createNodeSupabaseClient(readSupabaseConfigFromEnv(), {
1962
- storage: new KeyringSessionStore()
2313
+ storage: sessionStorage
1963
2314
  });
1964
2315
  const {
1965
2316
  data: { user },
1966
2317
  error: authError
1967
2318
  } = await supabase.auth.getUser();
1968
2319
  if (authError || !user || !user.email) {
1969
- throw new McpToolError(
2320
+ throw new AppContextError(
1970
2321
  "Not logged in. Run `taskchute login` from the CLI first (session is shared via OS keyring)."
1971
2322
  );
1972
2323
  }
1973
2324
  const userKeysRepo = new SupabaseUserKeysRepository(supabase);
1974
2325
  const snapshot = await userKeysRepo.getOwn();
1975
2326
  if (!snapshot) {
1976
- throw new McpToolError(
2327
+ throw new AppContextError(
1977
2328
  "E2EE not set up. Complete setup from the web app first (Settings \u2192 \u30C7\u30FC\u30BF\u306E\u6697\u53F7\u5316)."
1978
2329
  );
1979
2330
  }
1980
- const passphrase = resolvePassphrase(passphraseStore);
2331
+ const passphrase = await resolvePassphrase(passphraseStore, opts.promptPassphrase);
1981
2332
  let masterKey;
1982
2333
  try {
1983
2334
  masterKey = await unlockMasterKey(snapshot, { kind: "passphrase", passphrase });
1984
2335
  } catch (err) {
1985
2336
  if (err instanceof UnlockError) {
1986
- throw new McpToolError(`Unlock failed: ${err.message}`);
2337
+ throw new AppContextError(`Unlock failed: ${err.message}`);
1987
2338
  }
1988
2339
  throw err;
1989
2340
  }
1990
2341
  const cryptoSession = new CryptoSession();
1991
2342
  cryptoSession.setMasterKey(masterKey, snapshot.currentKeyId);
1992
- const repos = buildRepositories(supabase, cryptoSession);
2343
+ const lookupsRef = {
2344
+ current: { projects: [], tags: [], contexts: [] }
2345
+ };
2346
+ const repos = buildRepositories(supabase, cryptoSession, () => lookupsRef.current);
2347
+ const refreshLookups = async () => {
2348
+ const [projects, tags, contexts] = await Promise.all([
2349
+ repos.project.findAll(),
2350
+ repos.tag.findAll(),
2351
+ repos.context.findAll()
2352
+ ]);
2353
+ lookupsRef.current = { projects, tags, contexts };
2354
+ };
2355
+ try {
2356
+ await refreshLookups();
2357
+ } catch (err) {
2358
+ console.error("[createNodeAppContext] initial refreshLookups failed:", err);
2359
+ }
1993
2360
  return {
1994
2361
  supabase,
1995
2362
  cryptoSession,
1996
2363
  repos,
1997
2364
  userId: user.id,
1998
- email: user.email
2365
+ email: user.email,
2366
+ refreshLookups
1999
2367
  };
2000
2368
  }
2001
2369
 
2002
- // src/format/task-summary.ts
2003
- function taskSummaryFrom(task) {
2370
+ // src/crypto-context.ts
2371
+ var McpToolError = class extends Error {
2372
+ constructor(message) {
2373
+ super(message);
2374
+ this.name = "McpToolError";
2375
+ }
2376
+ };
2377
+ async function wrapDomainErrors(op) {
2378
+ try {
2379
+ return await op();
2380
+ } catch (err) {
2381
+ if (err instanceof McpToolError) throw err;
2382
+ if (err instanceof Error) throw new McpToolError(err.message);
2383
+ throw err;
2384
+ }
2385
+ }
2386
+ async function createAppContext(opts = {}) {
2387
+ try {
2388
+ return await createNodeAppContext({
2389
+ passphraseStore: opts.passphraseStore
2390
+ });
2391
+ } catch (err) {
2392
+ if (err instanceof AppContextError) {
2393
+ throw new McpToolError(err.message);
2394
+ }
2395
+ throw err;
2396
+ }
2397
+ }
2398
+
2399
+ // src/format/task-summary.ts
2400
+ function taskSummaryFrom(task) {
2004
2401
  const summary = {
2005
2402
  id: task.id,
2006
2403
  date: task.date,
@@ -2033,15 +2430,81 @@ function taskSummaryFrom(task) {
2033
2430
  return summary;
2034
2431
  }
2035
2432
 
2036
- // src/tools/add-task.ts
2433
+ // src/tools/add-parallel-task.ts
2434
+ var addParallelTaskInputSchema = z.object({
2435
+ title: z.string().min(1, "title must not be empty"),
2436
+ estimated_seconds: z.number().int().positive(),
2437
+ date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "date must be YYYY-MM-DD").optional().describe("\u5BFE\u8C61\u65E5\u3002\u7701\u7565\u6642\u306F\u4ECA\u65E5"),
2438
+ description: z.string().optional(),
2439
+ project_id: z.string().uuid().optional(),
2440
+ context_id: z.string().uuid().optional(),
2441
+ tag_ids: z.array(z.string().uuid()).optional(),
2442
+ is_background: z.boolean().optional(),
2443
+ start_immediately: z.boolean().optional().describe("true (default) \u3067 running \u72B6\u614B + \u5373\u8A08\u6E2C\u958B\u59CB\u3002false \u3067 planned \u4F5C\u6210\u306E\u307F")
2444
+ });
2445
+ async function addParallelTaskHandler(ctx, input) {
2446
+ if (input.project_id) {
2447
+ const p = await ctx.repos.project.findById(input.project_id);
2448
+ if (!p) throw new McpToolError(`Project not found: ${input.project_id}`);
2449
+ }
2450
+ if (input.context_id) {
2451
+ const c = await ctx.repos.context.findById(input.context_id);
2452
+ if (!c) throw new McpToolError(`Context not found: ${input.context_id}`);
2453
+ }
2454
+ if (input.tag_ids && input.tag_ids.length > 0) {
2455
+ const checked = await Promise.all(
2456
+ input.tag_ids.map(async (id) => ({
2457
+ id,
2458
+ exists: await ctx.repos.tag.findById(id) !== null
2459
+ }))
2460
+ );
2461
+ const missing = checked.filter((c) => !c.exists).map((c) => c.id);
2462
+ if (missing.length > 0) throw new McpToolError(`Tag(s) not found: ${missing.join(", ")}`);
2463
+ }
2464
+ const task = await addParallelTask(ctx.repos.task, {
2465
+ date: input.date ?? todayDateString(),
2466
+ title: input.title,
2467
+ estimatedSeconds: input.estimated_seconds,
2468
+ ...input.description !== void 0 && { description: input.description },
2469
+ ...input.project_id !== void 0 && { projectId: input.project_id },
2470
+ ...input.context_id !== void 0 && { contextId: input.context_id },
2471
+ ...input.tag_ids !== void 0 && { tagIds: input.tag_ids },
2472
+ ...input.is_background !== void 0 && { isBackground: input.is_background },
2473
+ ...input.start_immediately !== void 0 && { startImmediately: input.start_immediately }
2474
+ });
2475
+ return { task: taskSummaryFrom(task) };
2476
+ }
2037
2477
  var addTaskInputSchema = z.object({
2038
2478
  title: z.string().min(1, "title must not be empty"),
2039
2479
  date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "date must be YYYY-MM-DD").optional().describe("\u5BFE\u8C61\u65E5 (YYYY-MM-DD)\u3002\u7701\u7565\u6642\u306F\u30B5\u30FC\u30D0\u30FC\u306E\u4ECA\u65E5 (Asia/Tokyo) \u3092\u4F7F\u3046"),
2040
2480
  estimated_seconds: z.number().int().positive().optional().describe("\u898B\u7A4D\u3082\u308A\u6642\u9593 (\u79D2)\u3002\u7701\u7565\u6642\u306F 1800 (= 30 \u5206)"),
2041
- description: z.string().optional()
2481
+ description: z.string().optional(),
2482
+ scheduled_start_time: z.string().regex(/^([01]\d|2[0-3]):[0-5]\d$/, "scheduled_start_time must be HH:MM").optional().describe("\u4E88\u5B9A\u958B\u59CB\u6642\u523B (HH:MM)\u3002\u30BF\u30A4\u30E0\u30D6\u30ED\u30C3\u30AF / \u30BD\u30FC\u30C8\u9806\u306B\u5F71\u97FF"),
2483
+ project_id: z.string().uuid().optional(),
2484
+ context_id: z.string().uuid().optional(),
2485
+ tag_ids: z.array(z.string().uuid()).optional(),
2486
+ notify_at_scheduled_start: z.boolean().optional().describe("true \u306A\u3089 scheduled_start_time \u5230\u9054\u6642\u306B\u901A\u77E5 (Web \u306E\u307F\u52D5\u4F5C)"),
2487
+ is_background: z.boolean().optional().describe("true \u306A\u3089\u4E26\u884C\u9032\u884C\u4E2D\u306E\u30D0\u30C3\u30AF\u30B0\u30E9\u30A6\u30F3\u30C9\u30BF\u30B9\u30AF\u6271\u3044 (\u898B\u7A4D\u3082\u308A\u96C6\u8A08\u304B\u3089\u9664\u5916)")
2042
2488
  });
2043
2489
  var DEFAULT_ESTIMATED_SECONDS = 30 * 60;
2044
2490
  async function addTaskHandler(ctx, input) {
2491
+ if (input.project_id) {
2492
+ const project = await ctx.repos.project.findById(input.project_id);
2493
+ if (!project) throw new McpToolError(`Project not found: ${input.project_id}`);
2494
+ }
2495
+ if (input.context_id) {
2496
+ const context = await ctx.repos.context.findById(input.context_id);
2497
+ if (!context) throw new McpToolError(`Context not found: ${input.context_id}`);
2498
+ }
2499
+ if (input.tag_ids && input.tag_ids.length > 0) {
2500
+ const checked = await Promise.all(
2501
+ input.tag_ids.map(async (id) => ({ id, exists: await ctx.repos.tag.findById(id) !== null }))
2502
+ );
2503
+ const missing = checked.filter((c) => !c.exists).map((c) => c.id);
2504
+ if (missing.length > 0) {
2505
+ throw new McpToolError(`Tag(s) not found: ${missing.join(", ")}`);
2506
+ }
2507
+ }
2045
2508
  const date = input.date ?? todayDateString();
2046
2509
  const estimatedSeconds = input.estimated_seconds ?? DEFAULT_ESTIMATED_SECONDS;
2047
2510
  const existing = await ctx.repos.task.findByDate(date);
@@ -2054,10 +2517,430 @@ async function addTaskHandler(ctx, input) {
2054
2517
  sessions: [],
2055
2518
  status: "planned",
2056
2519
  orderIndex,
2057
- tagIds: []
2520
+ tagIds: input.tag_ids ?? [],
2521
+ projectId: input.project_id,
2522
+ contextId: input.context_id,
2523
+ scheduledStartTime: input.scheduled_start_time,
2524
+ notifyAtScheduledStart: input.notify_at_scheduled_start,
2525
+ isBackground: input.is_background
2058
2526
  });
2059
2527
  return { task: taskSummaryFrom(task) };
2060
2528
  }
2529
+
2530
+ // src/format/context-summary.ts
2531
+ function contextSummaryFrom(context) {
2532
+ const summary = {
2533
+ id: context.id,
2534
+ name: context.name,
2535
+ status: context.status,
2536
+ created_at: context.createdAt.toISOString(),
2537
+ updated_at: context.updatedAt.toISOString()
2538
+ };
2539
+ if (context.description !== void 0) summary.description = context.description;
2540
+ if (context.icon !== void 0) summary.icon = context.icon;
2541
+ if (context.color !== void 0) summary.color = context.color;
2542
+ return summary;
2543
+ }
2544
+
2545
+ // src/tools/archive-context.ts
2546
+ var archiveContextInputSchema = z.object({
2547
+ id: z.string().uuid()
2548
+ });
2549
+ async function archiveContextHandler(ctx, input) {
2550
+ const existing = await ctx.repos.context.findById(input.id);
2551
+ if (!existing) {
2552
+ throw new McpToolError(`Context not found: ${input.id}`);
2553
+ }
2554
+ const archived = await ctx.repos.context.archive(input.id);
2555
+ return { context: contextSummaryFrom(archived) };
2556
+ }
2557
+
2558
+ // src/format/project-summary.ts
2559
+ function projectSummaryFrom(project) {
2560
+ const summary = {
2561
+ id: project.id,
2562
+ name: project.name,
2563
+ status: project.status,
2564
+ created_at: project.createdAt.toISOString(),
2565
+ updated_at: project.updatedAt.toISOString()
2566
+ };
2567
+ if (project.description !== void 0) summary.description = project.description;
2568
+ if (project.color !== void 0) summary.color = project.color;
2569
+ if (project.parentProjectId !== void 0) summary.parent_project_id = project.parentProjectId;
2570
+ if (project.goalId !== void 0) summary.goal_id = project.goalId;
2571
+ if (project.startedAt !== void 0) summary.started_at = project.startedAt.toISOString();
2572
+ if (project.targetEndDate !== void 0) {
2573
+ summary.target_end_date = project.targetEndDate.toISOString();
2574
+ }
2575
+ if (project.archivedAt !== void 0) summary.archived_at = project.archivedAt.toISOString();
2576
+ return summary;
2577
+ }
2578
+
2579
+ // src/tools/archive-project.ts
2580
+ var archiveProjectInputSchema = z.object({
2581
+ id: z.string().uuid()
2582
+ });
2583
+ async function archiveProjectHandler(ctx, input) {
2584
+ const existing = await ctx.repos.project.findById(input.id);
2585
+ if (!existing) {
2586
+ throw new McpToolError(`Project not found: ${input.id}`);
2587
+ }
2588
+ const archived = await ctx.repos.project.archive(input.id);
2589
+ return { project: projectSummaryFrom(archived) };
2590
+ }
2591
+
2592
+ // src/format/routine-summary.ts
2593
+ function routineSummaryFrom(routine) {
2594
+ const summary = {
2595
+ id: routine.id,
2596
+ title: routine.title,
2597
+ estimated_seconds: routine.estimatedSeconds,
2598
+ tag_ids: routine.tagIds,
2599
+ frequency: routine.frequency,
2600
+ auto_expand: routine.autoExpand,
2601
+ status: routine.status,
2602
+ created_at: routine.createdAt.toISOString(),
2603
+ updated_at: routine.updatedAt.toISOString()
2604
+ };
2605
+ if (routine.description !== void 0) summary.description = routine.description;
2606
+ if (routine.projectId !== void 0) summary.project_id = routine.projectId;
2607
+ if (routine.contextId !== void 0) summary.context_id = routine.contextId;
2608
+ if (routine.defaultScheduledStartTime !== void 0) {
2609
+ summary.default_scheduled_start_time = routine.defaultScheduledStartTime;
2610
+ }
2611
+ if (routine.startDate !== void 0) summary.start_date = routine.startDate;
2612
+ if (routine.endDate !== void 0) summary.end_date = routine.endDate;
2613
+ return summary;
2614
+ }
2615
+
2616
+ // src/tools/archive-routine.ts
2617
+ var archiveRoutineInputSchema = z.object({
2618
+ id: z.string().uuid()
2619
+ });
2620
+ async function archiveRoutineHandler(ctx, input) {
2621
+ const existing = await ctx.repos.routine.findById(input.id);
2622
+ if (!existing) {
2623
+ throw new McpToolError(`Routine not found: ${input.id}`);
2624
+ }
2625
+ const archived = await ctx.repos.routine.archive(input.id);
2626
+ return { routine: routineSummaryFrom(archived) };
2627
+ }
2628
+
2629
+ // src/format/tag-summary.ts
2630
+ function tagSummaryFrom(tag) {
2631
+ const summary = {
2632
+ id: tag.id,
2633
+ name: tag.name,
2634
+ status: tag.status,
2635
+ created_at: tag.createdAt.toISOString(),
2636
+ updated_at: tag.updatedAt.toISOString()
2637
+ };
2638
+ if (tag.description !== void 0) summary.description = tag.description;
2639
+ if (tag.color !== void 0) summary.color = tag.color;
2640
+ return summary;
2641
+ }
2642
+
2643
+ // src/tools/archive-tag.ts
2644
+ var archiveTagInputSchema = z.object({
2645
+ id: z.string().uuid()
2646
+ });
2647
+ async function archiveTagHandler(ctx, input) {
2648
+ const existing = await ctx.repos.tag.findById(input.id);
2649
+ if (!existing) {
2650
+ throw new McpToolError(`Tag not found: ${input.id}`);
2651
+ }
2652
+ const archived = await ctx.repos.tag.archive(input.id);
2653
+ return { tag: tagSummaryFrom(archived) };
2654
+ }
2655
+
2656
+ // src/format/time-block-summary.ts
2657
+ function timeBlockSummaryFrom(timeBlock) {
2658
+ const summary = {
2659
+ id: timeBlock.id,
2660
+ name: timeBlock.name,
2661
+ start_time: timeBlock.startTime,
2662
+ end_time: timeBlock.endTime,
2663
+ weekdays: timeBlock.weekdays,
2664
+ status: timeBlock.status,
2665
+ created_at: timeBlock.createdAt.toISOString(),
2666
+ updated_at: timeBlock.updatedAt.toISOString()
2667
+ };
2668
+ if (timeBlock.defaultProjectId !== void 0) {
2669
+ summary.default_project_id = timeBlock.defaultProjectId;
2670
+ }
2671
+ if (timeBlock.defaultContextId !== void 0) {
2672
+ summary.default_context_id = timeBlock.defaultContextId;
2673
+ }
2674
+ if (timeBlock.defaultTagIds !== void 0) summary.default_tag_ids = timeBlock.defaultTagIds;
2675
+ if (timeBlock.color !== void 0) summary.color = timeBlock.color;
2676
+ if (timeBlock.startDate !== void 0) summary.start_date = timeBlock.startDate;
2677
+ if (timeBlock.endDate !== void 0) summary.end_date = timeBlock.endDate;
2678
+ return summary;
2679
+ }
2680
+
2681
+ // src/tools/archive-time-block.ts
2682
+ var archiveTimeBlockInputSchema = z.object({
2683
+ id: z.string().uuid()
2684
+ });
2685
+ async function archiveTimeBlockHandler(ctx, input) {
2686
+ const existing = await ctx.repos.timeBlock.findById(input.id);
2687
+ if (!existing) {
2688
+ throw new McpToolError(`TimeBlock not found: ${input.id}`);
2689
+ }
2690
+ const archived = await ctx.repos.timeBlock.archive(input.id);
2691
+ return { time_block: timeBlockSummaryFrom(archived) };
2692
+ }
2693
+ var createContextInputSchema = z.object({
2694
+ name: z.string().min(1, "name must not be empty"),
2695
+ description: z.string().optional(),
2696
+ icon: z.string().optional().describe("\u7D75\u6587\u5B57\u307E\u305F\u306F\u30A2\u30A4\u30B3\u30F3\u540D (\u4F8B: \u{1F3E0} / mdi-home)"),
2697
+ color: z.string().optional().describe("UI \u8272 (\u4F8B: #ff6b6b)")
2698
+ });
2699
+ async function createContextHandler(ctx, input) {
2700
+ const createInput = {
2701
+ name: input.name,
2702
+ status: "active"
2703
+ };
2704
+ if (input.description !== void 0) createInput.description = input.description;
2705
+ if (input.icon !== void 0) createInput.icon = input.icon;
2706
+ if (input.color !== void 0) createInput.color = input.color;
2707
+ const context = await ctx.repos.context.create(createInput);
2708
+ return { context: contextSummaryFrom(context) };
2709
+ }
2710
+ var createProjectInputSchema = z.object({
2711
+ name: z.string().min(1, "name must not be empty"),
2712
+ description: z.string().optional(),
2713
+ color: z.string().optional().describe("UI \u8272 (\u4F8B: #ff6b6b)"),
2714
+ parent_project_id: z.string().uuid().optional(),
2715
+ goal_id: z.string().uuid().optional(),
2716
+ started_at: z.string().datetime().optional().describe("ISO 8601 datetime"),
2717
+ target_end_date: z.string().datetime().optional().describe("ISO 8601 datetime")
2718
+ });
2719
+ async function createProjectHandler(ctx, input) {
2720
+ if (input.parent_project_id) {
2721
+ const parent = await ctx.repos.project.findById(input.parent_project_id);
2722
+ if (!parent) {
2723
+ throw new McpToolError(`Parent project not found: ${input.parent_project_id}`);
2724
+ }
2725
+ }
2726
+ const createInput = {
2727
+ name: input.name,
2728
+ status: "active"
2729
+ };
2730
+ if (input.description !== void 0) createInput.description = input.description;
2731
+ if (input.color !== void 0) createInput.color = input.color;
2732
+ if (input.parent_project_id !== void 0) createInput.parentProjectId = input.parent_project_id;
2733
+ if (input.goal_id !== void 0) createInput.goalId = input.goal_id;
2734
+ if (input.started_at !== void 0) createInput.startedAt = new Date(input.started_at);
2735
+ if (input.target_end_date !== void 0) {
2736
+ createInput.targetEndDate = new Date(input.target_end_date);
2737
+ }
2738
+ const project = await ctx.repos.project.create(createInput);
2739
+ return { project: projectSummaryFrom(project) };
2740
+ }
2741
+ var frequencySchema = z.discriminatedUnion("type", [
2742
+ z.object({ type: z.literal("daily") }),
2743
+ z.object({
2744
+ type: z.literal("weekly"),
2745
+ weekdays: z.array(z.number().int().min(0).max(6)).min(1, "at least 1 weekday")
2746
+ }),
2747
+ z.object({
2748
+ type: z.literal("monthly"),
2749
+ days: z.array(z.number().int().min(1).max(31)).min(1, "at least 1 day")
2750
+ }),
2751
+ z.object({ type: z.literal("custom"), cron: z.string().min(1) })
2752
+ ]);
2753
+ var createRoutineInputSchema = z.object({
2754
+ title: z.string().min(1, "title must not be empty"),
2755
+ description: z.string().optional(),
2756
+ estimated_seconds: z.number().int().positive(),
2757
+ frequency: frequencySchema,
2758
+ project_id: z.string().uuid().optional(),
2759
+ context_id: z.string().uuid().optional(),
2760
+ tag_ids: z.array(z.string().uuid()).optional(),
2761
+ default_scheduled_start_time: z.string().regex(/^([01]\d|2[0-3]):[0-5]\d$/, "must be HH:MM").optional(),
2762
+ auto_expand: z.boolean().optional().describe("true \u306A\u3089 Today \u3092\u958B\u3044\u305F\u6642\u306B\u81EA\u52D5\u5C55\u958B (default true)"),
2763
+ start_date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional().describe("\u3053\u306E\u65E5\u4ED8\u4EE5\u964D\u306B\u6709\u52B9\u3002YYYY-MM-DD"),
2764
+ end_date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional()
2765
+ });
2766
+ async function createRoutineHandler(ctx, input) {
2767
+ if (input.project_id) {
2768
+ const p = await ctx.repos.project.findById(input.project_id);
2769
+ if (!p) throw new McpToolError(`Project not found: ${input.project_id}`);
2770
+ }
2771
+ if (input.context_id) {
2772
+ const c = await ctx.repos.context.findById(input.context_id);
2773
+ if (!c) throw new McpToolError(`Context not found: ${input.context_id}`);
2774
+ }
2775
+ if (input.tag_ids && input.tag_ids.length > 0) {
2776
+ const checked = await Promise.all(
2777
+ input.tag_ids.map(async (id) => ({
2778
+ id,
2779
+ exists: await ctx.repos.tag.findById(id) !== null
2780
+ }))
2781
+ );
2782
+ const missing = checked.filter((c) => !c.exists).map((c) => c.id);
2783
+ if (missing.length > 0) throw new McpToolError(`Tag(s) not found: ${missing.join(", ")}`);
2784
+ }
2785
+ const createInput = {
2786
+ title: input.title,
2787
+ estimatedSeconds: input.estimated_seconds,
2788
+ tagIds: input.tag_ids ?? [],
2789
+ frequency: input.frequency,
2790
+ autoExpand: input.auto_expand ?? true,
2791
+ status: "active"
2792
+ };
2793
+ if (input.description !== void 0) createInput.description = input.description;
2794
+ if (input.project_id !== void 0) createInput.projectId = input.project_id;
2795
+ if (input.context_id !== void 0) createInput.contextId = input.context_id;
2796
+ if (input.default_scheduled_start_time !== void 0) {
2797
+ createInput.defaultScheduledStartTime = input.default_scheduled_start_time;
2798
+ }
2799
+ if (input.start_date !== void 0) createInput.startDate = input.start_date;
2800
+ if (input.end_date !== void 0) createInput.endDate = input.end_date;
2801
+ const routine = await ctx.repos.routine.create(createInput);
2802
+ return { routine: routineSummaryFrom(routine) };
2803
+ }
2804
+ var createTagInputSchema = z.object({
2805
+ name: z.string().min(1, "name must not be empty"),
2806
+ description: z.string().optional(),
2807
+ color: z.string().optional().describe("UI \u8272 (\u4F8B: #ff6b6b)")
2808
+ });
2809
+ async function createTagHandler(ctx, input) {
2810
+ const createInput = {
2811
+ name: input.name,
2812
+ status: "active"
2813
+ };
2814
+ if (input.description !== void 0) createInput.description = input.description;
2815
+ if (input.color !== void 0) createInput.color = input.color;
2816
+ const tag = await ctx.repos.tag.create(createInput);
2817
+ return { tag: tagSummaryFrom(tag) };
2818
+ }
2819
+ var HHMM = /^([01]\d|2[0-3]):[0-5]\d$/;
2820
+ var createTimeBlockInputSchema = z.object({
2821
+ name: z.string().min(1, "name must not be empty"),
2822
+ start_time: z.string().regex(HHMM, "start_time must be HH:MM"),
2823
+ end_time: z.string().regex(HHMM, "end_time must be HH:MM"),
2824
+ weekdays: z.array(z.number().int().min(0).max(6)).min(1, "at least 1 weekday"),
2825
+ default_project_id: z.string().uuid().optional(),
2826
+ default_context_id: z.string().uuid().optional(),
2827
+ default_tag_ids: z.array(z.string().uuid()).optional(),
2828
+ color: z.string().optional(),
2829
+ start_date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional(),
2830
+ end_date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional()
2831
+ });
2832
+ async function createTimeBlockHandler(ctx, input) {
2833
+ if (input.default_project_id) {
2834
+ const p = await ctx.repos.project.findById(input.default_project_id);
2835
+ if (!p) throw new McpToolError(`Project not found: ${input.default_project_id}`);
2836
+ }
2837
+ if (input.default_context_id) {
2838
+ const c = await ctx.repos.context.findById(input.default_context_id);
2839
+ if (!c) throw new McpToolError(`Context not found: ${input.default_context_id}`);
2840
+ }
2841
+ if (input.default_tag_ids && input.default_tag_ids.length > 0) {
2842
+ const checked = await Promise.all(
2843
+ input.default_tag_ids.map(async (id) => ({
2844
+ id,
2845
+ exists: await ctx.repos.tag.findById(id) !== null
2846
+ }))
2847
+ );
2848
+ const missing = checked.filter((c) => !c.exists).map((c) => c.id);
2849
+ if (missing.length > 0) throw new McpToolError(`Tag(s) not found: ${missing.join(", ")}`);
2850
+ }
2851
+ const createInput = {
2852
+ name: input.name,
2853
+ startTime: input.start_time,
2854
+ endTime: input.end_time,
2855
+ weekdays: input.weekdays,
2856
+ status: "active"
2857
+ };
2858
+ if (input.default_project_id !== void 0) {
2859
+ createInput.defaultProjectId = input.default_project_id;
2860
+ }
2861
+ if (input.default_context_id !== void 0) {
2862
+ createInput.defaultContextId = input.default_context_id;
2863
+ }
2864
+ if (input.default_tag_ids !== void 0) createInput.defaultTagIds = input.default_tag_ids;
2865
+ if (input.color !== void 0) createInput.color = input.color;
2866
+ if (input.start_date !== void 0) createInput.startDate = input.start_date;
2867
+ if (input.end_date !== void 0) createInput.endDate = input.end_date;
2868
+ const block = await ctx.repos.timeBlock.create(createInput);
2869
+ return { time_block: timeBlockSummaryFrom(block) };
2870
+ }
2871
+ var deleteContextInputSchema = z.object({
2872
+ id: z.string().uuid()
2873
+ });
2874
+ async function deleteContextHandler(ctx, input) {
2875
+ const existing = await ctx.repos.context.findById(input.id);
2876
+ if (!existing) {
2877
+ throw new McpToolError(`Context not found: ${input.id}`);
2878
+ }
2879
+ await ctx.repos.context.delete(input.id);
2880
+ return { id: input.id, deleted: true };
2881
+ }
2882
+ var deleteProjectInputSchema = z.object({
2883
+ id: z.string().uuid()
2884
+ });
2885
+ async function deleteProjectHandler(ctx, input) {
2886
+ const existing = await ctx.repos.project.findById(input.id);
2887
+ if (!existing) {
2888
+ throw new McpToolError(`Project not found: ${input.id}`);
2889
+ }
2890
+ await ctx.repos.project.delete(input.id);
2891
+ return { id: input.id, deleted: true };
2892
+ }
2893
+ var deleteRoutineInputSchema = z.object({
2894
+ id: z.string().uuid()
2895
+ });
2896
+ async function deleteRoutineHandler(ctx, input) {
2897
+ const existing = await ctx.repos.routine.findById(input.id);
2898
+ if (!existing) {
2899
+ throw new McpToolError(`Routine not found: ${input.id}`);
2900
+ }
2901
+ await ctx.repos.routine.delete(input.id);
2902
+ return { id: input.id, deleted: true };
2903
+ }
2904
+ var deleteTagInputSchema = z.object({
2905
+ id: z.string().uuid()
2906
+ });
2907
+ async function deleteTagHandler(ctx, input) {
2908
+ const existing = await ctx.repos.tag.findById(input.id);
2909
+ if (!existing) {
2910
+ throw new McpToolError(`Tag not found: ${input.id}`);
2911
+ }
2912
+ await ctx.repos.tag.delete(input.id);
2913
+ return { id: input.id, deleted: true };
2914
+ }
2915
+ var deleteTaskInputSchema = z.object({
2916
+ id: z.string().uuid({ message: "id must be a UUID" }),
2917
+ hard_delete: z.boolean().optional().describe(
2918
+ "true \u306A\u3089 routine_expansions \u306B skip \u8A18\u9332\u305B\u305A\u7269\u7406\u524A\u9664\u306E\u307F\u3002Routine \u7531\u6765\u30BF\u30B9\u30AF\u3092\u5B8C\u5168\u30EA\u30BB\u30C3\u30C8\u3057\u305F\u3044\u4E0A\u7D1A\u8005\u5411\u3051"
2919
+ )
2920
+ });
2921
+ async function deleteTaskHandler(ctx, input) {
2922
+ const existing = await ctx.repos.task.findById(input.id);
2923
+ if (!existing) {
2924
+ throw new McpToolError(`Task not found: ${input.id}`);
2925
+ }
2926
+ if (input.hard_delete === true) {
2927
+ await ctx.repos.task.delete(input.id);
2928
+ } else {
2929
+ await ctx.repos.task.deleteWithRoutineSkip(input.id, todayDateString());
2930
+ }
2931
+ return { id: input.id, deleted: true };
2932
+ }
2933
+ var deleteTimeBlockInputSchema = z.object({
2934
+ id: z.string().uuid()
2935
+ });
2936
+ async function deleteTimeBlockHandler(ctx, input) {
2937
+ const existing = await ctx.repos.timeBlock.findById(input.id);
2938
+ if (!existing) {
2939
+ throw new McpToolError(`TimeBlock not found: ${input.id}`);
2940
+ }
2941
+ await ctx.repos.timeBlock.delete(input.id);
2942
+ return { id: input.id, deleted: true };
2943
+ }
2061
2944
  var doneTaskInputSchema = z.object({
2062
2945
  id: z.string().uuid({ message: "id must be a UUID" })
2063
2946
  });
@@ -2069,6 +2952,127 @@ async function doneTaskHandler(ctx, input) {
2069
2952
  const updated = await completeTask(ctx.repos.task, input.id);
2070
2953
  return { task: taskSummaryFrom(updated) };
2071
2954
  }
2955
+ var interruptTaskInputSchema = z.object({
2956
+ target_task_id: z.string().uuid().describe("\u5272\u308A\u8FBC\u307F\u5BFE\u8C61 (running \u4E2D) \u306E\u30BF\u30B9\u30AF id"),
2957
+ title: z.string().min(1, "interruption title must not be empty"),
2958
+ estimated_seconds: z.number().int().positive(),
2959
+ description: z.string().optional(),
2960
+ project_id: z.string().uuid().optional(),
2961
+ context_id: z.string().uuid().optional(),
2962
+ tag_ids: z.array(z.string().uuid()).optional()
2963
+ });
2964
+ async function interruptTaskHandler(ctx, input) {
2965
+ if (input.project_id) {
2966
+ const p = await ctx.repos.project.findById(input.project_id);
2967
+ if (!p) throw new McpToolError(`Project not found: ${input.project_id}`);
2968
+ }
2969
+ if (input.context_id) {
2970
+ const c = await ctx.repos.context.findById(input.context_id);
2971
+ if (!c) throw new McpToolError(`Context not found: ${input.context_id}`);
2972
+ }
2973
+ if (input.tag_ids && input.tag_ids.length > 0) {
2974
+ const checked = await Promise.all(
2975
+ input.tag_ids.map(async (id) => ({
2976
+ id,
2977
+ exists: await ctx.repos.tag.findById(id) !== null
2978
+ }))
2979
+ );
2980
+ const missing = checked.filter((c) => !c.exists).map((c) => c.id);
2981
+ if (missing.length > 0) throw new McpToolError(`Tag(s) not found: ${missing.join(", ")}`);
2982
+ }
2983
+ const result = await wrapDomainErrors(
2984
+ () => interruptTaskWithAudit(ctx.repos.task, ctx.repos.taskNote, {
2985
+ targetTaskId: input.target_task_id,
2986
+ interruption: {
2987
+ title: input.title,
2988
+ estimatedSeconds: input.estimated_seconds,
2989
+ ...input.description !== void 0 && { description: input.description },
2990
+ ...input.project_id !== void 0 && { projectId: input.project_id },
2991
+ ...input.context_id !== void 0 && { contextId: input.context_id },
2992
+ ...input.tag_ids !== void 0 && { tagIds: input.tag_ids }
2993
+ }
2994
+ })
2995
+ );
2996
+ return {
2997
+ finished_original: taskSummaryFrom(result.finishedOriginal),
2998
+ interruption: taskSummaryFrom(result.interruption),
2999
+ continuation: taskSummaryFrom(result.continuation)
3000
+ };
3001
+ }
3002
+ var DEFAULT_LIMIT = 100;
3003
+ var listContextsInputSchema = z.object({
3004
+ status: z.enum(["active", "archived"]).optional().describe("\u7701\u7565\u6642\u306F active \u306E\u307F"),
3005
+ limit: z.number().int().positive().max(1e3).optional().describe(`\u8FD4\u5374\u4EF6\u6570\u306E\u4E0A\u9650 (default ${DEFAULT_LIMIT})`)
3006
+ });
3007
+ async function listContextsHandler(ctx, input) {
3008
+ const limit = input.limit ?? DEFAULT_LIMIT;
3009
+ const filter = input.status === void 0 ? { status: "active" } : { status: input.status };
3010
+ const all = await ctx.repos.context.findAll(filter);
3011
+ const truncated = all.length > limit;
3012
+ const contexts = (truncated ? all.slice(0, limit) : all).map(contextSummaryFrom);
3013
+ return { contexts, total: all.length, truncated };
3014
+ }
3015
+ var DEFAULT_LIMIT2 = 100;
3016
+ var listProjectsInputSchema = z.object({
3017
+ status: z.enum(["active", "archived"]).optional().describe('\u7701\u7565\u6642\u306F active \u306E\u307F\u3002"archived" \u3067 archived \u306E\u307F\u3001\u4E21\u65B9\u304C\u8981\u308B\u306A\u3089 2 \u56DE\u547C\u3073\u51FA\u3059'),
3018
+ limit: z.number().int().positive().max(1e3).optional().describe(`\u8FD4\u5374\u4EF6\u6570\u306E\u4E0A\u9650 (default ${DEFAULT_LIMIT2})`)
3019
+ });
3020
+ async function listProjectsHandler(ctx, input) {
3021
+ const limit = input.limit ?? DEFAULT_LIMIT2;
3022
+ const filter = input.status === void 0 ? { status: "active" } : { status: input.status };
3023
+ const all = await ctx.repos.project.findAll(filter);
3024
+ const truncated = all.length > limit;
3025
+ const projects = (truncated ? all.slice(0, limit) : all).map(projectSummaryFrom);
3026
+ return { projects, total: all.length, truncated };
3027
+ }
3028
+ var DEFAULT_DAYS = 90;
3029
+ var DEFAULT_LIMIT3 = 100;
3030
+ var listRecentTasksInputSchema = z.object({
3031
+ days: z.number().int().positive().max(365).optional().describe(`\u4ECA\u65E5\u304B\u3089\u9061\u308B\u65E5\u6570 (default ${DEFAULT_DAYS})`),
3032
+ query: z.string().optional().describe("\u6307\u5B9A\u3059\u308B\u3068 fuzzy match \u3067\u985E\u4F3C\u30BF\u30A4\u30C8\u30EB\u3092\u4E0A\u4F4D\u306B\u8FD4\u3059\u3002\u3072\u3089\u304C\u306A\u2194\u30AB\u30BF\u30AB\u30CA\u6B63\u898F\u5316\u8FBC\u307F"),
3033
+ limit: z.number().int().positive().max(1e3).optional().describe(`\u8FD4\u5374\u4EF6\u6570\u306E\u4E0A\u9650 (default ${DEFAULT_LIMIT3})`)
3034
+ });
3035
+ async function listRecentTasksHandler(ctx, input) {
3036
+ const days = input.days ?? DEFAULT_DAYS;
3037
+ const limit = input.limit ?? DEFAULT_LIMIT3;
3038
+ const today = todayDateString();
3039
+ const fromDate = (() => {
3040
+ const d = fromDateString(today);
3041
+ d.setDate(d.getDate() - days);
3042
+ return toDateString(d);
3043
+ })();
3044
+ const all = await ctx.repos.task.findFromDate(fromDate);
3045
+ const ordered = input.query ? findSimilarTasks(input.query, all, limit) : [...all].sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime());
3046
+ const truncated = ordered.length > limit;
3047
+ const tasks = (truncated ? ordered.slice(0, limit) : ordered).map(taskSummaryFrom);
3048
+ return { tasks, total: ordered.length, truncated };
3049
+ }
3050
+ var DEFAULT_LIMIT4 = 100;
3051
+ var listRoutinesInputSchema = z.object({
3052
+ status: z.enum(["active", "paused", "archived"]).optional().describe('\u7701\u7565\u6642\u306F active \u306E\u307F\u3002"paused" \u306F\u4E00\u6642\u505C\u6B62\u4E2D (\u5C55\u958B\u505C\u6B62), "archived" \u306F\u5B8C\u5168\u505C\u6B62'),
3053
+ limit: z.number().int().positive().max(1e3).optional().describe(`\u8FD4\u5374\u4EF6\u6570\u306E\u4E0A\u9650 (default ${DEFAULT_LIMIT4})`)
3054
+ });
3055
+ async function listRoutinesHandler(ctx, input) {
3056
+ const limit = input.limit ?? DEFAULT_LIMIT4;
3057
+ const filter = input.status === void 0 ? { status: "active" } : { status: input.status };
3058
+ const all = await ctx.repos.routine.findAll(filter);
3059
+ const truncated = all.length > limit;
3060
+ const routines = (truncated ? all.slice(0, limit) : all).map(routineSummaryFrom);
3061
+ return { routines, total: all.length, truncated };
3062
+ }
3063
+ var DEFAULT_LIMIT5 = 100;
3064
+ var listTagsInputSchema = z.object({
3065
+ status: z.enum(["active", "archived"]).optional().describe("\u7701\u7565\u6642\u306F active \u306E\u307F"),
3066
+ limit: z.number().int().positive().max(1e3).optional().describe(`\u8FD4\u5374\u4EF6\u6570\u306E\u4E0A\u9650 (default ${DEFAULT_LIMIT5})`)
3067
+ });
3068
+ async function listTagsHandler(ctx, input) {
3069
+ const limit = input.limit ?? DEFAULT_LIMIT5;
3070
+ const filter = input.status === void 0 ? { status: "active" } : { status: input.status };
3071
+ const all = await ctx.repos.tag.findAll(filter);
3072
+ const truncated = all.length > limit;
3073
+ const tags = (truncated ? all.slice(0, limit) : all).map(tagSummaryFrom);
3074
+ return { tags, total: all.length, truncated };
3075
+ }
2072
3076
  var listTasksInputSchema = z.object({
2073
3077
  date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "date must be YYYY-MM-DD").optional().describe("\u5BFE\u8C61\u65E5 (YYYY-MM-DD)\u3002\u7701\u7565\u6642\u306F\u30B5\u30FC\u30D0\u30FC\u306E\u4ECA\u65E5 (Asia/Tokyo) \u3092\u4F7F\u3046")
2074
3078
  });
@@ -2077,6 +3081,19 @@ async function listTasksHandler(ctx, input) {
2077
3081
  const tasks = await ctx.repos.task.findByDate(date);
2078
3082
  return { date, tasks: tasks.map(taskSummaryFrom) };
2079
3083
  }
3084
+ var DEFAULT_LIMIT6 = 100;
3085
+ var listTimeBlocksInputSchema = z.object({
3086
+ status: z.enum(["active", "archived"]).optional().describe("\u7701\u7565\u6642\u306F active \u306E\u307F"),
3087
+ limit: z.number().int().positive().max(1e3).optional().describe(`\u8FD4\u5374\u4EF6\u6570\u306E\u4E0A\u9650 (default ${DEFAULT_LIMIT6})`)
3088
+ });
3089
+ async function listTimeBlocksHandler(ctx, input) {
3090
+ const limit = input.limit ?? DEFAULT_LIMIT6;
3091
+ const filter = input.status === void 0 ? { status: "active" } : { status: input.status };
3092
+ const all = await ctx.repos.timeBlock.findAll(filter);
3093
+ const truncated = all.length > limit;
3094
+ const blocks = (truncated ? all.slice(0, limit) : all).map(timeBlockSummaryFrom);
3095
+ return { time_blocks: blocks, total: all.length, truncated };
3096
+ }
2080
3097
  var pauseTaskInputSchema = z.object({
2081
3098
  id: z.string().uuid({ message: "id must be a UUID" })
2082
3099
  });
@@ -2088,6 +3105,68 @@ async function pauseTaskHandler(ctx, input) {
2088
3105
  const updated = await pauseTask(ctx.repos.task, input.id);
2089
3106
  return { task: taskSummaryFrom(updated) };
2090
3107
  }
3108
+ var reopenTaskInputSchema = z.object({
3109
+ id: z.string().uuid({ message: "id must be a UUID" })
3110
+ });
3111
+ async function reopenTaskHandler(ctx, input) {
3112
+ const existing = await ctx.repos.task.findById(input.id);
3113
+ if (!existing) {
3114
+ throw new McpToolError(`Task not found: ${input.id}`);
3115
+ }
3116
+ const updated = await reopenTask(ctx.repos.task, input.id);
3117
+ return { task: taskSummaryFrom(updated) };
3118
+ }
3119
+ var reorderTaskInputSchema = z.object({
3120
+ id: z.string().uuid(),
3121
+ order_index: z.number().int(),
3122
+ scheduled_start_time: z.union([z.string().regex(/^([01]\d|2[0-3]):[0-5]\d$/, "must be HH:MM"), z.null()]).optional().describe(
3123
+ "\u6307\u5B9A\u3059\u308B\u3068\u540C\u6642\u306B\u66F8\u304D\u63DB\u3048\u308B\u3002null=\u672A\u5272\u5F53\u30B0\u30EB\u30FC\u30D7\u306B\u79FB\u52D5\u3002HH:MM=\u8A72\u5F53\u30D6\u30ED\u30C3\u30AF\u306B\u79FB\u52D5\u3002\u7701\u7565=\u5909\u66F4\u306A\u3057"
3124
+ )
3125
+ });
3126
+ async function reorderTaskHandler(ctx, input) {
3127
+ const existing = await ctx.repos.task.findById(input.id);
3128
+ if (!existing) {
3129
+ throw new McpToolError(`Task not found: ${input.id}`);
3130
+ }
3131
+ const patch = { orderIndex: input.order_index };
3132
+ if (input.scheduled_start_time !== void 0) {
3133
+ patch.scheduledStartTime = input.scheduled_start_time === null ? void 0 : input.scheduled_start_time;
3134
+ }
3135
+ const updated = await ctx.repos.task.update(input.id, patch);
3136
+ return { task: taskSummaryFrom(updated) };
3137
+ }
3138
+ var setActualTimesInputSchema = z.object({
3139
+ id: z.string().uuid(),
3140
+ start_time: z.union([z.string().regex(/^([01]\d|2[0-3]):[0-5]\d$/, "must be HH:MM"), z.null()]).describe("HH:MM\u3002null \u306A\u3089\u5B9F\u7E3E\u958B\u59CB\u306A\u3057\u3002end_time \u306E\u307F\u6307\u5B9A\u306F\u30A8\u30E9\u30FC"),
3141
+ end_time: z.union([z.string().regex(/^([01]\d|2[0-3]):[0-5]\d$/, "must be HH:MM"), z.null()]).describe("HH:MM\u3002null \u306A\u3089\u672A\u5B8C\u4E86 (running \u6271\u3044)")
3142
+ });
3143
+ async function setActualTimesHandler(ctx, input) {
3144
+ const existing = await ctx.repos.task.findById(input.id);
3145
+ if (!existing) {
3146
+ throw new McpToolError(`Task not found: ${input.id}`);
3147
+ }
3148
+ const patch = await wrapDomainErrors(
3149
+ () => Promise.resolve(
3150
+ buildSetActualTimesPatch(existing, {
3151
+ startTime: input.start_time,
3152
+ endTime: input.end_time
3153
+ })
3154
+ )
3155
+ );
3156
+ const updated = await ctx.repos.task.update(input.id, patch);
3157
+ return { task: taskSummaryFrom(updated) };
3158
+ }
3159
+ var skipTaskInputSchema = z.object({
3160
+ id: z.string().uuid({ message: "id must be a UUID" })
3161
+ });
3162
+ async function skipTaskHandler(ctx, input) {
3163
+ const existing = await ctx.repos.task.findById(input.id);
3164
+ if (!existing) {
3165
+ throw new McpToolError(`Task not found: ${input.id}`);
3166
+ }
3167
+ const updated = await skipTask(ctx.repos.task, input.id);
3168
+ return { task: taskSummaryFrom(updated) };
3169
+ }
2091
3170
  var startTaskInputSchema = z.object({
2092
3171
  id: z.string().uuid({ message: "id must be a UUID" })
2093
3172
  });
@@ -2099,6 +3178,332 @@ async function startTaskHandler(ctx, input) {
2099
3178
  const updated = await startTask(ctx.repos.task, input.id);
2100
3179
  return { task: taskSummaryFrom(updated) };
2101
3180
  }
3181
+ var updateContextInputSchema = z.object({
3182
+ id: z.string().uuid(),
3183
+ name: z.string().min(1).optional(),
3184
+ description: z.union([z.string(), z.null()]).optional(),
3185
+ icon: z.union([z.string(), z.null()]).optional(),
3186
+ color: z.union([z.string(), z.null()]).optional(),
3187
+ status: z.enum(["active", "archived"]).optional().describe("archived \u5316\u306F archive_context tool \u63A8\u5968")
3188
+ });
3189
+ async function updateContextHandler(ctx, input) {
3190
+ const existing = await ctx.repos.context.findById(input.id);
3191
+ if (!existing) {
3192
+ throw new McpToolError(`Context not found: ${input.id}`);
3193
+ }
3194
+ const patch = {};
3195
+ if (input.name !== void 0) patch.name = input.name;
3196
+ if (input.description !== void 0) {
3197
+ patch.description = input.description === null ? void 0 : input.description;
3198
+ }
3199
+ if (input.icon !== void 0) {
3200
+ patch.icon = input.icon === null ? void 0 : input.icon;
3201
+ }
3202
+ if (input.color !== void 0) {
3203
+ patch.color = input.color === null ? void 0 : input.color;
3204
+ }
3205
+ if (input.status !== void 0) patch.status = input.status;
3206
+ const updated = await ctx.repos.context.update(input.id, patch);
3207
+ return { context: contextSummaryFrom(updated) };
3208
+ }
3209
+ var updateProjectInputSchema = z.object({
3210
+ id: z.string().uuid(),
3211
+ name: z.string().min(1).optional(),
3212
+ description: z.union([z.string(), z.null()]).optional(),
3213
+ color: z.union([z.string(), z.null()]).optional(),
3214
+ parent_project_id: z.union([z.string().uuid(), z.null()]).optional(),
3215
+ goal_id: z.union([z.string().uuid(), z.null()]).optional(),
3216
+ started_at: z.union([z.string().datetime(), z.null()]).optional(),
3217
+ target_end_date: z.union([z.string().datetime(), z.null()]).optional(),
3218
+ status: z.enum(["active", "archived"]).optional().describe("archived \u5316\u306F archive_project tool \u63A8\u5968 (\u4E21\u65B9\u3068\u3082 archived_at \u3092\u8A2D\u5B9A\u3059\u308B)")
3219
+ });
3220
+ async function updateProjectHandler(ctx, input) {
3221
+ const existing = await ctx.repos.project.findById(input.id);
3222
+ if (!existing) {
3223
+ throw new McpToolError(`Project not found: ${input.id}`);
3224
+ }
3225
+ if (input.parent_project_id !== void 0 && input.parent_project_id !== null) {
3226
+ const parent = await ctx.repos.project.findById(input.parent_project_id);
3227
+ if (!parent) {
3228
+ throw new McpToolError(`Parent project not found: ${input.parent_project_id}`);
3229
+ }
3230
+ if (parent.id === input.id) {
3231
+ throw new McpToolError("A project cannot be its own parent");
3232
+ }
3233
+ }
3234
+ const patch = {};
3235
+ if (input.name !== void 0) patch.name = input.name;
3236
+ if (input.description !== void 0) {
3237
+ patch.description = input.description === null ? void 0 : input.description;
3238
+ }
3239
+ if (input.color !== void 0) {
3240
+ patch.color = input.color === null ? void 0 : input.color;
3241
+ }
3242
+ if (input.parent_project_id !== void 0) {
3243
+ patch.parentProjectId = input.parent_project_id === null ? void 0 : input.parent_project_id;
3244
+ }
3245
+ if (input.goal_id !== void 0) {
3246
+ patch.goalId = input.goal_id === null ? void 0 : input.goal_id;
3247
+ }
3248
+ if (input.started_at !== void 0) {
3249
+ patch.startedAt = input.started_at === null ? void 0 : new Date(input.started_at);
3250
+ }
3251
+ if (input.target_end_date !== void 0) {
3252
+ patch.targetEndDate = input.target_end_date === null ? void 0 : new Date(input.target_end_date);
3253
+ }
3254
+ if (input.status !== void 0) patch.status = input.status;
3255
+ const updated = await ctx.repos.project.update(input.id, patch);
3256
+ return { project: projectSummaryFrom(updated) };
3257
+ }
3258
+ var frequencySchema2 = z.discriminatedUnion("type", [
3259
+ z.object({ type: z.literal("daily") }),
3260
+ z.object({
3261
+ type: z.literal("weekly"),
3262
+ weekdays: z.array(z.number().int().min(0).max(6)).min(1)
3263
+ }),
3264
+ z.object({
3265
+ type: z.literal("monthly"),
3266
+ days: z.array(z.number().int().min(1).max(31)).min(1)
3267
+ }),
3268
+ z.object({ type: z.literal("custom"), cron: z.string().min(1) })
3269
+ ]);
3270
+ var updateRoutineInputSchema = z.object({
3271
+ id: z.string().uuid(),
3272
+ title: z.string().min(1).optional(),
3273
+ description: z.union([z.string(), z.null()]).optional(),
3274
+ estimated_seconds: z.number().int().positive().optional(),
3275
+ frequency: frequencySchema2.optional(),
3276
+ project_id: z.union([z.string().uuid(), z.null()]).optional(),
3277
+ context_id: z.union([z.string().uuid(), z.null()]).optional(),
3278
+ tag_ids: z.array(z.string().uuid()).optional(),
3279
+ default_scheduled_start_time: z.union([z.string().regex(/^([01]\d|2[0-3]):[0-5]\d$/, "must be HH:MM"), z.null()]).optional(),
3280
+ auto_expand: z.boolean().optional(),
3281
+ start_date: z.union([z.string().regex(/^\d{4}-\d{2}-\d{2}$/), z.null()]).optional(),
3282
+ end_date: z.union([z.string().regex(/^\d{4}-\d{2}-\d{2}$/), z.null()]).optional(),
3283
+ status: z.enum(["active", "paused", "archived"]).optional()
3284
+ });
3285
+ async function updateRoutineHandler(ctx, input) {
3286
+ const existing = await ctx.repos.routine.findById(input.id);
3287
+ if (!existing) {
3288
+ throw new McpToolError(`Routine not found: ${input.id}`);
3289
+ }
3290
+ if (input.project_id !== void 0 && input.project_id !== null) {
3291
+ const p = await ctx.repos.project.findById(input.project_id);
3292
+ if (!p) throw new McpToolError(`Project not found: ${input.project_id}`);
3293
+ }
3294
+ if (input.context_id !== void 0 && input.context_id !== null) {
3295
+ const c = await ctx.repos.context.findById(input.context_id);
3296
+ if (!c) throw new McpToolError(`Context not found: ${input.context_id}`);
3297
+ }
3298
+ if (input.tag_ids !== void 0 && input.tag_ids.length > 0) {
3299
+ const checked = await Promise.all(
3300
+ input.tag_ids.map(async (id) => ({
3301
+ id,
3302
+ exists: await ctx.repos.tag.findById(id) !== null
3303
+ }))
3304
+ );
3305
+ const missing = checked.filter((c) => !c.exists).map((c) => c.id);
3306
+ if (missing.length > 0) throw new McpToolError(`Tag(s) not found: ${missing.join(", ")}`);
3307
+ }
3308
+ const patch = {};
3309
+ if (input.title !== void 0) patch.title = input.title;
3310
+ if (input.description !== void 0) {
3311
+ patch.description = input.description === null ? void 0 : input.description;
3312
+ }
3313
+ if (input.estimated_seconds !== void 0) patch.estimatedSeconds = input.estimated_seconds;
3314
+ if (input.frequency !== void 0) patch.frequency = input.frequency;
3315
+ if (input.project_id !== void 0) {
3316
+ patch.projectId = input.project_id === null ? void 0 : input.project_id;
3317
+ }
3318
+ if (input.context_id !== void 0) {
3319
+ patch.contextId = input.context_id === null ? void 0 : input.context_id;
3320
+ }
3321
+ if (input.tag_ids !== void 0) patch.tagIds = input.tag_ids;
3322
+ if (input.default_scheduled_start_time !== void 0) {
3323
+ patch.defaultScheduledStartTime = input.default_scheduled_start_time === null ? void 0 : input.default_scheduled_start_time;
3324
+ }
3325
+ if (input.auto_expand !== void 0) patch.autoExpand = input.auto_expand;
3326
+ if (input.start_date !== void 0) {
3327
+ patch.startDate = input.start_date === null ? void 0 : input.start_date;
3328
+ }
3329
+ if (input.end_date !== void 0) {
3330
+ patch.endDate = input.end_date === null ? void 0 : input.end_date;
3331
+ }
3332
+ if (input.status !== void 0) patch.status = input.status;
3333
+ const updated = await ctx.repos.routine.update(input.id, patch);
3334
+ const today = todayDateString();
3335
+ const futureTasks = await ctx.repos.task.findByRoutineIdFromDate(input.id, today);
3336
+ const targets = pickPropagationTargets(updated, futureTasks, today);
3337
+ if (targets.length > 0) {
3338
+ const { auditingTask } = ctx.repos;
3339
+ if (!auditingTask) {
3340
+ throw new McpToolError(
3341
+ "auditingTask is not configured. Routine propagation requires audit lookups to be enabled (createNodeAppContext sets this up automatically)."
3342
+ );
3343
+ }
3344
+ const taskPatch = buildRoutineTaskPatch(updated);
3345
+ await Promise.all(
3346
+ targets.map(
3347
+ (task) => auditingTask.updateWithCause(task.id, taskPatch, "routine-propagation")
3348
+ )
3349
+ );
3350
+ }
3351
+ return {
3352
+ routine: routineSummaryFrom(updated),
3353
+ propagated_task_count: targets.length
3354
+ };
3355
+ }
3356
+ var updateTagInputSchema = z.object({
3357
+ id: z.string().uuid(),
3358
+ name: z.string().min(1).optional(),
3359
+ description: z.union([z.string(), z.null()]).optional(),
3360
+ color: z.union([z.string(), z.null()]).optional(),
3361
+ status: z.enum(["active", "archived"]).optional().describe("archived \u5316\u306F archive_tag tool \u63A8\u5968")
3362
+ });
3363
+ async function updateTagHandler(ctx, input) {
3364
+ const existing = await ctx.repos.tag.findById(input.id);
3365
+ if (!existing) {
3366
+ throw new McpToolError(`Tag not found: ${input.id}`);
3367
+ }
3368
+ const patch = {};
3369
+ if (input.name !== void 0) patch.name = input.name;
3370
+ if (input.description !== void 0) {
3371
+ patch.description = input.description === null ? void 0 : input.description;
3372
+ }
3373
+ if (input.color !== void 0) {
3374
+ patch.color = input.color === null ? void 0 : input.color;
3375
+ }
3376
+ if (input.status !== void 0) patch.status = input.status;
3377
+ const updated = await ctx.repos.tag.update(input.id, patch);
3378
+ return { tag: tagSummaryFrom(updated) };
3379
+ }
3380
+ var updateTaskInputSchema = z.object({
3381
+ id: z.string().uuid({ message: "id must be a UUID" }),
3382
+ title: z.string().min(1).optional(),
3383
+ description: z.union([z.string(), z.null()]).optional(),
3384
+ estimated_seconds: z.number().int().positive().optional(),
3385
+ scheduled_start_time: z.union([z.string().regex(/^([01]\d|2[0-3]):[0-5]\d$/, "must be HH:MM"), z.null()]).optional(),
3386
+ project_id: z.union([z.string().uuid(), z.null()]).optional(),
3387
+ context_id: z.union([z.string().uuid(), z.null()]).optional(),
3388
+ tag_ids: z.array(z.string().uuid()).optional(),
3389
+ notify_at_scheduled_start: z.boolean().optional(),
3390
+ is_background: z.boolean().optional()
3391
+ });
3392
+ async function validateReferences(ctx, refs) {
3393
+ if (refs.projectId) {
3394
+ const project = await ctx.repos.project.findById(refs.projectId);
3395
+ if (!project) throw new McpToolError(`Project not found: ${refs.projectId}`);
3396
+ }
3397
+ if (refs.contextId) {
3398
+ const context = await ctx.repos.context.findById(refs.contextId);
3399
+ if (!context) throw new McpToolError(`Context not found: ${refs.contextId}`);
3400
+ }
3401
+ if (refs.tagIds && refs.tagIds.length > 0) {
3402
+ const checked = await Promise.all(
3403
+ refs.tagIds.map(async (id) => ({ id, exists: await ctx.repos.tag.findById(id) !== null }))
3404
+ );
3405
+ const missing = checked.filter((c) => !c.exists).map((c) => c.id);
3406
+ if (missing.length > 0) {
3407
+ throw new McpToolError(`Tag(s) not found: ${missing.join(", ")}`);
3408
+ }
3409
+ }
3410
+ }
3411
+ async function updateTaskHandler(ctx, input) {
3412
+ const existing = await ctx.repos.task.findById(input.id);
3413
+ if (!existing) {
3414
+ throw new McpToolError(`Task not found: ${input.id}`);
3415
+ }
3416
+ await validateReferences(ctx, {
3417
+ projectId: input.project_id ?? void 0,
3418
+ contextId: input.context_id ?? void 0,
3419
+ tagIds: input.tag_ids
3420
+ });
3421
+ const patch = {};
3422
+ if (input.title !== void 0) patch.title = input.title;
3423
+ if (input.description !== void 0) {
3424
+ patch.description = input.description === null ? void 0 : input.description;
3425
+ }
3426
+ if (input.estimated_seconds !== void 0) patch.estimatedSeconds = input.estimated_seconds;
3427
+ if (input.scheduled_start_time !== void 0) {
3428
+ patch.scheduledStartTime = input.scheduled_start_time === null ? void 0 : input.scheduled_start_time;
3429
+ }
3430
+ if (input.project_id !== void 0) {
3431
+ patch.projectId = input.project_id === null ? void 0 : input.project_id;
3432
+ }
3433
+ if (input.context_id !== void 0) {
3434
+ patch.contextId = input.context_id === null ? void 0 : input.context_id;
3435
+ }
3436
+ if (input.tag_ids !== void 0) patch.tagIds = input.tag_ids;
3437
+ if (input.notify_at_scheduled_start !== void 0) {
3438
+ patch.notifyAtScheduledStart = input.notify_at_scheduled_start;
3439
+ }
3440
+ if (input.is_background !== void 0) patch.isBackground = input.is_background;
3441
+ const updated = await ctx.repos.task.update(input.id, patch);
3442
+ return { task: taskSummaryFrom(updated) };
3443
+ }
3444
+ var HHMM2 = /^([01]\d|2[0-3]):[0-5]\d$/;
3445
+ var updateTimeBlockInputSchema = z.object({
3446
+ id: z.string().uuid(),
3447
+ name: z.string().min(1).optional(),
3448
+ start_time: z.string().regex(HHMM2, "start_time must be HH:MM").optional(),
3449
+ end_time: z.string().regex(HHMM2, "end_time must be HH:MM").optional(),
3450
+ weekdays: z.array(z.number().int().min(0).max(6)).min(1).optional(),
3451
+ default_project_id: z.union([z.string().uuid(), z.null()]).optional(),
3452
+ default_context_id: z.union([z.string().uuid(), z.null()]).optional(),
3453
+ default_tag_ids: z.array(z.string().uuid()).optional(),
3454
+ color: z.union([z.string(), z.null()]).optional(),
3455
+ start_date: z.union([z.string().regex(/^\d{4}-\d{2}-\d{2}$/), z.null()]).optional(),
3456
+ end_date: z.union([z.string().regex(/^\d{4}-\d{2}-\d{2}$/), z.null()]).optional(),
3457
+ status: z.enum(["active", "archived"]).optional()
3458
+ });
3459
+ async function updateTimeBlockHandler(ctx, input) {
3460
+ const existing = await ctx.repos.timeBlock.findById(input.id);
3461
+ if (!existing) {
3462
+ throw new McpToolError(`TimeBlock not found: ${input.id}`);
3463
+ }
3464
+ if (input.default_project_id !== void 0 && input.default_project_id !== null) {
3465
+ const p = await ctx.repos.project.findById(input.default_project_id);
3466
+ if (!p) throw new McpToolError(`Project not found: ${input.default_project_id}`);
3467
+ }
3468
+ if (input.default_context_id !== void 0 && input.default_context_id !== null) {
3469
+ const c = await ctx.repos.context.findById(input.default_context_id);
3470
+ if (!c) throw new McpToolError(`Context not found: ${input.default_context_id}`);
3471
+ }
3472
+ if (input.default_tag_ids !== void 0 && input.default_tag_ids.length > 0) {
3473
+ const checked = await Promise.all(
3474
+ input.default_tag_ids.map(async (id) => ({
3475
+ id,
3476
+ exists: await ctx.repos.tag.findById(id) !== null
3477
+ }))
3478
+ );
3479
+ const missing = checked.filter((c) => !c.exists).map((c) => c.id);
3480
+ if (missing.length > 0) throw new McpToolError(`Tag(s) not found: ${missing.join(", ")}`);
3481
+ }
3482
+ const patch = {};
3483
+ if (input.name !== void 0) patch.name = input.name;
3484
+ if (input.start_time !== void 0) patch.startTime = input.start_time;
3485
+ if (input.end_time !== void 0) patch.endTime = input.end_time;
3486
+ if (input.weekdays !== void 0) patch.weekdays = input.weekdays;
3487
+ if (input.default_project_id !== void 0) {
3488
+ patch.defaultProjectId = input.default_project_id === null ? void 0 : input.default_project_id;
3489
+ }
3490
+ if (input.default_context_id !== void 0) {
3491
+ patch.defaultContextId = input.default_context_id === null ? void 0 : input.default_context_id;
3492
+ }
3493
+ if (input.default_tag_ids !== void 0) patch.defaultTagIds = input.default_tag_ids;
3494
+ if (input.color !== void 0) {
3495
+ patch.color = input.color === null ? void 0 : input.color;
3496
+ }
3497
+ if (input.start_date !== void 0) {
3498
+ patch.startDate = input.start_date === null ? void 0 : input.start_date;
3499
+ }
3500
+ if (input.end_date !== void 0) {
3501
+ patch.endDate = input.end_date === null ? void 0 : input.end_date;
3502
+ }
3503
+ if (input.status !== void 0) patch.status = input.status;
3504
+ const updated = await ctx.repos.timeBlock.update(input.id, patch);
3505
+ return { time_block: timeBlockSummaryFrom(updated) };
3506
+ }
2102
3507
 
2103
3508
  // src/server.ts
2104
3509
  function createDefaultContextFactory() {
@@ -2144,11 +3549,29 @@ function createMcpServer(opts) {
2144
3549
  "add_task",
2145
3550
  {
2146
3551
  title: "Add task",
2147
- description: "\u30BF\u30B9\u30AF\u3092\u8FFD\u52A0\u3059\u308B\u3002\u65E5\u4ED8\u7701\u7565\u6642\u306F\u4ECA\u65E5\u3002estimated_seconds \u7701\u7565\u6642\u306F 1800 (30 \u5206)\u3002",
3552
+ description: "\u30BF\u30B9\u30AF\u3092\u8FFD\u52A0\u3059\u308B\u3002\u65E5\u4ED8\u7701\u7565\u6642\u306F\u4ECA\u65E5\u3002estimated_seconds \u7701\u7565\u6642\u306F 1800 (30 \u5206)\u3002project_id / context_id / tag_ids \u306F\u4E8B\u524D\u306B list_projects / list_contexts / list_tags \u3067\u53D6\u5F97\u3057\u305F UUID \u3092\u6E21\u3059\u3002",
2148
3553
  inputSchema: addTaskInputSchema.shape
2149
3554
  },
2150
3555
  async (input) => wrapHandler(ctxFactory, addTaskHandler, input)
2151
3556
  );
3557
+ server.registerTool(
3558
+ "update_task",
3559
+ {
3560
+ title: "Update task",
3561
+ description: "\u30BF\u30B9\u30AF\u306E\u30E6\u30FC\u30B6\u30FC\u7DE8\u96C6\u53EF\u80FD\u30D5\u30A3\u30FC\u30EB\u30C9\u3092\u90E8\u5206\u66F4\u65B0\u3059\u308B\u3002undefined \u306F\u5909\u66F4\u306A\u3057\u3001null \u306F\u660E\u793A\u30AF\u30EA\u30A2\u3002status / order_index / actual_start_time \u7B49\u306E\u30B7\u30B9\u30C6\u30E0\u30D5\u30A3\u30FC\u30EB\u30C9\u306F\u5225 tool (start/pause/done/skip/reopen/reorder) \u7D4C\u7531\u3067\u5909\u66F4\u3059\u308B\u3002tag_ids \u306F\u5168\u7F6E\u63DB\u3002",
3562
+ inputSchema: updateTaskInputSchema.shape
3563
+ },
3564
+ async (input) => wrapHandler(ctxFactory, updateTaskHandler, input)
3565
+ );
3566
+ server.registerTool(
3567
+ "delete_task",
3568
+ {
3569
+ title: "Delete task",
3570
+ description: "\u30BF\u30B9\u30AF\u3092\u524A\u9664\u3059\u308B\u3002Routine \u7531\u6765\u30BF\u30B9\u30AF\u3092\u4ECA\u65E5\u4EE5\u964D\u306B\u6D88\u3059\u5834\u5408\u306F routine_expansions \u306B skip \u8A18\u9332\u3059\u308B (default)\u3002hard_delete: true \u306A\u3089\u7269\u7406\u524A\u9664\u306E\u307F\u3067\u3001\u6B21\u306E reload \u3067\u518D\u5C55\u958B\u3055\u308C\u308B (\u4E0A\u7D1A\u8005\u5411\u3051)\u3002",
3571
+ inputSchema: deleteTaskInputSchema.shape
3572
+ },
3573
+ async (input) => wrapHandler(ctxFactory, deleteTaskHandler, input)
3574
+ );
2152
3575
  server.registerTool(
2153
3576
  "start_task",
2154
3577
  {
@@ -2176,12 +3599,300 @@ function createMcpServer(opts) {
2176
3599
  },
2177
3600
  async (input) => wrapHandler(ctxFactory, doneTaskHandler, input)
2178
3601
  );
3602
+ server.registerTool(
3603
+ "skip_task",
3604
+ {
3605
+ title: "Skip task",
3606
+ description: "\u30BF\u30B9\u30AF\u3092 skipped \u306B\u9077\u79FB\u3055\u305B\u308B (\u3084\u3089\u306A\u3044\u3053\u3068\u304C\u6C7A\u307E\u3063\u305F\u30BF\u30B9\u30AF\u7528)\u3002reopen_task \u3067 planned \u306B\u623B\u305B\u308B\u3002",
3607
+ inputSchema: skipTaskInputSchema.shape
3608
+ },
3609
+ async (input) => wrapHandler(ctxFactory, skipTaskHandler, input)
3610
+ );
3611
+ server.registerTool(
3612
+ "reopen_task",
3613
+ {
3614
+ title: "Reopen task",
3615
+ description: "completed / skipped \u306E\u30BF\u30B9\u30AF\u3092 planned \u306B\u623B\u3059\u3002planned/running/paused \u306E\u3068\u304D\u306F no-op\u3002",
3616
+ inputSchema: reopenTaskInputSchema.shape
3617
+ },
3618
+ async (input) => wrapHandler(ctxFactory, reopenTaskHandler, input)
3619
+ );
3620
+ server.registerTool(
3621
+ "list_projects",
3622
+ {
3623
+ title: "List projects",
3624
+ description: "\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u4E00\u89A7\u3092\u8FD4\u3059\u3002`add_task` / `update_task` \u306E `project_id` \u306B\u6E21\u3059 UUID \u3092\u53D6\u5F97\u3059\u308B\u305F\u3081\u306B\u4F7F\u3046\u3002\u65E2\u5B9A\u306F active \u306E\u307F\u3001`status: archived` \u3067 archived \u3082\u53D6\u5F97\u53EF\u80FD\u3002`limit` \u65E2\u5B9A 100\u3002",
3625
+ inputSchema: listProjectsInputSchema.shape
3626
+ },
3627
+ async (input) => wrapHandler(ctxFactory, listProjectsHandler, input)
3628
+ );
3629
+ server.registerTool(
3630
+ "list_tags",
3631
+ {
3632
+ title: "List tags",
3633
+ description: "\u30BF\u30B0\u4E00\u89A7\u3092\u8FD4\u3059\u3002`add_task` / `update_task` \u306E `tag_ids` \u306B\u6E21\u3059 UUID \u3092\u53D6\u5F97\u3059\u308B\u305F\u3081\u306B\u4F7F\u3046\u3002`limit` \u65E2\u5B9A 100\u3002",
3634
+ inputSchema: listTagsInputSchema.shape
3635
+ },
3636
+ async (input) => wrapHandler(ctxFactory, listTagsHandler, input)
3637
+ );
3638
+ server.registerTool(
3639
+ "list_contexts",
3640
+ {
3641
+ title: "List contexts",
3642
+ description: "\u30B3\u30F3\u30C6\u30AD\u30B9\u30C8 (\u4F5C\u696D\u74B0\u5883\u30BF\u30B0\u3002\u4F8B: home / office / mobile) \u4E00\u89A7\u3092\u8FD4\u3059\u3002`add_task` / `update_task` \u306E `context_id` \u306B\u6E21\u3059 UUID \u3092\u53D6\u5F97\u3059\u308B\u305F\u3081\u306B\u4F7F\u3046\u3002`limit` \u65E2\u5B9A 100\u3002",
3643
+ inputSchema: listContextsInputSchema.shape
3644
+ },
3645
+ async (input) => wrapHandler(ctxFactory, listContextsHandler, input)
3646
+ );
3647
+ server.registerTool(
3648
+ "list_routines",
3649
+ {
3650
+ title: "List routines",
3651
+ description: '\u7E70\u308A\u8FD4\u3057\u30BF\u30B9\u30AF\u306E\u30C6\u30F3\u30D7\u30EC\u30FC\u30C8 (\u30EB\u30FC\u30C1\u30F3) \u4E00\u89A7\u3092\u8FD4\u3059\u3002`frequency` \u306F discriminated union (`{type: "daily"}` / `{type: "weekly", weekdays: [1,3,5]}` \u7B49) \u306E\u307E\u307E\u3002`limit` \u65E2\u5B9A 100\u3002',
3652
+ inputSchema: listRoutinesInputSchema.shape
3653
+ },
3654
+ async (input) => wrapHandler(ctxFactory, listRoutinesHandler, input)
3655
+ );
3656
+ server.registerTool(
3657
+ "list_time_blocks",
3658
+ {
3659
+ title: "List time blocks",
3660
+ description: "\u30BF\u30A4\u30E0\u30D6\u30ED\u30C3\u30AF (\u6642\u9593\u5E2F\u30C6\u30F3\u30D7\u30EC\u30FC\u30C8\u3002\u4F8B: \u671D\u306E\u96C6\u4E2D\u6642\u9593 09:00-12:00) \u4E00\u89A7\u3092\u8FD4\u3059\u3002`limit` \u65E2\u5B9A 100\u3002",
3661
+ inputSchema: listTimeBlocksInputSchema.shape
3662
+ },
3663
+ async (input) => wrapHandler(ctxFactory, listTimeBlocksHandler, input)
3664
+ );
3665
+ server.registerTool(
3666
+ "list_recent_tasks",
3667
+ {
3668
+ title: "List recent tasks",
3669
+ description: "\u904E\u53BB N \u65E5 (\u65E2\u5B9A 90) \u306E\u30BF\u30B9\u30AF\u3092\u8FD4\u3059\u3002`query` \u3092\u6307\u5B9A\u3059\u308B\u3068 fuzzy match \u3067\u985E\u4F3C\u30BF\u30A4\u30C8\u30EB\u4E0A\u4F4D\u3092\u8FD4\u3059 (\u3072\u3089\u304C\u306A\u2194\u30AB\u30BF\u30AB\u30CA\u6B63\u898F\u5316\u8FBC\u307F)\u3002\u300C\u6700\u8FD1\u3084\u3063\u305F\u4F3C\u305F\u30BF\u30B9\u30AF\u3092\u53C2\u8003\u306B\u65B0\u3057\u3044\u30BF\u30B9\u30AF\u3092\u4F5C\u3063\u3066\u300D\u306E\u3088\u3046\u306A AI \u64CD\u4F5C\u5411\u3051\u3002`limit` \u65E2\u5B9A 100\u3002",
3670
+ inputSchema: listRecentTasksInputSchema.shape
3671
+ },
3672
+ async (input) => wrapHandler(ctxFactory, listRecentTasksHandler, input)
3673
+ );
3674
+ server.registerTool(
3675
+ "create_project",
3676
+ {
3677
+ title: "Create project",
3678
+ description: "\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u3092\u65B0\u898F\u4F5C\u6210\u3059\u308B\u3002`name` \u5FC5\u9808\u3001`status` \u306F active \u56FA\u5B9A\u3002`parent_project_id` \u3092\u6307\u5B9A\u3059\u308B\u3068\u30B5\u30D6\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u3002",
3679
+ inputSchema: createProjectInputSchema.shape
3680
+ },
3681
+ async (input) => wrapHandler(ctxFactory, createProjectHandler, input)
3682
+ );
3683
+ server.registerTool(
3684
+ "update_project",
3685
+ {
3686
+ title: "Update project",
3687
+ description: "\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u3092\u90E8\u5206\u66F4\u65B0\u3059\u308B\u3002undefined=\u5909\u66F4\u306A\u3057\u3001null=\u660E\u793A\u30AF\u30EA\u30A2\u3002`status` \u306E archived \u5316\u306F archive_project tool \u63A8\u5968\u3002\u81EA\u5206\u81EA\u8EAB\u3092 parent \u306B\u3057\u3088\u3046\u3068\u3059\u308B\u3068\u30A8\u30E9\u30FC\u3002",
3688
+ inputSchema: updateProjectInputSchema.shape
3689
+ },
3690
+ async (input) => wrapHandler(ctxFactory, updateProjectHandler, input)
3691
+ );
3692
+ server.registerTool(
3693
+ "archive_project",
3694
+ {
3695
+ title: "Archive project",
3696
+ description: "\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u3092\u30A2\u30FC\u30AB\u30A4\u30D6\u3059\u308B (status=archived + archived_at \u30BB\u30C3\u30C8)\u3002\u95A2\u9023\u30BF\u30B9\u30AF\u306E project_id \u306F\u7DAD\u6301\u3055\u308C\u308B\u306E\u3067\u3001\u904E\u53BB\u5C65\u6B74\u304C\u8AAD\u3081\u308B\u3002\u901A\u5E38\u306F delete \u3088\u308A archive \u3092\u63A8\u5968\u3002",
3697
+ inputSchema: archiveProjectInputSchema.shape
3698
+ },
3699
+ async (input) => wrapHandler(ctxFactory, archiveProjectHandler, input)
3700
+ );
3701
+ server.registerTool(
3702
+ "delete_project",
3703
+ {
3704
+ title: "Delete project",
3705
+ description: "\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u3092\u7269\u7406\u524A\u9664\u3059\u308B\u3002\u30BF\u30B9\u30AF\u306E project_id \u306F NULL \u5316 (ON DELETE SET NULL)\u3001\u5B50\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u306E parent_project_id \u3082 NULL \u5316\u3002\u904E\u53BB\u5C65\u6B74\u3092\u4FDD\u3061\u305F\u3044\u5834\u5408\u306F archive_project \u3092\u4F7F\u3046\u3002",
3706
+ inputSchema: deleteProjectInputSchema.shape
3707
+ },
3708
+ async (input) => wrapHandler(ctxFactory, deleteProjectHandler, input)
3709
+ );
3710
+ server.registerTool(
3711
+ "create_tag",
3712
+ {
3713
+ title: "Create tag",
3714
+ description: "\u30BF\u30B0\u3092\u65B0\u898F\u4F5C\u6210\u3059\u308B\u3002`name` \u5FC5\u9808\u3001status \u306F active \u56FA\u5B9A\u3002",
3715
+ inputSchema: createTagInputSchema.shape
3716
+ },
3717
+ async (input) => wrapHandler(ctxFactory, createTagHandler, input)
3718
+ );
3719
+ server.registerTool(
3720
+ "update_tag",
3721
+ {
3722
+ title: "Update tag",
3723
+ description: "\u30BF\u30B0\u3092\u90E8\u5206\u66F4\u65B0\u3059\u308B\u3002null=\u660E\u793A\u30AF\u30EA\u30A2\u3002",
3724
+ inputSchema: updateTagInputSchema.shape
3725
+ },
3726
+ async (input) => wrapHandler(ctxFactory, updateTagHandler, input)
3727
+ );
3728
+ server.registerTool(
3729
+ "archive_tag",
3730
+ {
3731
+ title: "Archive tag",
3732
+ description: "\u30BF\u30B0\u3092\u30A2\u30FC\u30AB\u30A4\u30D6\u3059\u308B\u3002\u30BF\u30B9\u30AF\u306E tag_ids \u914D\u5217\u306B id \u306F\u6B8B\u308B\u306E\u3067\u3001\u65E2\u5B58\u30BF\u30B9\u30AF\u306E\u30BF\u30B0\u8868\u793A\u306F\u7DAD\u6301\u3055\u308C\u308B\u3002",
3733
+ inputSchema: archiveTagInputSchema.shape
3734
+ },
3735
+ async (input) => wrapHandler(ctxFactory, archiveTagHandler, input)
3736
+ );
3737
+ server.registerTool(
3738
+ "delete_tag",
3739
+ {
3740
+ title: "Delete tag",
3741
+ description: "\u30BF\u30B0\u3092\u7269\u7406\u524A\u9664\u3059\u308B\u3002\u30BF\u30B9\u30AF\u306E tag_ids \u914D\u5217\u306F UUID[] \u5217\u306A\u306E\u3067\u5B64\u5150 id \u304C\u6B8B\u308B (UI \u8868\u793A\u6642\u306B\u300C\u4E0D\u660E\u306A\u30BF\u30B0\u300D\u6271\u3044)\u3002\u5C65\u6B74\u5C0A\u91CD\u306A\u3089 archive_tag \u63A8\u5968\u3002",
3742
+ inputSchema: deleteTagInputSchema.shape
3743
+ },
3744
+ async (input) => wrapHandler(ctxFactory, deleteTagHandler, input)
3745
+ );
3746
+ server.registerTool(
3747
+ "create_context",
3748
+ {
3749
+ title: "Create context",
3750
+ description: "\u30B3\u30F3\u30C6\u30AD\u30B9\u30C8 (\u4F5C\u696D\u74B0\u5883\u30BF\u30B0\u3002home / office / mobile \u7B49) \u3092\u65B0\u898F\u4F5C\u6210\u3059\u308B\u3002`name` \u5FC5\u9808\u3001icon \u306F\u7D75\u6587\u5B57\u307E\u305F\u306F\u30A2\u30A4\u30B3\u30F3\u540D\u3002",
3751
+ inputSchema: createContextInputSchema.shape
3752
+ },
3753
+ async (input) => wrapHandler(ctxFactory, createContextHandler, input)
3754
+ );
3755
+ server.registerTool(
3756
+ "update_context",
3757
+ {
3758
+ title: "Update context",
3759
+ description: "\u30B3\u30F3\u30C6\u30AD\u30B9\u30C8\u3092\u90E8\u5206\u66F4\u65B0\u3059\u308B\u3002null=\u660E\u793A\u30AF\u30EA\u30A2\u3002",
3760
+ inputSchema: updateContextInputSchema.shape
3761
+ },
3762
+ async (input) => wrapHandler(ctxFactory, updateContextHandler, input)
3763
+ );
3764
+ server.registerTool(
3765
+ "archive_context",
3766
+ {
3767
+ title: "Archive context",
3768
+ description: "\u30B3\u30F3\u30C6\u30AD\u30B9\u30C8\u3092\u30A2\u30FC\u30AB\u30A4\u30D6\u3059\u308B\u3002\u30BF\u30B9\u30AF\u306E context_id \u306F\u7DAD\u6301\u3002",
3769
+ inputSchema: archiveContextInputSchema.shape
3770
+ },
3771
+ async (input) => wrapHandler(ctxFactory, archiveContextHandler, input)
3772
+ );
3773
+ server.registerTool(
3774
+ "delete_context",
3775
+ {
3776
+ title: "Delete context",
3777
+ description: "\u30B3\u30F3\u30C6\u30AD\u30B9\u30C8\u3092\u7269\u7406\u524A\u9664\u3059\u308B\u3002\u30BF\u30B9\u30AF\u306E context_id \u306F NULL \u5316 (ON DELETE SET NULL)\u3002\u5C65\u6B74\u5C0A\u91CD\u306A\u3089 archive_context \u63A8\u5968\u3002",
3778
+ inputSchema: deleteContextInputSchema.shape
3779
+ },
3780
+ async (input) => wrapHandler(ctxFactory, deleteContextHandler, input)
3781
+ );
3782
+ server.registerTool(
3783
+ "create_routine",
3784
+ {
3785
+ title: "Create routine",
3786
+ description: '\u7E70\u308A\u8FD4\u3057\u30BF\u30B9\u30AF\u306E\u30C6\u30F3\u30D7\u30EC\u30FC\u30C8 (\u30EB\u30FC\u30C1\u30F3) \u3092\u65B0\u898F\u4F5C\u6210\u3059\u308B\u3002`frequency` \u306F discriminated union (\u4F8B: {type: "weekly", weekdays: [1,3,5]})\u3002auto_expand=true (default) \u306A\u3089 Today \u3092\u958B\u3044\u305F\u6642\u306B\u81EA\u52D5\u5C55\u958B\u3055\u308C\u308B\u3002',
3787
+ inputSchema: createRoutineInputSchema.shape
3788
+ },
3789
+ async (input) => wrapHandler(ctxFactory, createRoutineHandler, input)
3790
+ );
3791
+ server.registerTool(
3792
+ "update_routine",
3793
+ {
3794
+ title: "Update routine (with propagation)",
3795
+ description: "\u30EB\u30FC\u30C1\u30F3\u3092\u90E8\u5206\u66F4\u65B0\u3059\u308B\u3002**\u672A\u6765 (date >= today) \u306E planned \u30BF\u30B9\u30AF\u306B\u81EA\u52D5\u4F1D\u64AD**\u3055\u308C\u308B (title / estimated / project / context / tags / scheduled_start_time)\u3002\u624B\u52D5\u7DE8\u96C6\u3057\u3066\u3044\u305F\u30BF\u30B9\u30AF\u306E\u8A72\u5F53\u30D5\u30A3\u30FC\u30EB\u30C9\u3082\u4E0A\u66F8\u304D\u3055\u308C\u308B\u70B9\u306B\u6CE8\u610F\u3002\u5B9F\u884C\u4E2D\u30FB\u5B8C\u4E86\u30FB\u30B9\u30AD\u30C3\u30D7\u6E08\u307F\u30FB\u904E\u53BB\u65E5\u306E\u30BF\u30B9\u30AF\u306F\u4FDD\u8B77\u3055\u308C\u308B\u3002propagated_task_count \u3092\u8FD4\u3059\u3002",
3796
+ inputSchema: updateRoutineInputSchema.shape
3797
+ },
3798
+ async (input) => wrapHandler(ctxFactory, updateRoutineHandler, input)
3799
+ );
3800
+ server.registerTool(
3801
+ "archive_routine",
3802
+ {
3803
+ title: "Archive routine",
3804
+ description: "\u30EB\u30FC\u30C1\u30F3\u3092\u30A2\u30FC\u30AB\u30A4\u30D6\u3059\u308B (status=archived)\u3002\u65E2\u5B58\u30BF\u30B9\u30AF\u306E routine_id \u306F\u7DAD\u6301\u3055\u308C\u308B (\u5C65\u6B74\u53EF)\u3002\u65B0\u898F\u5C55\u958B\u306F\u6B62\u307E\u308B\u3002",
3805
+ inputSchema: archiveRoutineInputSchema.shape
3806
+ },
3807
+ async (input) => wrapHandler(ctxFactory, archiveRoutineHandler, input)
3808
+ );
3809
+ server.registerTool(
3810
+ "delete_routine",
3811
+ {
3812
+ title: "Delete routine",
3813
+ description: "\u30EB\u30FC\u30C1\u30F3\u3092\u7269\u7406\u524A\u9664\u3059\u308B\u3002\u30BF\u30B9\u30AF\u306E routine_id \u306F ON DELETE SET NULL \u3067 NULL \u5316 (\u300Croutine \u7531\u6765\u300D\u306E\u30E9\u30D9\u30EB\u304C\u5931\u308F\u308C\u308B)\u3002\u5C65\u6B74\u5C0A\u91CD\u306A\u3089 archive_routine \u63A8\u5968\u3002",
3814
+ inputSchema: deleteRoutineInputSchema.shape
3815
+ },
3816
+ async (input) => wrapHandler(ctxFactory, deleteRoutineHandler, input)
3817
+ );
3818
+ server.registerTool(
3819
+ "create_time_block",
3820
+ {
3821
+ title: "Create time block",
3822
+ description: "\u30BF\u30A4\u30E0\u30D6\u30ED\u30C3\u30AF (\u6642\u9593\u5E2F\u30C6\u30F3\u30D7\u30EC\u30FC\u30C8) \u3092\u65B0\u898F\u4F5C\u6210\u3059\u308B\u3002weekdays \u306F 0=\u65E5\u66DC\uFF5E6=\u571F\u66DC\u306E\u914D\u5217 (\u7A7A\u914D\u5217\u4E0D\u53EF)\u3002default_project_id / default_context_id / default_tag_ids \u3092\u6307\u5B9A\u3059\u308B\u3068\u30D6\u30ED\u30C3\u30AF\u7BC4\u56F2\u306B\u8FFD\u52A0\u3055\u308C\u305F\u30BF\u30B9\u30AF\u306E\u30C7\u30D5\u30A9\u30EB\u30C8\u306B\u4F7F\u308F\u308C\u308B\u3002",
3823
+ inputSchema: createTimeBlockInputSchema.shape
3824
+ },
3825
+ async (input) => wrapHandler(ctxFactory, createTimeBlockHandler, input)
3826
+ );
3827
+ server.registerTool(
3828
+ "update_time_block",
3829
+ {
3830
+ title: "Update time block",
3831
+ description: "\u30BF\u30A4\u30E0\u30D6\u30ED\u30C3\u30AF\u3092\u90E8\u5206\u66F4\u65B0\u3059\u308B\u3002default_tag_ids \u306F\u914D\u5217\u6307\u5B9A\u3067\u5168\u7F6E\u63DB\u3001null \u30AF\u30EA\u30A2\u306F default_project_id / default_context_id / color / start_date / end_date \u306E\u307F\u5BFE\u5FDC\u3002",
3832
+ inputSchema: updateTimeBlockInputSchema.shape
3833
+ },
3834
+ async (input) => wrapHandler(ctxFactory, updateTimeBlockHandler, input)
3835
+ );
3836
+ server.registerTool(
3837
+ "archive_time_block",
3838
+ {
3839
+ title: "Archive time block",
3840
+ description: "\u30BF\u30A4\u30E0\u30D6\u30ED\u30C3\u30AF\u3092\u30A2\u30FC\u30AB\u30A4\u30D6\u3059\u308B (status=archived)\u3002\u904E\u53BB\u65E5\u8868\u793A\u3067\u306F\u5F53\u6642\u306E\u72B6\u614B\u304C\u518D\u73FE\u3055\u308C\u308B\u3002",
3841
+ inputSchema: archiveTimeBlockInputSchema.shape
3842
+ },
3843
+ async (input) => wrapHandler(ctxFactory, archiveTimeBlockHandler, input)
3844
+ );
3845
+ server.registerTool(
3846
+ "delete_time_block",
3847
+ {
3848
+ title: "Delete time block",
3849
+ description: "\u30BF\u30A4\u30E0\u30D6\u30ED\u30C3\u30AF\u3092\u7269\u7406\u524A\u9664\u3059\u308B\u3002\u30BF\u30B9\u30AF\u306F\u6642\u523B\u30D9\u30FC\u30B9\u3067\u52D5\u7684\u306B\u30D6\u30ED\u30C3\u30AF\u3092\u5224\u5B9A\u3059\u308B\u306E\u3067\u53C2\u7167\u4E0D\u6574\u5408\u306F\u767A\u751F\u3057\u306A\u3044\u304C\u3001\u904E\u53BB\u65E5\u8868\u793A\u3067\u3082\u3053\u306E\u30D6\u30ED\u30C3\u30AF\u306F\u51FA\u3066\u3053\u306A\u304F\u306A\u308B\u3002",
3850
+ inputSchema: deleteTimeBlockInputSchema.shape
3851
+ },
3852
+ async (input) => wrapHandler(ctxFactory, deleteTimeBlockHandler, input)
3853
+ );
3854
+ server.registerTool(
3855
+ "set_actual_times",
3856
+ {
3857
+ title: "Set actual start/end times",
3858
+ 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)\u3002start+end \u2192 completed\u3001start \u306E\u307F \u2192 running\u3001\u4E21\u65B9 null \u2192 planned \u30EA\u30BB\u30C3\u30C8\u3002end < start \u306A\u3089\u65E5\u8DE8\u304E\u30BB\u30C3\u30B7\u30E7\u30F3\u6271\u3044 (Domain \u3067 finishedAt \u3092\u7FCC\u65E5\u7E70\u4E0A\u3052)\u3002",
3859
+ inputSchema: setActualTimesInputSchema.shape
3860
+ },
3861
+ async (input) => wrapHandler(ctxFactory, setActualTimesHandler, input)
3862
+ );
3863
+ server.registerTool(
3864
+ "interrupt_task",
3865
+ {
3866
+ title: "Interrupt running task",
3867
+ description: "\u8D70\u884C\u4E2D\u30BF\u30B9\u30AF\u3092\u300C\u5272\u308A\u8FBC\u307F\u300D\u3067\u5206\u5272\u3059\u308B\u30021 \u547C\u3073\u51FA\u3057\u3067 3 \u30BF\u30B9\u30AF\u3092\u64CD\u4F5C: (1) \u5143\u30BF\u30B9\u30AF\u3092 completed (\u5B9F\u7E3E\u306F\u305D\u3053\u307E\u3067)\u3001(2) \u5272\u308A\u8FBC\u307F\u30BF\u30B9\u30AF\u3092 running \u3067\u65B0\u898F\u4F5C\u6210 (\u5373\u8A08\u6E2C\u958B\u59CB)\u3001(3) \u5143\u30BF\u30B9\u30AF\u306E\u6B8B\u4F5C\u696D\u3092 planned \u3067\u65B0\u898F\u4F5C\u6210 (splitFromTaskId \u3067\u30EA\u30CD\u30FC\u30B8\u4FDD\u6301)\u3002split note 3 \u4EF6\u304C\u81EA\u52D5\u8A18\u9332\u3055\u308C\u308B\u3002\u5BFE\u8C61\u30BF\u30B9\u30AF\u304C running \u4E2D\u3067\u306A\u3044\u3068\u30A8\u30E9\u30FC\u3002",
3868
+ inputSchema: interruptTaskInputSchema.shape
3869
+ },
3870
+ async (input) => wrapHandler(ctxFactory, interruptTaskHandler, input)
3871
+ );
3872
+ server.registerTool(
3873
+ "add_parallel_task",
3874
+ {
3875
+ title: "Add parallel task",
3876
+ description: "\u9032\u884C\u4E2D\u30BF\u30B9\u30AF\u3068\u4E26\u884C\u3059\u308B\u65B0\u898F\u30BF\u30B9\u30AF\u3092\u8FFD\u52A0\u3059\u308B\u3002start_immediately: true (default) \u3067 running \u72B6\u614B + \u5373\u8A08\u6E2C\u958B\u59CB\u3002false \u3067 planned \u4F5C\u6210\u306E\u307F\u3002is_background: true \u4F75\u7528\u3067\u898B\u7A4D\u3082\u308A\u96C6\u8A08\u304B\u3089\u9664\u5916\u3002",
3877
+ inputSchema: addParallelTaskInputSchema.shape
3878
+ },
3879
+ async (input) => wrapHandler(ctxFactory, addParallelTaskHandler, input)
3880
+ );
3881
+ server.registerTool(
3882
+ "reorder_task",
3883
+ {
3884
+ title: "Reorder task",
3885
+ description: "\u30BF\u30B9\u30AF\u306E\u4E26\u3073\u9806 (order_index) \u3092\u5909\u66F4\u3059\u308B\u3002\u6163\u7FD2\u3067 10 \u5358\u4F4D (\u96A3\u63A5\u4E2D\u9593\u633F\u5165\u306F 5 \u3084 15)\u3002scheduled_start_time \u3082\u540C\u6642\u6307\u5B9A\u53EF\u80FD (null=\u672A\u5272\u5F53\u30B0\u30EB\u30FC\u30D7\u3078\u3001HH:MM=\u8A72\u5F53\u30BF\u30A4\u30E0\u30D6\u30ED\u30C3\u30AF\u3078\u79FB\u52D5)\u3002",
3886
+ inputSchema: reorderTaskInputSchema.shape
3887
+ },
3888
+ async (input) => wrapHandler(ctxFactory, reorderTaskHandler, input)
3889
+ );
2179
3890
  return server;
2180
3891
  }
2181
3892
 
2182
3893
  // src/index.ts
2183
3894
  async function main() {
2184
- const version = "0.1.2" ;
3895
+ const version = "0.2.0" ;
2185
3896
  const server = createMcpServer({ version });
2186
3897
  const transport = new StdioServerTransport();
2187
3898
  await server.connect(transport);