@minimalcorp/tsunagi 0.1.3 → 0.1.4
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/.next/standalone/apps/web/.next/BUILD_ID +1 -1
- package/.next/standalone/apps/web/.next/app-path-routes-manifest.json +1 -0
- package/.next/standalone/apps/web/.next/build-manifest.json +3 -3
- package/.next/standalone/apps/web/.next/prerender-manifest.json +3 -3
- package/.next/standalone/apps/web/.next/routes-manifest.json +6 -0
- package/.next/standalone/apps/web/.next/server/app/_global-error.html +1 -1
- package/.next/standalone/apps/web/.next/server/app/_global-error.rsc +1 -1
- package/.next/standalone/apps/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/.next/standalone/apps/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/.next/standalone/apps/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/.next/standalone/apps/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/.next/standalone/apps/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/apps/web/.next/server/app/_not-found.html +1 -1
- package/.next/standalone/apps/web/.next/server/app/_not-found.rsc +1 -1
- package/.next/standalone/apps/web/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/.next/standalone/apps/web/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/.next/standalone/apps/web/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/.next/standalone/apps/web/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/.next/standalone/apps/web/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/.next/standalone/apps/web/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/apps/web/.next/server/app/health/route/app-paths-manifest.json +3 -0
- package/.next/standalone/apps/web/.next/server/app/health/route/build-manifest.json +9 -0
- package/.next/standalone/apps/web/.next/server/app/health/route/server-reference-manifest.json +4 -0
- package/.next/standalone/apps/web/.next/server/app/health/route.js +6 -0
- package/.next/standalone/apps/web/.next/server/app/health/route.js.map +5 -0
- package/.next/standalone/apps/web/.next/server/app/health/route.js.nft.json +1 -0
- package/.next/standalone/apps/web/.next/server/app/health/route_client-reference-manifest.js +3 -0
- package/.next/standalone/apps/web/.next/server/app/icon.png/route.js +2 -1
- package/.next/standalone/apps/web/.next/server/app/icon.png/route.js.nft.json +1 -1
- package/.next/standalone/apps/web/.next/server/app/index.html +1 -1
- package/.next/standalone/apps/web/.next/server/app/index.rsc +2 -2
- package/.next/standalone/apps/web/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/apps/web/.next/server/app/index.segments/_full.segment.rsc +2 -2
- package/.next/standalone/apps/web/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/.next/standalone/apps/web/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/.next/standalone/apps/web/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/apps/web/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/standalone/apps/web/.next/server/app/settings.html +1 -1
- package/.next/standalone/apps/web/.next/server/app/settings.rsc +1 -1
- package/.next/standalone/apps/web/.next/server/app/settings.segments/_full.segment.rsc +1 -1
- package/.next/standalone/apps/web/.next/server/app/settings.segments/_head.segment.rsc +1 -1
- package/.next/standalone/apps/web/.next/server/app/settings.segments/_index.segment.rsc +1 -1
- package/.next/standalone/apps/web/.next/server/app/settings.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/apps/web/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +1 -1
- package/.next/standalone/apps/web/.next/server/app/settings.segments/settings.segment.rsc +1 -1
- package/.next/standalone/apps/web/.next/server/app/tasks/[id]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/apps/web/.next/server/app-paths-manifest.json +1 -0
- package/.next/standalone/apps/web/.next/server/chunks/[root-of-the-server]__03yk.z3._.js +3 -0
- package/.next/standalone/apps/web/.next/server/chunks/[root-of-the-server]__0op9af~._.js +3 -0
- package/.next/standalone/apps/web/.next/server/chunks/apps_web__next-internal_server_app_health_route_actions_08pjq78.js +3 -0
- package/.next/standalone/apps/web/.next/server/chunks/node_modules_next_06p.d_a._.js +13 -0
- package/.next/standalone/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0qw6wy3._.js +2 -2
- package/.next/standalone/apps/web/.next/server/chunks/ssr/[root-of-the-server]__12yece_._.js +2 -2
- package/.next/standalone/apps/web/.next/server/chunks/ssr/_0g.cd6a._.js +1 -1
- package/.next/standalone/apps/web/.next/server/middleware-build-manifest.js +3 -3
- package/.next/standalone/apps/web/.next/server/pages/404.html +1 -1
- package/.next/standalone/apps/web/.next/server/pages/500.html +1 -1
- package/.next/standalone/apps/web/.next/server/server-reference-manifest.js +1 -1
- package/.next/standalone/apps/web/.next/server/server-reference-manifest.json +1 -1
- package/.next/standalone/apps/web/.next/static/chunks/{0l0s3fn6xga.t.js → 0bulq7i0gcc02.js} +2 -2
- package/.next/standalone/apps/web/.next/static/chunks/{0ew.-lnxoet~x.js → 0lvd32c5czli~.js} +2 -2
- package/.next/standalone/apps/web/.next/static/chunks/{0hx_sdyz0yyfb.js → 0x7qsr~cuotr_.js} +1 -1
- package/dist/auto-migrate.js +6 -7
- package/dist/cli.js +120 -10
- package/dist/plugin-lifecycle.d.ts +4 -1
- package/dist/plugin-lifecycle.js +11 -4
- package/dist/server/index.js +6 -1
- package/dist/server/routes/hooks.js +24 -12
- package/dist/server/routes/terminal.js +3 -2
- package/package.json +1 -1
- package/prisma/migrations/20260410000000_clear_stale_tab_todos/migration.sql +4 -0
- package/tsunagi-marketplace/plugins/tsunagi-plugin/skills/tsunagi-task/SKILL.md +21 -0
- package/.next/standalone/apps/web/.next/server/chunks/[root-of-the-server]__0jam776._.js +0 -13
- /package/.next/standalone/apps/web/.next/static/{y1VVVedZfN-isLJKKaC_l → WKHKhVSNPtIYKtVKANGvu}/_buildManifest.js +0 -0
- /package/.next/standalone/apps/web/.next/static/{y1VVVedZfN-isLJKKaC_l → WKHKhVSNPtIYKtVKANGvu}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/apps/web/.next/static/{y1VVVedZfN-isLJKKaC_l → WKHKhVSNPtIYKtVKANGvu}/_ssgManifest.js +0 -0
|
@@ -1,26 +1,31 @@
|
|
|
1
1
|
import { prisma } from '../lib/db.js';
|
|
2
2
|
export const hookEvents = [];
|
|
3
3
|
const sessionTasks = new Map();
|
|
4
|
-
/**
|
|
4
|
+
/**
|
|
5
|
+
* TaskEntryリストをTodo形式に変換。
|
|
6
|
+
* 'deleted' を含めて全 status を pass-through し、表示時のフィルタは frontend 側に任せる。
|
|
7
|
+
*/
|
|
5
8
|
function tasksToTodos(tasks) {
|
|
6
9
|
return Array.from(tasks.values()).map((t) => ({
|
|
7
10
|
content: t.subject,
|
|
8
|
-
status: t.status
|
|
11
|
+
status: t.status,
|
|
9
12
|
}));
|
|
10
13
|
}
|
|
11
14
|
/** DBでタブのステータス(+ todos)を直接更新する */
|
|
12
15
|
async function updateTabStatus(sessionId, status, todos) {
|
|
13
16
|
try {
|
|
14
|
-
await prisma.tab.updateMany({
|
|
17
|
+
const result = await prisma.tab.updateMany({
|
|
15
18
|
where: { tabId: sessionId },
|
|
16
19
|
data: {
|
|
17
20
|
status,
|
|
18
21
|
...(todos !== undefined && { todos: JSON.stringify(todos) }),
|
|
19
22
|
},
|
|
20
23
|
});
|
|
24
|
+
return { count: result.count };
|
|
21
25
|
}
|
|
22
26
|
catch (err) {
|
|
23
27
|
console.warn(`[hooks] Failed to update tab status in DB:`, err);
|
|
28
|
+
return { count: 0 };
|
|
24
29
|
}
|
|
25
30
|
}
|
|
26
31
|
export async function hooksRoutes(fastify) {
|
|
@@ -64,7 +69,9 @@ export async function hooksRoutes(fastify) {
|
|
|
64
69
|
if (body.tool_name === 'TodoWrite' && body.tool_input) {
|
|
65
70
|
const todos = body.tool_input.todos;
|
|
66
71
|
if (Array.isArray(todos)) {
|
|
67
|
-
|
|
72
|
+
fastify.log.info({ sessionId, todosCount: todos.length }, '[hooks] TodoWrite received');
|
|
73
|
+
const { count } = await updateTabStatus(sessionId, 'running', todos);
|
|
74
|
+
fastify.log.info({ sessionId, updatedTabCount: count }, '[hooks] TodoWrite tab updated');
|
|
68
75
|
io.to(room).emit('todos-updated', { sessionId, todos });
|
|
69
76
|
todosUpdated = true;
|
|
70
77
|
}
|
|
@@ -87,16 +94,19 @@ export async function hooksRoutes(fastify) {
|
|
|
87
94
|
todosUpdated = true;
|
|
88
95
|
}
|
|
89
96
|
}
|
|
90
|
-
// TaskUpdate:
|
|
97
|
+
// TaskUpdate: タスクのステータスを更新('deleted' 含む。Mapからは除去せず保持し、表示層で除外)
|
|
91
98
|
if (body.tool_name === 'TaskUpdate' && body.tool_response && body.tool_input) {
|
|
92
99
|
const taskId = body.tool_input.taskId;
|
|
93
|
-
|
|
100
|
+
// tool_input.status を優先(直接入力なので確実)、フォールバックで tool_response.statusChange.to
|
|
101
|
+
const newStatus = body.tool_input.status ??
|
|
102
|
+
body.tool_response.statusChange?.to;
|
|
94
103
|
const tasks = sessionTasks.get(sessionId);
|
|
95
|
-
if (tasks && taskId &&
|
|
104
|
+
if (tasks && taskId && newStatus) {
|
|
96
105
|
const entry = tasks.get(taskId);
|
|
97
106
|
if (entry) {
|
|
98
|
-
entry.status =
|
|
107
|
+
entry.status = newStatus;
|
|
99
108
|
const todos = tasksToTodos(tasks);
|
|
109
|
+
fastify.log.info({ sessionId, taskId, newStatus, mapSize: tasks.size }, '[hooks] TaskUpdate processed');
|
|
100
110
|
await updateTabStatus(sessionId, 'running', todos);
|
|
101
111
|
io.to(room).emit('todos-updated', { sessionId, todos });
|
|
102
112
|
todosUpdated = true;
|
|
@@ -118,16 +128,18 @@ export async function hooksRoutes(fastify) {
|
|
|
118
128
|
io.to(room).emit('status-changed', { sessionId, status: 'success' });
|
|
119
129
|
break;
|
|
120
130
|
case 'StopFailure':
|
|
121
|
-
// APIエラーでターン終了 → failure
|
|
122
|
-
await updateTabStatus(sessionId, 'error');
|
|
131
|
+
// APIエラーでターン終了 → failure状態に更新、todosをクリア
|
|
132
|
+
await updateTabStatus(sessionId, 'error', []);
|
|
123
133
|
io.to(room).emit('status-changed', { sessionId, status: 'failure' });
|
|
134
|
+
io.to(room).emit('todos-updated', { sessionId, todos: [] });
|
|
124
135
|
break;
|
|
125
136
|
case 'SessionEnd':
|
|
126
|
-
// セッション終了(Escキー中断・Ctrl+C・/exit等)→ idle
|
|
137
|
+
// セッション終了(Escキー中断・Ctrl+C・/exit等)→ idle状態に更新、todosをクリア
|
|
127
138
|
// reason: "prompt_input_exit" = ユーザー中断、"other" = プロセスkill等
|
|
128
139
|
fastify.log.info({ sessionId, reason: body.reason }, 'SessionEnd received');
|
|
129
|
-
await updateTabStatus(sessionId, 'idle');
|
|
140
|
+
await updateTabStatus(sessionId, 'idle', []);
|
|
130
141
|
io.to(room).emit('status-changed', { sessionId, status: 'idle' });
|
|
142
|
+
io.to(room).emit('todos-updated', { sessionId, todos: [] });
|
|
131
143
|
break;
|
|
132
144
|
default:
|
|
133
145
|
fastify.log.debug({ eventName }, 'Unhandled hook event');
|
|
@@ -48,12 +48,13 @@ export async function terminalRoutes(fastify) {
|
|
|
48
48
|
// PTYプロセス終了 → room全体に通知(全接続クライアントに終了を伝える)+ セッション削除
|
|
49
49
|
const exitHandler = ptyProcess.onExit(({ exitCode }) => {
|
|
50
50
|
io.to(room).emit('exit', { exitCode });
|
|
51
|
-
// Ctrl+C等でClaudeが強制終了した場合にclaudeStatusをidle
|
|
51
|
+
// Ctrl+C等でClaudeが強制終了した場合にclaudeStatusをidleにリセット、todosをクリア
|
|
52
52
|
io.to(room).emit('status-changed', { sessionId, status: 'idle' });
|
|
53
|
+
io.to(room).emit('todos-updated', { sessionId, todos: [] });
|
|
53
54
|
prisma.tab
|
|
54
55
|
.updateMany({
|
|
55
56
|
where: { tabId: sessionId },
|
|
56
|
-
data: { status: 'idle' },
|
|
57
|
+
data: { status: 'idle', todos: '[]' },
|
|
57
58
|
})
|
|
58
59
|
.catch(() => {
|
|
59
60
|
/* DB更新失敗は無視 */
|
package/package.json
CHANGED
|
@@ -17,6 +17,27 @@ description: プロジェクトのタスク管理ツール。タスクの検索
|
|
|
17
17
|
|
|
18
18
|
遷移は必ず順方向: backlog → planning → coding → reviewing → done
|
|
19
19
|
|
|
20
|
+
## 実装開始時の TODO 作成(必須)
|
|
21
|
+
|
|
22
|
+
`coding` に遷移したら、**即座に** Claude Code 組み込みの `TodoWrite` ツールで実装ステップを TODO として登録する。これにより tsunagi UI にプログレスバーが表示される。
|
|
23
|
+
|
|
24
|
+
### 手順
|
|
25
|
+
|
|
26
|
+
1. `tsunagi_update_task(status: "coding")` の直後に `TodoWrite` を呼ぶ
|
|
27
|
+
2. 各 TODO は完了判断が明確な作業単位にする
|
|
28
|
+
- 例: 「○○ファイルを修正」「マイグレーション作成・適用」「lint/type-check を通す」「動作確認」
|
|
29
|
+
3. ステップ完了ごとに `TodoWrite` で該当項目の status を `in_progress` → `completed` に更新する
|
|
30
|
+
4. 作業中に追加ステップが判明した場合、既存 TODO に追記して `TodoWrite` で再登録する
|
|
31
|
+
|
|
32
|
+
### 語彙の使い分け(混同注意)
|
|
33
|
+
|
|
34
|
+
| 対象 | 使うツール | status 語彙 |
|
|
35
|
+
| --------------- | ---------------------- | -------------------------------------- |
|
|
36
|
+
| tsunagi タスク | `tsunagi_update_task` | backlog/planning/coding/reviewing/done |
|
|
37
|
+
| tab TODO (進捗) | `TodoWrite` (組み込み) | pending/in_progress/completed |
|
|
38
|
+
|
|
39
|
+
tsunagi タスクの status に `in_progress` を渡してはいけない(「よくある間違い」表参照)。逆に TODO の進捗管理は組み込み `TodoWrite` で行う。
|
|
40
|
+
|
|
20
41
|
## 現在のタスクを特定する
|
|
21
42
|
|
|
22
43
|
タスク操作には `id`, `session_id`, `cwd` のいずれかでタスクを指定する。
|