@fukuchang/taskchute-mcp 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +18 -0
- package/dist/index.js +60 -16
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.3.0 (2026-05-28) — 実績時刻の秒精度サポート
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- `set_actual_times` の `start_time` / `end_time` が `HH:MM` に加え `HH:MM:SS`
|
|
8
|
+
も受け入れるようになった。秒は任意で、省略時は従来通り 0 として扱う。Web UI
|
|
9
|
+
でも実績時刻の入力 / 表示が秒精度で行えるようになり、Play ボタンを押した時刻
|
|
10
|
+
も秒単位で `actual_start_time` に記録される。
|
|
11
|
+
|
|
12
|
+
### Notes
|
|
13
|
+
|
|
14
|
+
- 後方互換あり: 既存の `HH:MM` 入力 / 保存データはそのまま動く。Supabase の
|
|
15
|
+
時刻カラムは TEXT のためマイグレーション不要
|
|
16
|
+
- `start_task` / 並列タスク / 割り込みタスクで記録される `actual_start_time`
|
|
17
|
+
は今後 `HH:MM:SS` 形式で返ることがある (従来は `HH:MM`)。文字列としての
|
|
18
|
+
扱いは変わらないが、下流ツールが正規表現で `HH:MM` のみを期待していた場合
|
|
19
|
+
は `HH:MM(:SS)?` にゆるめる必要がある
|
|
20
|
+
|
|
3
21
|
## 0.2.0 (2026-05-28) — Web パリティ完全達成 ✨
|
|
4
22
|
|
|
5
23
|
5 PR (PR1〜5) で MCP/CLI を Web 機能パリティまで拡張し、PR6 で SOLID 観点の
|
package/dist/index.js
CHANGED
|
@@ -218,14 +218,14 @@ async function deriveKEK(password, salt, params = DEFAULT_ARGON2ID_PARAMS) {
|
|
|
218
218
|
}
|
|
219
219
|
|
|
220
220
|
// ../crypto/src/wrap.ts
|
|
221
|
-
async function unwrapMasterKey(wrapped, iv, kek) {
|
|
221
|
+
async function unwrapMasterKey(wrapped, iv, kek, options = {}) {
|
|
222
222
|
return crypto.subtle.unwrapKey(
|
|
223
223
|
"raw",
|
|
224
224
|
wrapped,
|
|
225
225
|
kek,
|
|
226
226
|
{ name: "AES-GCM", iv },
|
|
227
227
|
{ name: "AES-GCM", length: 256 },
|
|
228
|
-
false,
|
|
228
|
+
options.extractable ?? false,
|
|
229
229
|
["encrypt", "decrypt"]
|
|
230
230
|
);
|
|
231
231
|
}
|
|
@@ -511,6 +511,7 @@ async function rowToContext(row, cryptoSession) {
|
|
|
511
511
|
id: row.id,
|
|
512
512
|
name,
|
|
513
513
|
status: row.status,
|
|
514
|
+
notifyAtScheduledStart: row.notify_at_scheduled_start,
|
|
514
515
|
createdAt: new Date(row.created_at),
|
|
515
516
|
updatedAt: new Date(row.updated_at)
|
|
516
517
|
};
|
|
@@ -524,6 +525,9 @@ function nonEncryptedFieldsToRow(input) {
|
|
|
524
525
|
if ("icon" in input) row.icon = input.icon ?? null;
|
|
525
526
|
if ("color" in input) row.color = input.color ?? null;
|
|
526
527
|
if ("status" in input && input.status !== void 0) row.status = input.status;
|
|
528
|
+
if ("notifyAtScheduledStart" in input && input.notifyAtScheduledStart !== void 0) {
|
|
529
|
+
row.notify_at_scheduled_start = input.notifyAtScheduledStart;
|
|
530
|
+
}
|
|
527
531
|
return row;
|
|
528
532
|
}
|
|
529
533
|
async function encryptContextFieldsInto(row, entityId, patch, cryptoSession) {
|
|
@@ -1613,6 +1617,12 @@ function toTimeString(date) {
|
|
|
1613
1617
|
const minutes = String(date.getMinutes()).padStart(2, "0");
|
|
1614
1618
|
return `${hours}:${minutes}`;
|
|
1615
1619
|
}
|
|
1620
|
+
function toTimeStringWithSeconds(date) {
|
|
1621
|
+
const hours = String(date.getHours()).padStart(2, "0");
|
|
1622
|
+
const minutes = String(date.getMinutes()).padStart(2, "0");
|
|
1623
|
+
const seconds = String(date.getSeconds()).padStart(2, "0");
|
|
1624
|
+
return `${hours}:${minutes}:${seconds}`;
|
|
1625
|
+
}
|
|
1616
1626
|
|
|
1617
1627
|
// ../domain/src/task-utils.ts
|
|
1618
1628
|
function sessionDurationSeconds(session) {
|
|
@@ -1630,14 +1640,15 @@ function calculateActualSeconds(task, now = /* @__PURE__ */ new Date()) {
|
|
|
1630
1640
|
}
|
|
1631
1641
|
|
|
1632
1642
|
// ../domain/src/task-operations.ts
|
|
1633
|
-
var TIME_RE = /^([01]\d|2[0-3]):([0-5]\d)
|
|
1643
|
+
var TIME_RE = /^([01]\d|2[0-3]):([0-5]\d)(?::([0-5]\d))?$/;
|
|
1634
1644
|
function parseTimeOnDate(dateString, timeString) {
|
|
1635
1645
|
const m = TIME_RE.exec(timeString);
|
|
1636
1646
|
if (!m || !m[1] || !m[2]) {
|
|
1637
1647
|
throw new Error(`Invalid time string: ${timeString}`);
|
|
1638
1648
|
}
|
|
1639
1649
|
const base = fromDateString(dateString);
|
|
1640
|
-
|
|
1650
|
+
const seconds = m[3] ? Number(m[3]) : 0;
|
|
1651
|
+
base.setHours(Number(m[1]), Number(m[2]), seconds, 0);
|
|
1641
1652
|
return base;
|
|
1642
1653
|
}
|
|
1643
1654
|
async function startTask(repo, taskId) {
|
|
@@ -1650,7 +1661,7 @@ async function startTask(repo, taskId) {
|
|
|
1650
1661
|
currentSession: { startedAt: now }
|
|
1651
1662
|
};
|
|
1652
1663
|
if (!task.actualStartTime) {
|
|
1653
|
-
patch.actualStartTime =
|
|
1664
|
+
patch.actualStartTime = toTimeStringWithSeconds(now);
|
|
1654
1665
|
}
|
|
1655
1666
|
return repo.update(taskId, patch);
|
|
1656
1667
|
}
|
|
@@ -1737,7 +1748,7 @@ async function interruptTask(repo, params) {
|
|
|
1737
1748
|
currentSession: { startedAt: interruptionStartedAt },
|
|
1738
1749
|
status: "running",
|
|
1739
1750
|
orderIndex: target.orderIndex + 1,
|
|
1740
|
-
actualStartTime:
|
|
1751
|
+
actualStartTime: toTimeStringWithSeconds(interruptionStartedAt),
|
|
1741
1752
|
projectId: params.interruption.projectId,
|
|
1742
1753
|
contextId: params.interruption.contextId,
|
|
1743
1754
|
tagIds: params.interruption.tagIds ?? []
|
|
@@ -1778,7 +1789,7 @@ async function addParallelTask(repo, input) {
|
|
|
1778
1789
|
currentSession: startedAt ? { startedAt } : void 0,
|
|
1779
1790
|
status: startImmediately ? "running" : "planned",
|
|
1780
1791
|
orderIndex: maxOrder + 10,
|
|
1781
|
-
actualStartTime: startedAt ?
|
|
1792
|
+
actualStartTime: startedAt ? toTimeStringWithSeconds(startedAt) : void 0,
|
|
1782
1793
|
projectId: input.projectId,
|
|
1783
1794
|
contextId: input.contextId,
|
|
1784
1795
|
tagIds: input.tagIds ?? [],
|
|
@@ -2203,6 +2214,14 @@ function pickPropagationTargets(routine, tasks, today) {
|
|
|
2203
2214
|
);
|
|
2204
2215
|
}
|
|
2205
2216
|
|
|
2217
|
+
// ../domain/src/context-task-inheritance.ts
|
|
2218
|
+
function applyContextNotificationInheritance(input, context) {
|
|
2219
|
+
if (context?.notifyAtScheduledStart !== true) return input;
|
|
2220
|
+
const current = input.notifyAtScheduledStart;
|
|
2221
|
+
if (current !== void 0) return input;
|
|
2222
|
+
return { ...input, notifyAtScheduledStart: true };
|
|
2223
|
+
}
|
|
2224
|
+
|
|
2206
2225
|
// ../infra-supabase/src/audit-task-repository.ts
|
|
2207
2226
|
var AuditingTaskRepository = class {
|
|
2208
2227
|
// `erasableSyntaxOnly` 配下では parameter property が使えないので、明示的に
|
|
@@ -2492,8 +2511,9 @@ async function addTaskHandler(ctx, input) {
|
|
|
2492
2511
|
const project = await ctx.repos.project.findById(input.project_id);
|
|
2493
2512
|
if (!project) throw new McpToolError(`Project not found: ${input.project_id}`);
|
|
2494
2513
|
}
|
|
2514
|
+
let context = null;
|
|
2495
2515
|
if (input.context_id) {
|
|
2496
|
-
|
|
2516
|
+
context = await ctx.repos.context.findById(input.context_id);
|
|
2497
2517
|
if (!context) throw new McpToolError(`Context not found: ${input.context_id}`);
|
|
2498
2518
|
}
|
|
2499
2519
|
if (input.tag_ids && input.tag_ids.length > 0) {
|
|
@@ -2509,7 +2529,7 @@ async function addTaskHandler(ctx, input) {
|
|
|
2509
2529
|
const estimatedSeconds = input.estimated_seconds ?? DEFAULT_ESTIMATED_SECONDS;
|
|
2510
2530
|
const existing = await ctx.repos.task.findByDate(date);
|
|
2511
2531
|
const orderIndex = computeNextOrderIndex(existing);
|
|
2512
|
-
const
|
|
2532
|
+
const createInput = {
|
|
2513
2533
|
date,
|
|
2514
2534
|
title: input.title,
|
|
2515
2535
|
description: input.description,
|
|
@@ -2523,7 +2543,10 @@ async function addTaskHandler(ctx, input) {
|
|
|
2523
2543
|
scheduledStartTime: input.scheduled_start_time,
|
|
2524
2544
|
notifyAtScheduledStart: input.notify_at_scheduled_start,
|
|
2525
2545
|
isBackground: input.is_background
|
|
2526
|
-
}
|
|
2546
|
+
};
|
|
2547
|
+
const task = await ctx.repos.task.create(
|
|
2548
|
+
applyContextNotificationInheritance(createInput, context)
|
|
2549
|
+
);
|
|
2527
2550
|
return { task: taskSummaryFrom(task) };
|
|
2528
2551
|
}
|
|
2529
2552
|
|
|
@@ -2533,6 +2556,7 @@ function contextSummaryFrom(context) {
|
|
|
2533
2556
|
id: context.id,
|
|
2534
2557
|
name: context.name,
|
|
2535
2558
|
status: context.status,
|
|
2559
|
+
notify_at_scheduled_start: context.notifyAtScheduledStart ?? false,
|
|
2536
2560
|
created_at: context.createdAt.toISOString(),
|
|
2537
2561
|
updated_at: context.updatedAt.toISOString()
|
|
2538
2562
|
};
|
|
@@ -2694,7 +2718,10 @@ var createContextInputSchema = z.object({
|
|
|
2694
2718
|
name: z.string().min(1, "name must not be empty"),
|
|
2695
2719
|
description: z.string().optional(),
|
|
2696
2720
|
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)")
|
|
2721
|
+
color: z.string().optional().describe("UI \u8272 (\u4F8B: #ff6b6b)"),
|
|
2722
|
+
notify_at_scheduled_start: z.boolean().optional().describe(
|
|
2723
|
+
"true \u306A\u3089\u3001\u3053\u306E\u30B3\u30F3\u30C6\u30AD\u30B9\u30C8\u3092\u4ED8\u3051\u305F\u65B0\u898F/\u66F4\u65B0\u30BF\u30B9\u30AF\u306E\u901A\u77E5\u3092 ON \u306B\u81EA\u52D5\u7D99\u627F\u3059\u308B"
|
|
2724
|
+
)
|
|
2698
2725
|
});
|
|
2699
2726
|
async function createContextHandler(ctx, input) {
|
|
2700
2727
|
const createInput = {
|
|
@@ -2704,6 +2731,9 @@ async function createContextHandler(ctx, input) {
|
|
|
2704
2731
|
if (input.description !== void 0) createInput.description = input.description;
|
|
2705
2732
|
if (input.icon !== void 0) createInput.icon = input.icon;
|
|
2706
2733
|
if (input.color !== void 0) createInput.color = input.color;
|
|
2734
|
+
if (input.notify_at_scheduled_start !== void 0) {
|
|
2735
|
+
createInput.notifyAtScheduledStart = input.notify_at_scheduled_start;
|
|
2736
|
+
}
|
|
2707
2737
|
const context = await ctx.repos.context.create(createInput);
|
|
2708
2738
|
return { context: contextSummaryFrom(context) };
|
|
2709
2739
|
}
|
|
@@ -3135,10 +3165,11 @@ async function reorderTaskHandler(ctx, input) {
|
|
|
3135
3165
|
const updated = await ctx.repos.task.update(input.id, patch);
|
|
3136
3166
|
return { task: taskSummaryFrom(updated) };
|
|
3137
3167
|
}
|
|
3168
|
+
var ACTUAL_TIME_RE = /^([01]\d|2[0-3]):[0-5]\d(?::[0-5]\d)?$/;
|
|
3138
3169
|
var setActualTimesInputSchema = z.object({
|
|
3139
3170
|
id: z.string().uuid(),
|
|
3140
|
-
start_time: z.union([z.string().regex(
|
|
3141
|
-
end_time: z.union([z.string().regex(
|
|
3171
|
+
start_time: z.union([z.string().regex(ACTUAL_TIME_RE, "must be HH:MM or HH:MM:SS"), z.null()]).describe("HH:MM \u307E\u305F\u306F HH:MM:SS\u3002null \u306A\u3089\u5B9F\u7E3E\u958B\u59CB\u306A\u3057\u3002end_time \u306E\u307F\u6307\u5B9A\u306F\u30A8\u30E9\u30FC"),
|
|
3172
|
+
end_time: z.union([z.string().regex(ACTUAL_TIME_RE, "must be HH:MM or HH:MM:SS"), z.null()]).describe("HH:MM \u307E\u305F\u306F HH:MM:SS\u3002null \u306A\u3089\u672A\u5B8C\u4E86 (running \u6271\u3044)")
|
|
3142
3173
|
});
|
|
3143
3174
|
async function setActualTimesHandler(ctx, input) {
|
|
3144
3175
|
const existing = await ctx.repos.task.findById(input.id);
|
|
@@ -3184,7 +3215,10 @@ var updateContextInputSchema = z.object({
|
|
|
3184
3215
|
description: z.union([z.string(), z.null()]).optional(),
|
|
3185
3216
|
icon: z.union([z.string(), z.null()]).optional(),
|
|
3186
3217
|
color: z.union([z.string(), z.null()]).optional(),
|
|
3187
|
-
status: z.enum(["active", "archived"]).optional().describe("archived \u5316\u306F archive_context tool \u63A8\u5968")
|
|
3218
|
+
status: z.enum(["active", "archived"]).optional().describe("archived \u5316\u306F archive_context tool \u63A8\u5968"),
|
|
3219
|
+
notify_at_scheduled_start: z.boolean().optional().describe(
|
|
3220
|
+
"true \u306A\u3089\u3001\u3053\u306E\u30B3\u30F3\u30C6\u30AD\u30B9\u30C8\u3092\u4ED8\u3051\u305F\u65B0\u898F/\u66F4\u65B0\u30BF\u30B9\u30AF\u306E\u901A\u77E5\u3092 ON \u306B\u81EA\u52D5\u7D99\u627F\u3059\u308B"
|
|
3221
|
+
)
|
|
3188
3222
|
});
|
|
3189
3223
|
async function updateContextHandler(ctx, input) {
|
|
3190
3224
|
const existing = await ctx.repos.context.findById(input.id);
|
|
@@ -3203,6 +3237,9 @@ async function updateContextHandler(ctx, input) {
|
|
|
3203
3237
|
patch.color = input.color === null ? void 0 : input.color;
|
|
3204
3238
|
}
|
|
3205
3239
|
if (input.status !== void 0) patch.status = input.status;
|
|
3240
|
+
if (input.notify_at_scheduled_start !== void 0) {
|
|
3241
|
+
patch.notifyAtScheduledStart = input.notify_at_scheduled_start;
|
|
3242
|
+
}
|
|
3206
3243
|
const updated = await ctx.repos.context.update(input.id, patch);
|
|
3207
3244
|
return { context: contextSummaryFrom(updated) };
|
|
3208
3245
|
}
|
|
@@ -3342,9 +3379,11 @@ async function updateRoutineHandler(ctx, input) {
|
|
|
3342
3379
|
);
|
|
3343
3380
|
}
|
|
3344
3381
|
const taskPatch = buildRoutineTaskPatch(updated);
|
|
3382
|
+
const routineContext = updated.contextId ? await ctx.repos.context.findById(updated.contextId) : null;
|
|
3383
|
+
const propagatedPatch = applyContextNotificationInheritance(taskPatch, routineContext);
|
|
3345
3384
|
await Promise.all(
|
|
3346
3385
|
targets.map(
|
|
3347
|
-
(task) => auditingTask.updateWithCause(task.id,
|
|
3386
|
+
(task) => auditingTask.updateWithCause(task.id, propagatedPatch, "routine-propagation")
|
|
3348
3387
|
)
|
|
3349
3388
|
);
|
|
3350
3389
|
}
|
|
@@ -3438,6 +3477,11 @@ async function updateTaskHandler(ctx, input) {
|
|
|
3438
3477
|
patch.notifyAtScheduledStart = input.notify_at_scheduled_start;
|
|
3439
3478
|
}
|
|
3440
3479
|
if (input.is_background !== void 0) patch.isBackground = input.is_background;
|
|
3480
|
+
const newContextId = patch.contextId;
|
|
3481
|
+
if (newContextId) {
|
|
3482
|
+
const newContext = await ctx.repos.context.findById(newContextId);
|
|
3483
|
+
Object.assign(patch, applyContextNotificationInheritance(patch, newContext));
|
|
3484
|
+
}
|
|
3441
3485
|
const updated = await ctx.repos.task.update(input.id, patch);
|
|
3442
3486
|
return { task: taskSummaryFrom(updated) };
|
|
3443
3487
|
}
|
|
@@ -3892,7 +3936,7 @@ function createMcpServer(opts) {
|
|
|
3892
3936
|
|
|
3893
3937
|
// src/index.ts
|
|
3894
3938
|
async function main() {
|
|
3895
|
-
const version = "0.
|
|
3939
|
+
const version = "0.4.0" ;
|
|
3896
3940
|
const server = createMcpServer({ version });
|
|
3897
3941
|
const transport = new StdioServerTransport();
|
|
3898
3942
|
await server.connect(transport);
|
package/package.json
CHANGED