@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.
Files changed (51) hide show
  1. package/dist/agents/codex-review/body.md +98 -0
  2. package/dist/agents/team-reviewer/body.md +78 -0
  3. package/dist/agents/team-worker/body.md +46 -0
  4. package/dist/scripts/codex-review.js +237 -0
  5. package/dist/scripts/detect-base-branch.js +185 -0
  6. package/dist/scripts/resolve-session.js +76 -0
  7. package/dist/skills/soda-brief/body.md +73 -0
  8. package/dist/skills/soda-discuss/README.md +25 -0
  9. package/dist/skills/soda-discuss/body.md +216 -0
  10. package/dist/skills/soda-fix/body.md +137 -0
  11. package/dist/skills/soda-plan/body.md +333 -0
  12. package/dist/skills/soda-research/body.md +127 -0
  13. package/dist/skills/soda-review/body.md +165 -0
  14. package/dist/skills/soda-review-todos/body.md +19 -0
  15. package/dist/skills/soda-team-init/body.md +313 -0
  16. package/dist/skills/soda-team-init/references/coordination-files.md +188 -0
  17. package/dist/skills/soda-team-run/body.md +329 -0
  18. package/dist/skills/soda-todo/body.md +86 -0
  19. package/dist/src/cli/commands/agent.js +29 -0
  20. package/dist/src/cli/commands/codex-review.js +14 -0
  21. package/dist/src/cli/commands/decision.js +103 -0
  22. package/dist/src/cli/commands/import.js +174 -0
  23. package/dist/src/cli/commands/link.js +52 -0
  24. package/dist/src/cli/commands/list.js +12 -0
  25. package/dist/src/cli/commands/node.js +118 -0
  26. package/dist/src/cli/commands/review.js +23 -0
  27. package/dist/src/cli/commands/session.js +23 -0
  28. package/dist/src/cli/commands/skill.js +29 -0
  29. package/dist/src/cli/commands/tag.js +31 -0
  30. package/dist/src/cli/helpers.js +51 -0
  31. package/dist/src/cli/index.js +48 -0
  32. package/dist/src/cli.js +59 -0
  33. package/dist/src/core/database.js +209 -0
  34. package/dist/src/core/ensure-dirs.js +8 -0
  35. package/dist/src/core/index.js +1 -0
  36. package/dist/src/core/kinds.js +46 -0
  37. package/dist/src/core/schema.sql +36 -0
  38. package/dist/src/core/schemas.js +41 -0
  39. package/dist/src/core/search.js +80 -0
  40. package/dist/src/core/types.js +0 -0
  41. package/dist/src/tui/App.js +130 -0
  42. package/dist/src/tui/actions.js +9 -0
  43. package/dist/src/tui/components/FilterBar.js +46 -0
  44. package/dist/src/tui/components/LinkList.js +53 -0
  45. package/dist/src/tui/components/NodeDetail.js +111 -0
  46. package/dist/src/tui/components/NodeList.js +62 -0
  47. package/dist/src/tui/components/StatusBar.js +90 -0
  48. package/dist/src/tui/hooks/useNavigation.js +57 -0
  49. package/dist/src/tui/hooks/useNodes.js +44 -0
  50. package/dist/src/tui/index.js +4 -0
  51. 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
+ }