@whatasoda/agent-tools 0.1.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/agents/codex-review/body.md +98 -0
- package/dist/agents/team-reviewer/body.md +78 -0
- package/dist/agents/team-worker/body.md +46 -0
- package/dist/scripts/codex-review.js +237 -0
- package/dist/scripts/detect-base-branch.js +185 -0
- package/dist/scripts/resolve-session.js +76 -0
- package/dist/skills/soda-brief/body.md +73 -0
- package/dist/skills/soda-discuss/README.md +25 -0
- package/dist/skills/soda-discuss/body.md +216 -0
- package/dist/skills/soda-fix/body.md +137 -0
- package/dist/skills/soda-plan/body.md +333 -0
- package/dist/skills/soda-research/body.md +127 -0
- package/dist/skills/soda-review/body.md +165 -0
- package/dist/skills/soda-review-todos/body.md +19 -0
- package/dist/skills/soda-team-init/body.md +313 -0
- package/dist/skills/soda-team-init/references/coordination-files.md +188 -0
- package/dist/skills/soda-team-run/body.md +329 -0
- package/dist/skills/soda-todo/body.md +86 -0
- package/dist/src/cli/commands/agent.js +29 -0
- package/dist/src/cli/commands/codex-review.js +14 -0
- package/dist/src/cli/commands/decision.js +103 -0
- package/dist/src/cli/commands/import.js +174 -0
- package/dist/src/cli/commands/link.js +52 -0
- package/dist/src/cli/commands/list.js +12 -0
- package/dist/src/cli/commands/node.js +118 -0
- package/dist/src/cli/commands/review.js +23 -0
- package/dist/src/cli/commands/session.js +23 -0
- package/dist/src/cli/commands/skill.js +29 -0
- package/dist/src/cli/commands/tag.js +31 -0
- package/dist/src/cli/helpers.js +51 -0
- package/dist/src/cli/index.js +48 -0
- package/dist/src/cli.js +59 -0
- package/dist/src/core/database.js +209 -0
- package/dist/src/core/ensure-dirs.js +8 -0
- package/dist/src/core/index.js +1 -0
- package/dist/src/core/kinds.js +46 -0
- package/dist/src/core/schema.sql +36 -0
- package/dist/src/core/schemas.js +41 -0
- package/dist/src/core/search.js +80 -0
- package/dist/src/core/types.js +0 -0
- package/dist/src/tui/App.js +130 -0
- package/dist/src/tui/actions.js +9 -0
- package/dist/src/tui/components/FilterBar.js +46 -0
- package/dist/src/tui/components/LinkList.js +53 -0
- package/dist/src/tui/components/NodeDetail.js +111 -0
- package/dist/src/tui/components/NodeList.js +62 -0
- package/dist/src/tui/components/StatusBar.js +90 -0
- package/dist/src/tui/hooks/useNavigation.js +57 -0
- package/dist/src/tui/hooks/useNodes.js +44 -0
- package/dist/src/tui/index.js +4 -0
- package/package.json +29 -0
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
|
|
2
|
+
Execute agent team tasks by orchestrating specialized agents (Worker, Reviewer, Architect, Investigator). Each invocation runs one or more cycles, auto-continuing when all tasks pass and actionable work remains.
|
|
3
|
+
|
|
4
|
+
Use English for all generated file content and sub-agent communication. User interaction (AskUserQuestion options, status updates, summaries) must be in Japanese.
|
|
5
|
+
|
|
6
|
+
If `$ARGUMENTS` is empty, default to selecting the next actionable tasks automatically.
|
|
7
|
+
|
|
8
|
+
## Cycle Definition
|
|
9
|
+
|
|
10
|
+
One cycle of `/soda-team-run` consists of:
|
|
11
|
+
1. Select actionable tasks
|
|
12
|
+
2. Execute tasks in parallel (Worker per task)
|
|
13
|
+
3. Review completed tasks (Reviewer per task)
|
|
14
|
+
4. Handle results (merge / re-implement / escalate)
|
|
15
|
+
5. Update coordination files
|
|
16
|
+
6. Report to user
|
|
17
|
+
|
|
18
|
+
When all tasks in a cycle pass and actionable tasks remain, the next cycle begins automatically (auto-continue). The cycle pauses for user input when any task fails, escalates, or conflicts — or when no actionable tasks remain.
|
|
19
|
+
|
|
20
|
+
## Step 1: Load Project State
|
|
21
|
+
|
|
22
|
+
**Repo root detection**:
|
|
23
|
+
```bash
|
|
24
|
+
git rev-parse --show-toplevel
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
**Project resolution** — resolve which namespaced project to use:
|
|
28
|
+
|
|
29
|
+
1. List subdirectories under `.agent-team/`.
|
|
30
|
+
2. If `.agent-team/` does not exist or has no subdirectories:
|
|
31
|
+
- If `.agent-team/CONFIG.md` exists directly (legacy flat layout): warn the user that the old format is detected, suggest re-initializing with `/soda-team-init`. Stop.
|
|
32
|
+
- Otherwise: inform the user no projects found, suggest `/soda-team-init`. Stop.
|
|
33
|
+
3. If exactly one subdirectory: use it as `{{PROJECT_DIR}}`.
|
|
34
|
+
4. If multiple subdirectories:
|
|
35
|
+
a. Extract project-name from the current branch (e.g., `team/auth-refactor` → `auth-refactor`)
|
|
36
|
+
b. Find a subdirectory whose name after the `YYYYMMDD-` prefix starts with `<project-name>` (e.g., `20260320-auth-refactor` and `20260320-auth-refactor-2` both match `auth-refactor`) → use it as `{{PROJECT_DIR}}`
|
|
37
|
+
c. Fallback: select the most recent by lexicographic sort (last entry, since `YYYYMMDD` prefix sorts chronologically)
|
|
38
|
+
5. Present the selected project for user confirmation before proceeding.
|
|
39
|
+
|
|
40
|
+
`{{PROJECT_DIR}}` is the resolved path (e.g., `.agent-team/20260320-auth-refactor`) used in all subsequent file references.
|
|
41
|
+
|
|
42
|
+
Read `{{PROJECT_DIR}}/TASKS.md`, `{{PROJECT_DIR}}/ARCHITECTURE.md`, and `{{PROJECT_DIR}}/CONFIG.md`.
|
|
43
|
+
|
|
44
|
+
If TASKS.md is missing in the selected project, inform the user and suggest `/soda-team-init`. Stop.
|
|
45
|
+
|
|
46
|
+
Extract the **Integration Branch** from CONFIG.md. All merge operations target this branch. Verify it exists:
|
|
47
|
+
```bash
|
|
48
|
+
git rev-parse --verify {{INTEGRATION_BRANCH}}
|
|
49
|
+
```
|
|
50
|
+
If missing, inform the user and stop.
|
|
51
|
+
|
|
52
|
+
Parse TASKS.md to determine:
|
|
53
|
+
- Total task count and status distribution
|
|
54
|
+
- Which tasks are actionable (pending `[ ]` with all deps satisfied)
|
|
55
|
+
- Which tasks are in-progress `[~]` (may be stale from a previous interrupted cycle)
|
|
56
|
+
- Which tasks are blocked `[!]`
|
|
57
|
+
|
|
58
|
+
If in-progress tasks exist, use AskUserQuestion:
|
|
59
|
+
- "中断タスクを再開する" — treat `[~]` tasks as this cycle's targets
|
|
60
|
+
- "中断タスクをリセットして次を選ぶ" — reset `[~]` to `[ ]`, then select normally
|
|
61
|
+
|
|
62
|
+
## Step 2: Task Selection
|
|
63
|
+
|
|
64
|
+
### Automatic Selection (default)
|
|
65
|
+
|
|
66
|
+
Identify all actionable tasks — tasks with status `[ ]` whose dependencies are all `[x]`.
|
|
67
|
+
|
|
68
|
+
Group actionable tasks by parallelizability:
|
|
69
|
+
- Tasks with no mutual file conflicts → can run in parallel
|
|
70
|
+
- Tasks that modify overlapping files → must run sequentially
|
|
71
|
+
|
|
72
|
+
Display the proposed batch:
|
|
73
|
+
|
|
74
|
+
> **今回のサイクル** ({{N}}タスク並列実行)
|
|
75
|
+
>
|
|
76
|
+
> | タスク | グループ | 概要 | 依存 |
|
|
77
|
+
> |--------|----------|------|------|
|
|
78
|
+
> | TASK-001 | A | {{title}} | なし |
|
|
79
|
+
> | TASK-003 | A | {{title}} | なし |
|
|
80
|
+
>
|
|
81
|
+
> 順次実行待ち: TASK-002 (TASK-001 完了後)
|
|
82
|
+
|
|
83
|
+
**Auto-continue**: If actionable tasks were found via automatic selection, proceed directly to Step 3 after displaying the batch. Do NOT use AskUserQuestion.
|
|
84
|
+
|
|
85
|
+
**Fallback** (use AskUserQuestion): If no actionable tasks are found, inform the user and present options:
|
|
86
|
+
- "ブロック中のタスクを確認する"
|
|
87
|
+
- "終了"
|
|
88
|
+
|
|
89
|
+
### Manual Selection
|
|
90
|
+
|
|
91
|
+
If `$ARGUMENTS` specifies a task ID (e.g., "TASK-005"), group name (e.g., "GROUP-A"):
|
|
92
|
+
- Task ID: execute that specific task (check deps are satisfied)
|
|
93
|
+
- Group name: select all actionable tasks in that group
|
|
94
|
+
- Present the selection for confirmation
|
|
95
|
+
- Do NOT proceed until the user confirms the batch.
|
|
96
|
+
|
|
97
|
+
## Step 3: Worker Execution
|
|
98
|
+
|
|
99
|
+
For each task in the batch, launch a Worker sub-agent in parallel.
|
|
100
|
+
|
|
101
|
+
### Worktree Setup
|
|
102
|
+
|
|
103
|
+
For each Worker, create an isolated git worktree branching from the integration branch:
|
|
104
|
+
```bash
|
|
105
|
+
git worktree add .worktrees/{{TASK-ID}} -b task/{{TASK-ID}} {{INTEGRATION_BRANCH}}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Worker Sub-agent
|
|
109
|
+
|
|
110
|
+
Launch via `Task(subagent_type: soda:team-worker)`. The agent definition handles constraints, tools, and output format.
|
|
111
|
+
|
|
112
|
+
**Dynamic prompt** — pass only these sections:
|
|
113
|
+
|
|
114
|
+
```
|
|
115
|
+
## Task
|
|
116
|
+
[contents of TASK-NNN.md]
|
|
117
|
+
|
|
118
|
+
## Commit Message
|
|
119
|
+
{{imperative mood description from task title}}
|
|
120
|
+
|
|
121
|
+
## Working Directory
|
|
122
|
+
{{worktree path}}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Parallel Execution
|
|
126
|
+
|
|
127
|
+
Launch all Workers in the batch simultaneously using parallel `Task` calls. Each Worker operates on its own worktree — no file conflicts possible.
|
|
128
|
+
|
|
129
|
+
While Workers execute, report to the user:
|
|
130
|
+
|
|
131
|
+
> **実行中** ({{N}}タスク並列)
|
|
132
|
+
> - TASK-001: Worker 起動済み
|
|
133
|
+
> - TASK-003: Worker 起動済み
|
|
134
|
+
|
|
135
|
+
### Worker Result Handling
|
|
136
|
+
|
|
137
|
+
As each Worker completes:
|
|
138
|
+
|
|
139
|
+
- **DONE**: Proceed to Step 4 (Review)
|
|
140
|
+
- **BLOCKED**: Read BLOCKER.md from worktree. Orchestrator decides:
|
|
141
|
+
- If this is the first block: reset the worktree, launch an Investigator sub-agent to analyze the blocker, then create a new Worker with additional context on the same worktree
|
|
142
|
+
- If this is the second block for the same task: escalate to user (see Step 5)
|
|
143
|
+
|
|
144
|
+
**Worktree reset for retry**: When re-launching a Worker on the same task, reset the worktree to a clean state instead of creating a new one:
|
|
145
|
+
```bash
|
|
146
|
+
cd .worktrees/{{TASK-ID}}
|
|
147
|
+
git clean -fd
|
|
148
|
+
git reset --hard {{INTEGRATION_BRANCH}}
|
|
149
|
+
```
|
|
150
|
+
This maintains the disposable Worker principle (no stale state) while avoiding unnecessary disk usage from worktree recreation.
|
|
151
|
+
|
|
152
|
+
Update TASKS.md status to `[~]` when Worker starts, and track completion.
|
|
153
|
+
|
|
154
|
+
## Step 4: Review
|
|
155
|
+
|
|
156
|
+
For each completed Worker (status DONE), launch a Reviewer sub-agent via `Task(subagent_type: soda:team-reviewer)`. The agent definition handles constraints, tools, Trivial Fix Policy, Review Criteria, and output format.
|
|
157
|
+
|
|
158
|
+
**Dynamic prompt** — pass only these sections:
|
|
159
|
+
|
|
160
|
+
```
|
|
161
|
+
## Task Definition
|
|
162
|
+
[contents of TASK-NNN.md]
|
|
163
|
+
|
|
164
|
+
## Architecture Decisions
|
|
165
|
+
[contents of ARCHITECTURE.md — or relevant ADRs only if file is large]
|
|
166
|
+
|
|
167
|
+
## Working Directory
|
|
168
|
+
{{worktree path}}
|
|
169
|
+
|
|
170
|
+
## Changes to Review
|
|
171
|
+
[git diff of the Worker's worktree branch vs base]
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### Review Result Handling
|
|
175
|
+
|
|
176
|
+
- **PASS**: Write REVIEW-NNN-A.md → proceed to merge (Step 5)
|
|
177
|
+
- **PASS_WITH_FIX**: Write REVIEW-NNN-A.md (including Trivial Fixes Applied) → proceed to merge (Step 5). The Reviewer has already committed the fix.
|
|
178
|
+
- **FAIL**: Write REVIEW-NNN-A.md → append findings to TASK-NNN.md History → reset worktree (`git clean -fd && git reset --hard {{INTEGRATION_BRANCH}}`) → create new Worker on the same worktree (return to Step 3 for this task)
|
|
179
|
+
- **ESCALATE**: Write REVIEW-NNN-A.md → invoke Architect (Step 5). Worktree handling depends on Architect outcome — see Exiting Architect role.
|
|
180
|
+
|
|
181
|
+
Limit re-implementation attempts to 2 before user escalation. If a task fails review twice, mark as `[!]` and escalate to user. When the user explicitly chooses "追加調査して再試行" from User Escalation, the retry counter resets (the user has made an informed decision to continue).
|
|
182
|
+
|
|
183
|
+
## Step 5: Resolution
|
|
184
|
+
|
|
185
|
+
### Merge (PASS)
|
|
186
|
+
|
|
187
|
+
For each passed task, merge the Worker branch into the integration branch:
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
git checkout {{INTEGRATION_BRANCH}}
|
|
191
|
+
git merge --squash task/{{TASK-ID}}
|
|
192
|
+
git commit -m "{{task title}} (TASK-NNN)"
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
**Merge conflict handling**: If the merge fails due to conflicts:
|
|
196
|
+
- Abort the merge: `git reset --merge`
|
|
197
|
+
- Report the conflict to the user with the affected files
|
|
198
|
+
- Use AskUserQuestion:
|
|
199
|
+
- "コンフリクトを手動で解決する" — user resolves, then resume
|
|
200
|
+
- "このタスクを後回しにする" — mark `[!]`, clean up worktree (`git worktree remove .worktrees/{{TASK-ID}}` and `git branch -D task/{{TASK-ID}}`), proceed with other tasks
|
|
201
|
+
- Do NOT attempt automatic conflict resolution.
|
|
202
|
+
|
|
203
|
+
After successful merge, clean up:
|
|
204
|
+
```bash
|
|
205
|
+
git worktree remove .worktrees/{{TASK-ID}}
|
|
206
|
+
git branch -D task/{{TASK-ID}}
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
Update TASKS.md: `[~]` → `[x]` with merge commit SHA.
|
|
210
|
+
|
|
211
|
+
### Architect Escalation (ESCALATE)
|
|
212
|
+
|
|
213
|
+
When a Reviewer flags a design-level issue, the Orchestrator switches to **Architect role**. This is a deliberate context shift — while in Architect role, progress concerns are set aside and the focus is entirely on design correctness.
|
|
214
|
+
|
|
215
|
+
**Entering Architect role**:
|
|
216
|
+
1. Read ARCHITECTURE.md to load the full design decision history
|
|
217
|
+
2. Read the Reviewer's ESCALATE findings
|
|
218
|
+
3. Present the escalation to the user with the Reviewer's findings and relevant ADRs
|
|
219
|
+
|
|
220
|
+
**Architect dialogue with user**:
|
|
221
|
+
- Follow soda-discuss Interaction Principles (options with evidence and recommendation, one topic at a time)
|
|
222
|
+
- The user decides:
|
|
223
|
+
- Resolve the design issue (user ↔ Architect dialogue)
|
|
224
|
+
- Override and accept the implementation as-is
|
|
225
|
+
- Defer the task (mark as `[!]`)
|
|
226
|
+
|
|
227
|
+
**Exiting Architect role**:
|
|
228
|
+
- If a design decision was made:
|
|
229
|
+
- Write new/revised ADR to ARCHITECTURE.md
|
|
230
|
+
- Update affected TASK-NNN.md files with revised Design Constraints (summarized, not just references)
|
|
231
|
+
- Reset worktree (`git clean -fd && git reset --hard {{INTEGRATION_BRANCH}}`)
|
|
232
|
+
- The task re-enters Step 3 with updated constraints
|
|
233
|
+
- If the user chose to override:
|
|
234
|
+
- Do NOT reset the worktree — the Worker's implementation is accepted as-is
|
|
235
|
+
- Proceed directly to Merge (PASS) in Step 5
|
|
236
|
+
- If the user chose to defer:
|
|
237
|
+
- Mark task as `[!]` in TASKS.md
|
|
238
|
+
- Clean up worktree (`git worktree remove .worktrees/{{TASK-ID}}` and `git branch -D task/{{TASK-ID}}`)
|
|
239
|
+
- Resume Orchestrator role and continue the cycle
|
|
240
|
+
|
|
241
|
+
### User Escalation (BLOCKED)
|
|
242
|
+
|
|
243
|
+
When a task is blocked twice or a Worker fails twice:
|
|
244
|
+
|
|
245
|
+
Present the situation to the user:
|
|
246
|
+
|
|
247
|
+
> **エスカレーション**: TASK-NNN
|
|
248
|
+
> - 失敗回数: {{N}}
|
|
249
|
+
> - 直近の問題: {{summary from REVIEW or BLOCKER.md}}
|
|
250
|
+
> - 試行履歴: {{brief history}}
|
|
251
|
+
|
|
252
|
+
Use AskUserQuestion:
|
|
253
|
+
- "追加調査して再試行" — launch Investigator, update TASK-NNN.md context, retry (worktree is reset, retry counter resets)
|
|
254
|
+
- "タスクを分割する" — see Task Splitting below
|
|
255
|
+
- "タスクをスキップ" — mark `[!]` with reason, clean up worktree (`git worktree remove .worktrees/{{TASK-ID}}` and `git branch -D task/{{TASK-ID}}`)
|
|
256
|
+
- "手動で対応する" — mark `[!]`, clean up worktree, user handles outside the team
|
|
257
|
+
|
|
258
|
+
### Task Splitting
|
|
259
|
+
|
|
260
|
+
When the user chooses to split a failed task:
|
|
261
|
+
|
|
262
|
+
1. **Analyze**: Read the failed TASK-NNN.md, BLOCKER.md (if any), and latest REVIEW-NNN-A.md (if any) to understand the failure
|
|
263
|
+
2. **Investigate**: Launch an Investigator sub-agent with the failure context to propose a split strategy
|
|
264
|
+
3. **Present**: Show the proposed sub-tasks to the user in table format (same as soda-team-init Step 5):
|
|
265
|
+
> | # | タスク | 受入条件 | 依存 |
|
|
266
|
+
> |---|--------|----------|------|
|
|
267
|
+
> | 1 | {{title}} | {{acceptance}} | なし |
|
|
268
|
+
> | 2 | {{title}} | {{acceptance}} | #1 |
|
|
269
|
+
4. **Confirm**: Use AskUserQuestion for user approval
|
|
270
|
+
5. **Generate**:
|
|
271
|
+
- Mark original task as `[!]` in TASKS.md with note: `split → TASK-XXX, TASK-YYY`
|
|
272
|
+
- Generate new TASK-XXX.md, TASK-YYY.md files:
|
|
273
|
+
- Inherit Context and Design Constraints from the original task
|
|
274
|
+
- Add failure insights to History: `- Split from TASK-NNN: "{{failure summary}}"`
|
|
275
|
+
- Add new tasks to TASKS.md
|
|
276
|
+
- New task numbers: `max(existing task numbers) + 1`, continuing zero-padded sequence
|
|
277
|
+
6. **Clean up**: Remove the original task's worktree (`git worktree remove`, `git branch -D`)
|
|
278
|
+
7. New tasks become actionable in the next cycle (or current cycle if user selects "次のサイクルを実行")
|
|
279
|
+
|
|
280
|
+
## Step 6: Cycle Report
|
|
281
|
+
|
|
282
|
+
After all tasks in the batch are resolved, present a cycle summary:
|
|
283
|
+
|
|
284
|
+
> **サイクル完了** {{(自動続行) if auto-continuing}}
|
|
285
|
+
>
|
|
286
|
+
> | タスク | 結果 | 詳細 |
|
|
287
|
+
> |--------|------|------|
|
|
288
|
+
> | TASK-001 | ✅ マージ済み | {{commit SHA}} |
|
|
289
|
+
> | TASK-003 | ✅ マージ済み | {{commit SHA}} |
|
|
290
|
+
>
|
|
291
|
+
> **進捗**: {{done}}/{{total}} タスク完了 ({{percent}}%)
|
|
292
|
+
> **次のサイクル**: TASK-004, TASK-005 (並列実行)
|
|
293
|
+
|
|
294
|
+
**Auto-continue**: If ALL of the following are true, proceed directly to Step 2 without AskUserQuestion:
|
|
295
|
+
- Every task in the batch resulted in PASS or PASS_WITH_FIX (merged successfully)
|
|
296
|
+
- Actionable tasks remain (pending tasks with all deps satisfied)
|
|
297
|
+
|
|
298
|
+
**Stop conditions** (evaluated in this order):
|
|
299
|
+
1. **All tasks complete** → present completion summary and exit (no AskUserQuestion):
|
|
300
|
+
> **全タスク完了**
|
|
301
|
+
> - 完了: {{total}} タスク
|
|
302
|
+
> - マージコミット: {{list of merge commit SHAs}}
|
|
303
|
+
2. **Any task had FAIL, ESCALATE, or merge conflict** → present results and use AskUserQuestion:
|
|
304
|
+
- "次のサイクルを実行" — return to Step 2
|
|
305
|
+
- "終了"
|
|
306
|
+
3. **No remaining actionable tasks but incomplete tasks exist** (all remaining are blocked) → present status and use AskUserQuestion:
|
|
307
|
+
- "ブロック中のタスクを確認する"
|
|
308
|
+
- "終了"
|
|
309
|
+
|
|
310
|
+
## Constraints
|
|
311
|
+
|
|
312
|
+
- Each invocation runs one or more cycles. Auto-continue to the next cycle when all tasks pass and actionable tasks remain. Pause for user input on any failure, escalation, conflict, or when no actionable tasks remain.
|
|
313
|
+
- Workers MUST run on isolated git worktrees — never on the working tree.
|
|
314
|
+
- Workers MUST NOT use interactive tools (AskUserQuestion, EnterPlanMode).
|
|
315
|
+
- Reviewers MUST NOT make non-trivial modifications. Trivial fixes (1-2 lines, unambiguous) are permitted and must be recorded.
|
|
316
|
+
- Re-implementation is limited to 2 attempts per task before user escalation.
|
|
317
|
+
- TASKS.md is the single source of truth for task status. Update it after every state change.
|
|
318
|
+
- All coordination files must conform to `../soda-team-init/references/coordination-files.md`.
|
|
319
|
+
- Merge target is the integration branch recorded in `{{PROJECT_DIR}}/CONFIG.md`. Do NOT assume `main`.
|
|
320
|
+
|
|
321
|
+
## Sub-agent Usage
|
|
322
|
+
|
|
323
|
+
Every sub-agent prompt MUST begin with the appropriate constraint block (Worker constraints or Reviewer constraints as defined in Steps 3 and 4).
|
|
324
|
+
|
|
325
|
+
Sub-agent types:
|
|
326
|
+
- **Worker**: `Task(subagent_type: soda:team-worker)` — implementation agent, runs on isolated worktree. See `agents/team-worker.md` for constraints and output format.
|
|
327
|
+
- **Reviewer**: `Task(subagent_type: soda:team-reviewer)` — review agent with validation execution and trivial fix authority. See `agents/team-reviewer.md` for constraints, Trivial Fix Policy, Review Criteria, and output format.
|
|
328
|
+
- **Investigator**: `Task(subagent_type: Explore)` — codebase investigation
|
|
329
|
+
- **Architect**: Role switch within main context (not a sub-agent). Orchestrator loads ARCHITECTURE.md and enters design-focused dialogue with user via AskUserQuestion. See "Architect Escalation" in Step 5.
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
|
|
2
|
+
Use English for internal reasoning (thinking). User interaction must be in Japanese.
|
|
3
|
+
|
|
4
|
+
## Todo Schema
|
|
5
|
+
|
|
6
|
+
| Field | Type | Default |
|
|
7
|
+
|----------|------------------------------------------|---------|
|
|
8
|
+
| status | pending / in_progress / done / cancelled | pending |
|
|
9
|
+
| priority | low / medium / high | (none) |
|
|
10
|
+
| deadline | ISO 8601 datetime | (none) |
|
|
11
|
+
|
|
12
|
+
## Available CLI Commands (wat)
|
|
13
|
+
|
|
14
|
+
| Command | Description |
|
|
15
|
+
|---|---|
|
|
16
|
+
| `sd node create` | Create a new node with `--kind`, `--body`, `--tags`, `--prop`, `--props-json`, or `--stdin` |
|
|
17
|
+
| `sd node search` | Search nodes with `--query`, `--kind`, `--tags`, `--limit`, `--offset` |
|
|
18
|
+
| `sd tag add <id> <tags...>` | Add tags to a node |
|
|
19
|
+
|
|
20
|
+
## Instructions
|
|
21
|
+
|
|
22
|
+
Follow these five steps in order.
|
|
23
|
+
|
|
24
|
+
### Step 1: Accept Input
|
|
25
|
+
|
|
26
|
+
Accept the user's raw input. This may be:
|
|
27
|
+
- Direct text describing tasks (e.g., "来週までにPRレビュー、あと牛乳買う")
|
|
28
|
+
- Vague or compound descriptions that need splitting
|
|
29
|
+
- References to conversation context
|
|
30
|
+
|
|
31
|
+
If the user invokes `/sb-todo` without input, ask what they'd like to record.
|
|
32
|
+
|
|
33
|
+
### Step 2: Groom
|
|
34
|
+
|
|
35
|
+
Process the raw input into structured todo items:
|
|
36
|
+
|
|
37
|
+
1. **Split** compound inputs into separate todos (one actionable item per todo)
|
|
38
|
+
2. **Clean** body text — concise, actionable phrasing
|
|
39
|
+
3. **Infer** priority from urgency cues ("急ぎ" → high, "いつか" → low)
|
|
40
|
+
4. **Extract** deadlines from temporal references ("来週" → concrete ISO 8601 datetime, e.g., `2026-03-28T00:00:00Z`)
|
|
41
|
+
5. **Assign** tags based on content context (e.g., work, personal, shopping)
|
|
42
|
+
|
|
43
|
+
If critical information is ambiguous or missing and you cannot make a reasonable inference, ask the user before proceeding. Do not guess when uncertain.
|
|
44
|
+
|
|
45
|
+
### Step 3: Present
|
|
46
|
+
|
|
47
|
+
Present all groomed todos in a batch list for review:
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
以下のTODOを作成します:
|
|
51
|
+
|
|
52
|
+
1. **PRレビューを完了する**
|
|
53
|
+
- priority: medium
|
|
54
|
+
- deadline: 2026-03-28T00:00:00Z
|
|
55
|
+
- tags: work
|
|
56
|
+
|
|
57
|
+
2. **牛乳を買う**
|
|
58
|
+
- priority: low
|
|
59
|
+
- tags: shopping
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Step 4: Approve / Modify
|
|
63
|
+
|
|
64
|
+
Wait for the user's response:
|
|
65
|
+
- **Approval** ("OK", "いいね", etc.) → proceed to Step 5
|
|
66
|
+
- **Modification** ("2番のpriorityはhighにして") → apply changes, re-present the full list (return to Step 3)
|
|
67
|
+
|
|
68
|
+
Repeat until the user approves.
|
|
69
|
+
|
|
70
|
+
### Step 5: Create
|
|
71
|
+
|
|
72
|
+
Create each approved todo via CLI:
|
|
73
|
+
|
|
74
|
+
```sh
|
|
75
|
+
sd node create --kind todo --body "<body>" --prop status=pending --prop priority=<priority> --tags <tag1>,<tag2>
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Add `--prop deadline=<datetime>` when a deadline was specified.
|
|
79
|
+
|
|
80
|
+
After all nodes are created, show a summary:
|
|
81
|
+
|
|
82
|
+
```
|
|
83
|
+
作成完了:
|
|
84
|
+
- <id1>: PRレビューを完了する
|
|
85
|
+
- <id2>: 牛乳を買う
|
|
86
|
+
```
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import path from "path";
|
|
2
|
+
import { exitWithError } from "../helpers.js";
|
|
3
|
+
const packageRoot = path.resolve(import.meta.dir, "../../../");
|
|
4
|
+
export async function handleAgent(args) {
|
|
5
|
+
const [action, name] = args;
|
|
6
|
+
switch (action) {
|
|
7
|
+
case "print":
|
|
8
|
+
return agentPrint(name);
|
|
9
|
+
default:
|
|
10
|
+
exitWithError("Usage: soda agent <print> <name>");
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
async function agentPrint(name) {
|
|
14
|
+
if (!name) {
|
|
15
|
+
exitWithError("Usage: soda agent print <name>");
|
|
16
|
+
}
|
|
17
|
+
const baseDir = path.join(packageRoot, "agents");
|
|
18
|
+
const filePath = path.resolve(baseDir, name, "body.md");
|
|
19
|
+
if (!filePath.startsWith(baseDir + path.sep)) {
|
|
20
|
+
exitWithError(`Invalid agent name: ${name}`);
|
|
21
|
+
}
|
|
22
|
+
const file = Bun.file(filePath);
|
|
23
|
+
const exists = await file.exists();
|
|
24
|
+
if (!exists) {
|
|
25
|
+
exitWithError(`Agent not found: ${name}`);
|
|
26
|
+
}
|
|
27
|
+
const content = await file.text();
|
|
28
|
+
process.stdout.write(content);
|
|
29
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import path from "path";
|
|
2
|
+
const packageRoot = path.resolve(import.meta.dir, "../../../");
|
|
3
|
+
export async function handleCodexReview(args) {
|
|
4
|
+
const scriptPath = path.join(packageRoot, "scripts", "codex-review.ts");
|
|
5
|
+
const proc = Bun.spawn(["bun", scriptPath, ...args], {
|
|
6
|
+
stdout: "inherit",
|
|
7
|
+
stderr: "inherit",
|
|
8
|
+
stdin: "inherit"
|
|
9
|
+
});
|
|
10
|
+
const exitCode = await proc.exited;
|
|
11
|
+
if (exitCode !== 0) {
|
|
12
|
+
process.exit(exitCode);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { exitWithError, outputJson, parseCli, readStdin } from "../helpers.js";
|
|
2
|
+
export async function handleDecision(db, args) {
|
|
3
|
+
const [action] = args;
|
|
4
|
+
const rest = args.slice(1);
|
|
5
|
+
switch (action) {
|
|
6
|
+
case "create":
|
|
7
|
+
return decisionCreate(db, rest);
|
|
8
|
+
case "list":
|
|
9
|
+
return decisionList(db, rest);
|
|
10
|
+
case "import": {
|
|
11
|
+
const { handleImport } = await import("./import.js");
|
|
12
|
+
return handleImport(db, rest);
|
|
13
|
+
}
|
|
14
|
+
default:
|
|
15
|
+
exitWithError("Usage: sd decision <create|list|import>");
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
async function decisionCreate(db, args) {
|
|
19
|
+
const { values } = parseCli(args, {
|
|
20
|
+
constraint: { type: "string" },
|
|
21
|
+
why: { type: "string" },
|
|
22
|
+
scope: { type: "string" },
|
|
23
|
+
"repo-owner": { type: "string" },
|
|
24
|
+
"repo-name": { type: "string" },
|
|
25
|
+
tag: { multiple: true, type: "string" },
|
|
26
|
+
"rejected-alt-json": { type: "string" },
|
|
27
|
+
stdin: { default: false, type: "boolean" }
|
|
28
|
+
});
|
|
29
|
+
if (values.stdin) {
|
|
30
|
+
const input = await readStdin();
|
|
31
|
+
const result = db.createNode({
|
|
32
|
+
body: input.constraint,
|
|
33
|
+
kind: "decision",
|
|
34
|
+
properties: input.properties,
|
|
35
|
+
tags: input.tags
|
|
36
|
+
});
|
|
37
|
+
return outputJson(result);
|
|
38
|
+
}
|
|
39
|
+
if (!values.constraint) {
|
|
40
|
+
exitWithError("Error: --constraint is required");
|
|
41
|
+
}
|
|
42
|
+
if (!values.why) {
|
|
43
|
+
exitWithError("Error: --why is required");
|
|
44
|
+
}
|
|
45
|
+
if (!values.scope) {
|
|
46
|
+
exitWithError("Error: --scope is required");
|
|
47
|
+
}
|
|
48
|
+
const properties = {
|
|
49
|
+
constraint: values.constraint,
|
|
50
|
+
why: values.why,
|
|
51
|
+
scope: values.scope
|
|
52
|
+
};
|
|
53
|
+
if (values["repo-owner"]) {
|
|
54
|
+
properties.repo_owner = values["repo-owner"];
|
|
55
|
+
}
|
|
56
|
+
if (values["repo-name"]) {
|
|
57
|
+
properties.repo_name = values["repo-name"];
|
|
58
|
+
}
|
|
59
|
+
if (values["rejected-alt-json"]) {
|
|
60
|
+
try {
|
|
61
|
+
properties.rejected_alternatives = JSON.parse(values["rejected-alt-json"]);
|
|
62
|
+
} catch {
|
|
63
|
+
exitWithError("Error: invalid JSON in --rejected-alt-json");
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
const tags = values.tag;
|
|
67
|
+
const result = db.createNode({
|
|
68
|
+
body: values.constraint,
|
|
69
|
+
kind: "decision",
|
|
70
|
+
properties,
|
|
71
|
+
tags
|
|
72
|
+
});
|
|
73
|
+
outputJson(result);
|
|
74
|
+
}
|
|
75
|
+
async function decisionList(db, args) {
|
|
76
|
+
const { values } = parseCli(args, {
|
|
77
|
+
tag: { multiple: true, type: "string" },
|
|
78
|
+
repo: { type: "string" },
|
|
79
|
+
limit: { type: "string" }
|
|
80
|
+
});
|
|
81
|
+
const tags = values.tag;
|
|
82
|
+
const limit = values.limit ? parseInt(values.limit, 10) : 50;
|
|
83
|
+
if (isNaN(limit))
|
|
84
|
+
exitWithError("Error: --limit must be a number");
|
|
85
|
+
const results = db.search({
|
|
86
|
+
kind: "decision",
|
|
87
|
+
limit: values.repo ? Number.MAX_SAFE_INTEGER : limit,
|
|
88
|
+
offset: 0,
|
|
89
|
+
tags: tags?.length ? tags : undefined
|
|
90
|
+
});
|
|
91
|
+
if (values.repo) {
|
|
92
|
+
const repoStr = values.repo;
|
|
93
|
+
if (!repoStr.includes("/"))
|
|
94
|
+
exitWithError("Error: --repo must be in owner/name format");
|
|
95
|
+
const [repoOwner, repoName] = repoStr.split("/");
|
|
96
|
+
const filtered = results.nodes.filter((node) => {
|
|
97
|
+
const props = node.properties;
|
|
98
|
+
return props?.repo_owner === repoOwner && props?.repo_name === repoName;
|
|
99
|
+
});
|
|
100
|
+
return outputJson(filtered);
|
|
101
|
+
}
|
|
102
|
+
outputJson(results.nodes);
|
|
103
|
+
}
|