From e57e5e7226799bad8119283843880832283cde0a Mon Sep 17 00:00:00 2001
From: nrslib <38722970+nrslib@users.noreply.github.com>
Date: Mon, 2 Feb 2026 13:06:12 +0900
Subject: [PATCH] refactor
---
bin/takt | 2 +-
docs/data-flow-diagrams.md | 562 +++++++++
docs/data-flow.md | 1029 +++++++++++++++++
docs/vertical-slice-migration-map.md | 111 ++
package.json | 2 +-
src/__tests__/engine-abort.test.ts | 2 +-
src/__tests__/engine-agent-overrides.test.ts | 2 +-
src/__tests__/engine-blocked.test.ts | 2 +-
src/__tests__/engine-error.test.ts | 2 +-
src/__tests__/engine-happy-path.test.ts | 2 +-
src/__tests__/engine-parallel.test.ts | 2 +-
src/__tests__/engine-test-helpers.ts | 2 +-
src/__tests__/engine-worktree-report.test.ts | 4 +-
src/__tests__/instructionBuilder.test.ts | 4 +-
src/__tests__/it-error-recovery.test.ts | 2 +-
src/__tests__/it-instruction-builder.test.ts | 2 +-
src/__tests__/it-pipeline-modes.test.ts | 2 +-
src/__tests__/it-pipeline.test.ts | 2 +-
.../it-three-phase-execution.test.ts | 2 +-
src/__tests__/it-workflow-execution.test.ts | 2 +-
src/__tests__/it-workflow-patterns.test.ts | 2 +-
src/__tests__/parallel-logger.test.ts | 2 +-
src/__tests__/transitions.test.ts | 2 +-
src/cli.ts | 323 ------
src/cli/commands.ts | 81 ++
src/cli/helpers.ts | 62 +
src/cli/index.ts | 18 +
src/cli/program.ts | 89 ++
src/cli/routing.ts | 98 ++
src/models/agent.ts | 12 +-
src/models/config.ts | 16 +-
src/models/index.ts | 9 -
src/models/schemas.ts | 29 +
src/models/workflow.ts | 53 +-
src/workflow/engine/OptionsBuilder.ts | 2 +-
src/workflow/engine/ParallelRunner.ts | 6 +-
src/workflow/engine/StepExecutor.ts | 4 +-
src/workflow/engine/WorkflowEngine.ts | 8 +-
src/workflow/{ => engine}/blocked-handler.ts | 4 +-
src/workflow/{ => engine}/loop-detector.ts | 4 +-
src/workflow/{ => engine}/parallel-logger.ts | 2 +-
src/workflow/{ => engine}/phase-runner.ts | 12 +-
src/workflow/engine/state-manager.ts | 118 ++
src/workflow/{ => engine}/transitions.ts | 2 +-
src/workflow/{ => evaluation}/rule-utils.ts | 2 +-
src/workflow/index.ts | 20 +-
.../instruction/InstructionBuilder.ts | 8 +-
.../instruction/ReportInstructionBuilder.ts | 4 +-
.../instruction/StatusJudgmentBuilder.ts | 2 +-
src/workflow/instruction/escape.ts | 2 +-
.../{ => instruction}/instruction-context.ts | 2 +-
.../{ => instruction}/status-rules.ts | 2 +-
src/workflow/state-manager.ts | 75 --
53 files changed, 2270 insertions(+), 545 deletions(-)
create mode 100644 docs/data-flow-diagrams.md
create mode 100644 docs/data-flow.md
create mode 100644 docs/vertical-slice-migration-map.md
delete mode 100644 src/cli.ts
create mode 100644 src/cli/commands.ts
create mode 100644 src/cli/helpers.ts
create mode 100644 src/cli/index.ts
create mode 100644 src/cli/program.ts
create mode 100644 src/cli/routing.ts
rename src/workflow/{ => engine}/blocked-handler.ts (90%)
rename src/workflow/{ => engine}/loop-detector.ts (92%)
rename src/workflow/{ => engine}/parallel-logger.ts (98%)
rename src/workflow/{ => engine}/phase-runner.ts (88%)
create mode 100644 src/workflow/engine/state-manager.ts
rename src/workflow/{ => engine}/transitions.ts (97%)
rename src/workflow/{ => evaluation}/rule-utils.ts (90%)
rename src/workflow/{ => instruction}/instruction-context.ts (98%)
rename src/workflow/{ => instruction}/status-rules.ts (97%)
delete mode 100644 src/workflow/state-manager.ts
diff --git a/bin/takt b/bin/takt
index b479137..5b920e4 100755
--- a/bin/takt
+++ b/bin/takt
@@ -16,7 +16,7 @@ const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
// Import the actual CLI from dist
-const cliPath = join(__dirname, '..', 'dist', 'cli.js');
+const cliPath = join(__dirname, '..', 'dist', 'cli', 'index.js');
try {
await import(cliPath);
diff --git a/docs/data-flow-diagrams.md b/docs/data-flow-diagrams.md
new file mode 100644
index 0000000..1188dee
--- /dev/null
+++ b/docs/data-flow-diagrams.md
@@ -0,0 +1,562 @@
+# TAKTデータフロー図解
+
+このドキュメントでは、TAKTのデータフローをMermaid図で可視化します。
+
+## 目次
+
+1. [シーケンス図: インタラクティブモードからワークフロー実行まで](#シーケンス図-インタラクティブモードからワークフロー実行まで)
+2. [フローチャート: 3フェーズステップ実行](#フローチャート-3フェーズステップ実行)
+3. [フローチャート: ルール評価の5段階フォールバック](#フローチャート-ルール評価の5段階フォールバック)
+4. [ステートマシン図: WorkflowEngineのステップ遷移](#ステートマシン図-workflowengineのステップ遷移)
+
+---
+
+## シーケンス図: インタラクティブモードからワークフロー実行まで
+
+```mermaid
+sequenceDiagram
+ participant User
+ participant CLI as CLI Layer
+ participant Interactive as Interactive Layer
+ participant Orchestration as Execution Orchestration
+ participant TaskExec as Task Execution
+ participant WorkflowExec as Workflow Execution
+ participant Engine as WorkflowEngine
+ participant StepExec as StepExecutor
+ participant Provider as Provider Layer
+
+ User->>CLI: takt (短い入力 or 引数なし)
+ CLI->>Interactive: interactiveMode(cwd, initialInput?)
+
+ loop 会話ループ
+ Interactive->>User: プロンプト表示
+ User->>Interactive: メッセージ入力
+ Interactive->>Provider: callAI(prompt)
+ Provider-->>Interactive: AIレスポンス
+ Interactive->>User: AIレスポンス表示
+ end
+
+ User->>Interactive: /go コマンド
+ Interactive->>Interactive: buildTaskFromHistory()
+ Interactive-->>CLI: { confirmed: true, task: string }
+
+ CLI->>Orchestration: selectAndExecuteTask(cwd, task)
+
+ Orchestration->>Orchestration: determineWorkflow()
+ Note over Orchestration: ワークフロー選択
(interactive or override)
+
+ Orchestration->>Orchestration: confirmAndCreateWorktree()
+ Orchestration->>Provider: summarizeTaskName(task)
+ Provider-->>Orchestration: taskSlug
+ Orchestration->>Orchestration: createSharedClone()
+
+ Orchestration->>TaskExec: executeTask(options)
+ TaskExec->>TaskExec: loadWorkflowByIdentifier()
+ TaskExec->>WorkflowExec: executeWorkflow(config, task, cwd)
+
+ WorkflowExec->>WorkflowExec: セッション管理初期化
+ Note over WorkflowExec: loadAgentSessions()
generateSessionId()
initNdjsonLog()
+
+ WorkflowExec->>Engine: new WorkflowEngine(config, cwd, task, options)
+ WorkflowExec->>Engine: イベント購読 (step:start, step:complete, etc.)
+ WorkflowExec->>Engine: engine.run()
+
+ loop ワークフローステップ
+ Engine->>StepExec: runStep(step)
+
+ StepExec->>StepExec: InstructionBuilder.build()
+ Note over StepExec: コンテキスト → インストラクション
+
+ StepExec->>Provider: runAgent(instruction)
+ Note over Provider: Phase 1: Main Execution
+ Provider-->>StepExec: AgentResponse
+
+ opt step.report 定義あり
+ StepExec->>Provider: runReportPhase()
+ Note over Provider: Phase 2: Report Output
(Write-only)
+ end
+
+ opt tag-based rules あり
+ StepExec->>Provider: runStatusJudgmentPhase()
+ Note over Provider: Phase 3: Status Judgment
(no tools)
+ Provider-->>StepExec: tagContent
+ end
+
+ StepExec->>StepExec: detectMatchedRule()
+ Note over StepExec: ルール評価
(5段階フォールバック)
+
+ StepExec-->>Engine: { response, instruction }
+ Engine->>Engine: resolveNextStep()
+
+ alt nextStep === COMPLETE
+ Engine-->>WorkflowExec: ワークフロー完了
+ else nextStep === ABORT
+ Engine-->>WorkflowExec: ワークフロー中断
+ else 通常ステップ
+ Engine->>Engine: state.currentStep = nextStep
+ end
+ end
+
+ WorkflowExec-->>TaskExec: { success: boolean }
+ TaskExec-->>Orchestration: taskSuccess
+
+ opt taskSuccess && isWorktree
+ Orchestration->>Orchestration: autoCommitAndPush()
+ opt autoPr or user confirms
+ Orchestration->>Orchestration: createPullRequest()
+ end
+ end
+
+ Orchestration-->>User: タスク完了
+```
+
+---
+
+## フローチャート: 3フェーズステップ実行
+
+```mermaid
+flowchart TD
+ Start([ステップ実行開始]) --> BuildInstruction[InstructionBuilder.build]
+ BuildInstruction --> Phase1{Phase 1:
Main Execution}
+
+ Phase1 --> ContextBuild[コンテキスト収集]
+ ContextBuild --> |7セクション自動注入| AssemblePrompt[プロンプト組み立て]
+ AssemblePrompt --> |プレースホルダー置換| CompleteInstruction[完全なインストラクション]
+
+ CompleteInstruction --> RunAgent[runAgent]
+ RunAgent --> ProviderCall[provider.call]
+ ProviderCall --> |onStream callback| StreamUI[UI表示]
+ ProviderCall --> Response1[AgentResponse]
+
+ Response1 --> CheckReport{step.report
定義あり?}
+ CheckReport -->|Yes| Phase2[Phase 2:
Report Output]
+ CheckReport -->|No| CheckTag{tag-based
rules あり?}
+
+ Phase2 --> ResumeSession1[セッション継続
sessionId同じ]
+ ResumeSession1 --> ReportBuilder[ReportInstructionBuilder.build]
+ ReportBuilder --> WriteOnly[Write-only tools]
+ WriteOnly --> RunReport[runAgent
レポート出力]
+ RunReport --> CheckTag
+
+ CheckTag -->|Yes| Phase3[Phase 3:
Status Judgment]
+ CheckTag -->|No| RuleEval[detectMatchedRule]
+
+ Phase3 --> ResumeSession2[セッション継続
sessionId同じ]
+ ResumeSession2 --> StatusBuilder[StatusJudgmentBuilder.build]
+ StatusBuilder --> NoTools[Tools: なし
判断のみ]
+ NoTools --> RunStatus[runAgent
ステータス出力]
+ RunStatus --> TagContent[tagContent:
STEP:N タグ]
+
+ TagContent --> RuleEval
+ RuleEval --> FiveStageFallback[5段階フォールバック]
+
+ FiveStageFallback --> Stage1{1. Aggregate?}
+ Stage1 -->|Yes| AllAny[all/any 評価]
+ Stage1 -->|No| Stage2{2. Phase 3 tag?}
+
+ AllAny --> Matched[マッチ!]
+
+ Stage2 -->|Yes| Phase3Tag[STEP:N from
status judgment]
+ Stage2 -->|No| Stage3{3. Phase 1 tag?}
+
+ Phase3Tag --> Matched
+
+ Stage3 -->|Yes| Phase1Tag[STEP:N from
main output]
+ Stage3 -->|No| Stage4{4. AI judge
ai rules?}
+
+ Phase1Tag --> Matched
+
+ Stage4 -->|Yes| AIJudge[AI evaluates
ai conditions]
+ Stage4 -->|No| Stage5[5. AI judge
fallback]
+
+ AIJudge --> Matched
+ Stage5 --> AIFallback[AI evaluates
all conditions]
+ AIFallback --> Matched
+
+ Matched --> UpdateResponse[response.matchedRuleIndex
response.matchedRuleMethod]
+ UpdateResponse --> StoreOutput[state.stepOutputs.set]
+ StoreOutput --> End([ステップ完了])
+
+ style Phase1 fill:#e1f5ff
+ style Phase2 fill:#fff4e6
+ style Phase3 fill:#f3e5f5
+ style Matched fill:#c8e6c9
+```
+
+---
+
+## フローチャート: ルール評価の5段階フォールバック
+
+```mermaid
+flowchart TD
+ Start([ルール評価開始]) --> Input[入力:
step, content, tagContent]
+
+ Input --> Stage1{Stage 1:
Aggregate評価
親ステップ?}
+ Stage1 -->|Yes| CheckAggregate{rules に
allまたはanyあり?}
+ CheckAggregate -->|Yes| EvalAggregate[AggregateEvaluator]
+ EvalAggregate --> CheckAggResult{マッチした?}
+ CheckAggResult -->|Yes| ReturnAgg[method: aggregate
返却]
+ CheckAggResult -->|No| Stage2
+
+ CheckAggregate -->|No| Stage2
+ Stage1 -->|No| Stage2{Stage 2:
Phase 3 tag
tagContent に
STEP:N あり?}
+
+ Stage2 -->|Yes| ExtractTag3[正規表現で抽出:
STEP:(\d+)]
+ ExtractTag3 --> ValidateIndex3{index が
rules 範囲内?}
+ ValidateIndex3 -->|Yes| ReturnTag3[method: phase3_tag
返却]
+ ValidateIndex3 -->|No| Stage3
+
+ Stage2 -->|No| Stage3{Stage 3:
Phase 1 tag
content に
STEP:N あり?}
+
+ Stage3 -->|Yes| ExtractTag1[正規表現で抽出:
STEP:(\d+)]
+ ExtractTag1 --> ValidateIndex1{index が
rules 範囲内?}
+ ValidateIndex1 -->|Yes| ReturnTag1[method: phase1_tag
返却]
+ ValidateIndex1 -->|No| Stage4
+
+ Stage3 -->|No| Stage4{Stage 4:
AI judge
ai rules あり?}
+
+ Stage4 -->|Yes| FilterAI[aiルールのみ抽出
ai 関数パース]
+ FilterAI --> CallAI[AIJudgeEvaluator
condition を評価]
+ CallAI --> CheckAIResult{マッチした?}
+ CheckAIResult -->|Yes| ReturnAI[method: ai_judge
返却]
+ CheckAIResult -->|No| Stage5
+
+ Stage4 -->|No| Stage5[Stage 5:
AI judge fallback
全条件を評価]
+
+ Stage5 --> AllConditions[全ルール条件を収集]
+ AllConditions --> CallAIFallback[AIJudgeEvaluator
全条件を評価]
+ CallAIFallback --> CheckFallbackResult{マッチした?}
+ CheckFallbackResult -->|Yes| ReturnFallback[method: ai_judge_fallback
返却]
+ CheckFallbackResult -->|No| NoMatch[null 返却
マッチなし]
+
+ ReturnAgg --> End([返却:
index, method])
+ ReturnTag3 --> End
+ ReturnTag1 --> End
+ ReturnAI --> End
+ ReturnFallback --> End
+ NoMatch --> End
+
+ style Stage1 fill:#e3f2fd
+ style Stage2 fill:#fff3e0
+ style Stage3 fill:#fce4ec
+ style Stage4 fill:#f3e5f5
+ style Stage5 fill:#e8f5e9
+ style End fill:#c8e6c9
+ style NoMatch fill:#ffcdd2
+```
+
+---
+
+## ステートマシン図: WorkflowEngineのステップ遷移
+
+```mermaid
+stateDiagram-v2
+ [*] --> Initializing: new WorkflowEngine
+
+ Initializing --> Running: engine.run()
+ note right of Initializing
+ state = {
+ status: 'running',
+ currentStep: initialStep,
+ iteration: 0,
+ ...
+ }
+ end note
+
+ state Running {
+ [*] --> CheckAbort: while loop
+
+ CheckAbort --> CheckIteration: abortRequested?
+ CheckAbort --> Aborted: Yes → abort
+
+ CheckIteration --> CheckLoop: iteration < max?
+ CheckIteration --> IterationLimit: No → emit iteration:limit
+
+ IterationLimit --> UserDecision: onIterationLimit callback
+ UserDecision --> CheckLoop: 追加イテレーション許可
+ UserDecision --> Aborted: 拒否
+
+ CheckLoop --> GetStep: loopDetector.check()
+ CheckLoop --> Aborted: loop detected
+
+ GetStep --> BuildInstruction: getStep(currentStep)
+
+ BuildInstruction --> EmitStart: InstructionBuilder
+
+ EmitStart --> RunStep: emit step:start
+
+ RunStep --> EmitComplete: runStep(step)
+ note right of RunStep
+ - Normal: StepExecutor
+ - Parallel: ParallelRunner
+ 3-phase execution
+ end note
+
+ EmitComplete --> CheckBlocked: emit step:complete
+
+ CheckBlocked --> HandleBlocked: status === blocked?
+ CheckBlocked --> EvaluateRules: No
+
+ HandleBlocked --> UserInput: handleBlocked()
+ UserInput --> CheckAbort: ユーザー入力追加
+ UserInput --> Aborted: キャンセル
+
+ EvaluateRules --> ResolveNext: detectMatchedRule()
+
+ ResolveNext --> CheckNext: determineNextStepByRules()
+
+ CheckNext --> Completed: nextStep === COMPLETE
+ CheckNext --> Aborted: nextStep === ABORT
+ CheckNext --> Transition: 通常ステップ
+
+ Transition --> CheckAbort: state.currentStep = nextStep
+ }
+
+ Running --> Completed: workflow:complete
+ Running --> Aborted: workflow:abort
+
+ Completed --> [*]: return state
+ Aborted --> [*]: return state
+
+ note right of Completed
+ state.status = 'completed'
+ emit workflow:complete
+ end note
+
+ note right of Aborted
+ state.status = 'aborted'
+ emit workflow:abort
+ 原因:
+ - User abort (Ctrl+C)
+ - Iteration limit
+ - Loop detected
+ - Blocked without input
+ - Step execution error
+ end note
+```
+
+---
+
+## データ変換の流れ
+
+```mermaid
+flowchart LR
+ subgraph Input ["入力"]
+ A1[ユーザー入力
CLI引数]
+ A2[会話履歴
ConversationMessage]
+ end
+
+ subgraph Transform1 ["変換1: タスク化"]
+ B1[isDirectTask判定]
+ B2[buildTaskFromHistory]
+ end
+
+ subgraph Task ["タスク"]
+ C[task: string]
+ end
+
+ subgraph Transform2 ["変換2: 環境準備"]
+ D1[determineWorkflow]
+ D2[summarizeTaskName
AI呼び出し]
+ D3[createSharedClone]
+ end
+
+ subgraph Execution ["実行環境"]
+ E1[workflowIdentifier]
+ E2[execCwd, branch]
+ end
+
+ subgraph Transform3 ["変換3: 設定読み込み"]
+ F1[loadWorkflowByIdentifier]
+ F2[loadAgentSessions]
+ end
+
+ subgraph Config ["設定"]
+ G1[WorkflowConfig]
+ G2[initialSessions]
+ end
+
+ subgraph Transform4 ["変換4: 状態初期化"]
+ H[createInitialState]
+ end
+
+ subgraph State ["実行状態"]
+ I[WorkflowState]
+ end
+
+ subgraph Transform5 ["変換5: インストラクション"]
+ J[InstructionBuilder.build]
+ end
+
+ subgraph Instruction ["プロンプト"]
+ K[instruction: string]
+ end
+
+ subgraph Transform6 ["変換6: AI実行"]
+ L[provider.call]
+ end
+
+ subgraph Response ["応答"]
+ M[AgentResponse]
+ end
+
+ subgraph Transform7 ["変換7: ルール評価"]
+ N[detectMatchedRule]
+ end
+
+ subgraph Transition ["遷移"]
+ O[nextStep: string]
+ end
+
+ A1 --> B1
+ A2 --> B2
+ B1 --> C
+ B2 --> C
+
+ C --> D1
+ C --> D2
+ D1 --> E1
+ D2 --> D3
+ D3 --> E2
+
+ E1 --> F1
+ E2 --> F2
+ F1 --> G1
+ F2 --> G2
+
+ G1 --> H
+ G2 --> H
+ H --> I
+
+ I --> J
+ C --> J
+ J --> K
+
+ K --> L
+ L --> M
+
+ M --> N
+ I --> N
+ N --> O
+
+ O -.-> I
+
+ style Input fill:#e3f2fd
+ style Task fill:#fff3e0
+ style Execution fill:#fce4ec
+ style Config fill:#f3e5f5
+ style State fill:#e8f5e9
+ style Instruction fill:#fff9c4
+ style Response fill:#f1f8e9
+ style Transition fill:#c8e6c9
+```
+
+---
+
+## コンテキスト蓄積の流れ
+
+```mermaid
+flowchart TB
+ subgraph Initial ["初期入力"]
+ A[task: string]
+ end
+
+ subgraph Context1 ["コンテキスト1: タスク"]
+ B[InstructionContext]
+ B1[task]
+ end
+
+ subgraph Step1 ["ステップ1実行"]
+ C1[Phase 1: Main]
+ C2[Phase 2: Report]
+ C3[Phase 3: Status]
+ C4[AgentResponse]
+ end
+
+ subgraph Context2 ["コンテキスト2: +前回応答"]
+ D[InstructionContext]
+ D1[task]
+ D2[previousOutput]
+ end
+
+ subgraph Blocked ["Blocked発生"]
+ E[handleBlocked]
+ E1[ユーザー追加入力]
+ end
+
+ subgraph Context3 ["コンテキスト3: +ユーザー入力"]
+ F[InstructionContext]
+ F1[task]
+ F2[previousOutput]
+ F3[userInputs]
+ end
+
+ subgraph Step2 ["ステップ2実行"]
+ G1[Phase 1: Main]
+ G2[stepOutputs蓄積]
+ G3[AgentResponse]
+ end
+
+ subgraph Context4 ["コンテキスト4: 完全"]
+ H[InstructionContext]
+ H1[task]
+ H2[previousOutput]
+ H3[userInputs]
+ H4[iteration]
+ H5[stepIteration]
+ H6[reportDir]
+ H7[...すべてのメタデータ]
+ end
+
+ A --> B1
+ B1 --> C1
+ C1 --> C2
+ C2 --> C3
+ C3 --> C4
+
+ C4 --> D2
+ B1 --> D1
+
+ D --> E
+ E --> E1
+ E1 --> F3
+ D1 --> F1
+ D2 --> F2
+
+ F --> G1
+ G1 --> G2
+ G2 --> G3
+
+ G3 --> H2
+ F1 --> H1
+ F3 --> H3
+ G2 --> H4
+ G2 --> H5
+ G2 --> H6
+
+ H -.繰り返し.-> Step2
+
+ style Initial fill:#e3f2fd
+ style Context1 fill:#fff3e0
+ style Step1 fill:#fce4ec
+ style Context2 fill:#fff9c4
+ style Blocked fill:#ffcdd2
+ style Context3 fill:#f1f8e9
+ style Step2 fill:#c8e6c9
+ style Context4 fill:#dcedc8
+```
+
+---
+
+## まとめ
+
+これらの図は、TAKTのデータフローを以下の視点から可視化しています:
+
+1. **シーケンス図**: 時系列での各レイヤー間のやりとり
+2. **3フェーズフローチャート**: ステップ実行の詳細な処理フロー
+3. **ルール評価フローチャート**: 5段階フォールバックの意思決定ロジック
+4. **ステートマシン**: WorkflowEngineの状態遷移
+5. **データ変換図**: 各段階でのデータ形式変換
+6. **コンテキスト蓄積図**: 実行が進むにつれてコンテキストが蓄積される様子
+
+これらの図を `data-flow.md` と合わせて参照することで、TAKTのアーキテクチャを多角的に理解できます。
diff --git a/docs/data-flow.md b/docs/data-flow.md
new file mode 100644
index 0000000..ec20d03
--- /dev/null
+++ b/docs/data-flow.md
@@ -0,0 +1,1029 @@
+# TAKTデータフロー解析
+
+このドキュメントでは、TAKTにおけるデータフロー、特にインタラクティブモードからワークフロー実行に至るまでのデータの流れを説明します。
+
+## 目次
+
+1. [概要](#概要)
+2. [全体フロー図](#全体フロー図)
+3. [各レイヤーの詳細](#各レイヤーの詳細)
+4. [データフローの段階](#データフローの段階)
+5. [重要な変換ポイント](#重要な変換ポイント)
+
+---
+
+## 概要
+
+TAKTのデータフローは以下の7つの主要なレイヤーで構成されています:
+
+1. **CLI Layer** - ユーザー入力の受付
+2. **Interactive Layer** - タスクの対話的な明確化
+3. **Execution Orchestration Layer** - ワークフロー選択とworktree管理
+4. **Workflow Execution Layer** - セッション管理とイベント処理
+5. **Engine Layer** - ステートマシンによるステップ実行
+6. **Instruction Building Layer** - プロンプト生成
+7. **Provider Layer** - AIプロバイダーとの通信
+
+---
+
+## 全体フロー図
+
+```
+┌─────────────────────────────────────────────────────────────────┐
+│ 1. CLI Layer (cli.ts) │
+│ ユーザー入力 → 引数パース → コマンド振り分け │
+└────────────────────────────┬────────────────────────────────────┘
+ │
+ ┌────────────┴──────────────┐
+ │ │
+ Direct Task Input Short Input / No Args
+ │ │
+ │ ▼
+ │ ┌─────────────────────────────────┐
+ │ │ 2. Interactive Layer │
+ │ │ (interactive.ts) │
+ │ │ │
+ │ │ ┌─────────────────────┐ │
+ │ │ │ User Conversation │ │
+ │ │ │ - Clarification │ │
+ │ │ │ - Codebase Search │ │
+ │ │ │ - AI Response │ │
+ │ │ └──────┬──────────────┘ │
+ │ │ │ │
+ │ │ ▼ │
+ │ │ User confirms with /go │
+ │ │ │ │
+ │ │ ▼ │
+ │ │ buildTaskFromHistory() │
+ │ │ (会話履歴 → タスク文字列) │
+ │ └─────────┬───────────────────────┘
+ │ │
+ └────────────────────────┘
+ │
+ │ task: string
+ ▼
+┌─────────────────────────────────────────────────────────────────┐
+│ 3. Execution Orchestration Layer │
+│ (selectAndExecute.ts) │
+│ │
+│ ┌──────────────────────┐ │
+│ │ determineWorkflow() │ ← workflow選択 (interactive/override) │
+│ └─────────┬────────────┘ │
+│ │ workflowIdentifier: string │
+│ ▼ │
+│ ┌──────────────────────────────────┐ │
+│ │ confirmAndCreateWorktree() │ │
+│ │ - AI branchname generation │ │
+│ │ - createSharedClone() │ │
+│ └─────────┬────────────────────────┘ │
+│ │ { execCwd, isWorktree, branch } │
+│ ▼ │
+│ ┌──────────────────────────────────┐ │
+│ │ executeTask() │ │
+│ │ - task: string │ │
+│ │ - cwd: string (実行ディレクトリ) │ │
+│ │ - workflowIdentifier: string │ │
+│ │ - projectCwd: string (.takt/在処) │ │
+│ └─────────┬────────────────────────┘ │
+└────────────┼────────────────────────────────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────────────────────────┐
+│ 4. Workflow Execution Layer │
+│ (workflowExecution.ts, taskExecution.ts) │
+│ │
+│ ┌────────────────────────────────┐ │
+│ │ loadWorkflowByIdentifier() │ │
+│ │ → WorkflowConfig │ │
+│ └────────┬───────────────────────┘ │
+│ │ │
+│ ▼ │
+│ ┌────────────────────────────────┐ │
+│ │ Session Management │ │
+│ │ - loadAgentSessions() │ ← projectCwd or cwd │
+│ │ - generateSessionId() │ │
+│ │ - createSessionLog() │ │
+│ │ - initNdjsonLog() │ │
+│ └────────┬───────────────────────┘ │
+│ │ │
+│ ▼ │
+│ ┌────────────────────────────────┐ │
+│ │ WorkflowEngine initialization │ │
+│ │ │ │
+│ │ new WorkflowEngine( │ │
+│ │ config: WorkflowConfig, │ │
+│ │ cwd: string, │ │
+│ │ task: string, │ │
+│ │ options: { │ │
+│ │ onStream, │ ← StreamDisplay handler │
+│ │ initialSessions, │ ← 保存済みセッションID │
+│ │ onSessionUpdate, │ ← セッション更新callback │
+│ │ projectCwd, │ │
+│ │ language, │ │
+│ │ provider, │ │
+│ │ model │ │
+│ │ } │ │
+│ │ ) │ │
+│ └────────┬───────────────────────┘ │
+│ │ │
+│ ▼ │
+│ ┌────────────────────────────────┐ │
+│ │ Event Subscription │ │
+│ │ - step:start │ │
+│ │ - step:complete │ │
+│ │ - step:report │ │
+│ │ - workflow:complete │ │
+│ │ - workflow:abort │ │
+│ └────────┬───────────────────────┘ │
+│ │ │
+│ ▼ │
+│ ┌────────────────────────────────┐ │
+│ │ engine.run() │ │
+│ └────────┬───────────────────────┘ │
+└───────────┼─────────────────────────────────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────────────────────────┐
+│ 5. Engine Layer (WorkflowEngine.ts) │
+│ │
+│ ┌────────────────────────────────────────┐ │
+│ │ State Machine Loop │ │
+│ │ │ │
+│ │ while (state.status === 'running') { │ │
+│ │ ┌────────────────────────────────┐ │ │
+│ │ │ 1. Iteration & Loop Check │ │ │
+│ │ └────────────┬───────────────────┘ │ │
+│ │ │ │ │
+│ │ ▼ │ │
+│ │ ┌────────────────────────────────┐ │ │
+│ │ │ 2. Get Current Step │ │ │
+│ │ │ step = getStep( │ │ │
+│ │ │ state.currentStep │ │ │
+│ │ │ ) │ │ │
+│ │ └────────────┬───────────────────┘ │ │
+│ │ │ │ │
+│ │ ▼ │ │
+│ │ ┌────────────────────────────────┐ │ │
+│ │ │ 3. Build Instruction │ │ ← InstructionBuilder
+│ │ │ (if not parallel) │ │ │
+│ │ └────────────┬───────────────────┘ │ │
+│ │ │ │ │
+│ │ ▼ │ │
+│ │ ┌────────────────────────────────┐ │ │
+│ │ │ 4. Emit step:start │ │ │
+│ │ └────────────┬───────────────────┘ │ │
+│ │ │ │ │
+│ │ ▼ │ │
+│ │ ┌────────────────────────────────┐ │ │
+│ │ │ 5. runStep() │ │ │
+│ │ │ ├─ Normal: StepExecutor │ │ ← 3-phase execution
+│ │ │ └─ Parallel: ParallelRunner │ │ │
+│ │ └────────────┬───────────────────┘ │ │
+│ │ │ │ │
+│ │ │ { response, instruction } │
+│ │ ▼ │ │
+│ │ ┌────────────────────────────────┐ │ │
+│ │ │ 6. Emit step:complete │ │ │
+│ │ └────────────┬───────────────────┘ │ │
+│ │ │ │ │
+│ │ ▼ │ │
+│ │ ┌────────────────────────────────┐ │ │
+│ │ │ 7. Handle Blocked │ │ │
+│ │ │ (if status === 'blocked') │ │ │
+│ │ └────────────┬───────────────────┘ │ │
+│ │ │ │ │
+│ │ ▼ │ │
+│ │ ┌────────────────────────────────┐ │ │
+│ │ │ 8. Rule Evaluation │ │ ← RuleEvaluator │
+│ │ │ resolveNextStep() │ │ │
+│ │ └────────────┬───────────────────┘ │ │
+│ │ │ │ │
+│ │ │ nextStep: string │ │
+│ │ ▼ │ │
+│ │ ┌────────────────────────────────┐ │ │
+│ │ │ 9. Transition │ │ │
+│ │ │ - COMPLETE → break │ │ │
+│ │ │ - ABORT → break │ │ │
+│ │ │ - other → update state │ │ │
+│ │ └────────────────────────────────┘ │ │
+│ │ } │ │
+│ └────────────────────────────────────────┘ │
+└─────────────────────────────────────────────────────────────────┘
+ │
+ ▼ (from runStep)
+┌─────────────────────────────────────────────────────────────────┐
+│ 6. Instruction Building & Step Execution Layer │
+│ │
+│ ┌────────────────────────────────────────────────────────────┐│
+│ │ StepExecutor.runNormalStep() ││
+│ │ ││
+│ │ ┌──────────────────────────────────────────────────────┐ ││
+│ │ │ Phase 1: Main Execution │ ││
+│ │ │ │ ││
+│ │ │ InstructionBuilder.build() │ ││
+│ │ │ ├─ Execution Context (cwd, permission) │ ││
+│ │ │ ├─ Workflow Context (iteration, step, report) │ ││
+│ │ │ ├─ User Request ({task}) │ ││
+│ │ │ ├─ Previous Response ({previous_response}) │ ││
+│ │ │ ├─ Additional User Inputs ({user_inputs}) │ ││
+│ │ │ ├─ Instructions (instruction_template) │ ││
+│ │ │ └─ Status Output Rules (tag-based) │ ││
+│ │ │ │ ││
+│ │ │ → instruction: string │ ││
+│ │ │ │ ││
+│ │ │ runAgent(agent, instruction, options) │ ││
+│ │ │ → response: AgentResponse │ ││
+│ │ └──────────────────────┬───────────────────────────────┘ ││
+│ │ │ ││
+│ │ ▼ ││
+│ │ ┌──────────────────────────────────────────────────────┐ ││
+│ │ │ Phase 2: Report Output (if step.report defined) │ ││
+│ │ │ │ ││
+│ │ │ runReportPhase() │ ││
+│ │ │ - Resume session │ ││
+│ │ │ - Write-only tools │ ││
+│ │ │ - ReportInstructionBuilder │ ││
+│ │ └──────────────────────┬───────────────────────────────┘ ││
+│ │ │ ││
+│ │ ▼ ││
+│ │ ┌──────────────────────────────────────────────────────┐ ││
+│ │ │ Phase 3: Status Judgment (if tag-based rules) │ ││
+│ │ │ │ ││
+│ │ │ runStatusJudgmentPhase() │ ││
+│ │ │ - Resume session │ ││
+│ │ │ - No tools (judgment only) │ ││
+│ │ │ - StatusJudgmentBuilder │ ││
+│ │ │ → tagContent: string │ ││
+│ │ └──────────────────────┬───────────────────────────────┘ ││
+│ │ │ ││
+│ │ ▼ ││
+│ │ ┌──────────────────────────────────────────────────────┐ ││
+│ │ │ Rule Evaluation │ ││
+│ │ │ │ ││
+│ │ │ detectMatchedRule(step, content, tagContent) │ ││
+│ │ │ 1. Aggregate (all()/any()) │ ││
+│ │ │ 2. Phase 3 tag ([STEP:N]) │ ││
+│ │ │ 3. Phase 1 tag (fallback) │ ││
+│ │ │ 4. AI judge (ai("...")) │ ││
+│ │ │ 5. AI judge fallback (all conditions) │ ││
+│ │ │ → { index, method } │ ││
+│ │ └──────────────────────┬───────────────────────────────┘ ││
+│ │ │ ││
+│ │ ▼ ││
+│ │ response with matchedRuleIndex & matchedRuleMethod ││
+│ └────────────────────────────────────────────────────────────┘│
+└────────────────────────────┬────────────────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────────────────────────┐
+│ 7. Provider Layer (agents/runner.ts → providers/) │
+│ │
+│ ┌────────────────────────────────────────┐ │
+│ │ runAgent() │ │
+│ │ - Resolve agent spec │ │
+│ │ - Get provider │ │
+│ │ - Call provider.call() │ │
+│ └────────────┬───────────────────────────┘ │
+│ │ │
+│ ▼ │
+│ ┌────────────────────────────────────────┐ │
+│ │ Provider.call() │ │
+│ │ (ClaudeProvider / CodexProvider) │ │
+│ │ │ │
+│ │ - Build system prompt │ │
+│ │ - Call SDK (callClaude / callCodex) │ │
+│ │ - Stream handling (onStream callback) │ │
+│ │ - Error propagation │ │
+│ │ │ │
+│ │ → { status, content, sessionId, ... } │ │
+│ └────────────────────────────────────────┘ │
+└─────────────────────────────────────────────────────────────────┘
+```
+
+---
+
+## 各レイヤーの詳細
+
+### 1. CLI Layer (`src/cli.ts`)
+
+**役割**: ユーザー入力の受付とコマンド振り分け
+
+**主要な処理**:
+- コマンドライン引数のパース
+- 入力タイプの判定:
+ - `isDirectTask()`: 複数単語またはissue参照 → 直接実行
+ - 短い単語または引数なし → インタラクティブモード
+- グローバル設定の初期化 (`initGlobalDirs`, `initProjectDirs`)
+- パイプラインモード vs 通常モードの判定
+
+**データ入力**:
+- CLI引数: `task`, `--workflow`, `--issue`, など
+
+**データ出力**:
+- `task: string` (タスク記述)
+- `workflow: string | undefined` (ワークフロー名またはパス)
+- `createWorktree: boolean | undefined`
+- その他オプション
+
+---
+
+### 2. Interactive Layer (`src/commands/interactive/interactive.ts`)
+
+**役割**: タスクの対話的な明確化
+
+**主要な処理**:
+1. **会話ループ**:
+ - `readLine()`: ユーザー入力を1行ずつ読み込み
+ - `callAI()`: AIプロバイダーを呼び出し
+ - 履歴管理: `ConversationMessage[]`
+
+2. **セッション管理**:
+ - `loadAgentSessions()`: 過去のセッションを復元
+ - `updateAgentSession()`: セッションIDを更新・保存
+
+3. **スラッシュコマンド**:
+ - `/go`: タスク確定、実行へ進む
+ - `/cancel`: キャンセル
+ - `Ctrl+D`: EOF、キャンセル
+
+4. **タスク組み立て**:
+ - `buildTaskFromHistory()`: 会話履歴を結合してタスク文字列を生成
+
+**データ入力**:
+- `initialInput?: string` (CLI引数から)
+- ユーザーの対話入力
+
+**データ出力**:
+- `InteractiveModeResult`:
+ - `confirmed: boolean`
+ - `task: string` (会話履歴全体を結合した文字列)
+
+---
+
+### 3. Execution Orchestration Layer (`src/commands/execution/selectAndExecute.ts`)
+
+**役割**: ワークフロー選択とworktree管理
+
+**主要な処理**:
+
+1. **ワークフロー決定** (`determineWorkflow()`):
+ - オーバーライド指定がある場合:
+ - パス形式 → そのまま使用
+ - 名前形式 → バリデーション
+ - オーバーライドなし → インタラクティブ選択 (`selectWorkflow()`)
+
+2. **Worktree作成** (`confirmAndCreateWorktree()`):
+ - ユーザー確認 (または `--create-worktree` フラグ)
+ - ブランチ名生成 (`summarizeTaskName()` - AIでタスクから英語スラグ生成)
+ - `createSharedClone()`: git clone --shared で軽量クローン作成
+
+3. **タスク実行開始** (`selectAndExecuteTask()`):
+ - `executeTask()` を呼び出し
+ - 成功時: Auto-commit & Push
+ - PR作成 (オプション)
+
+**データ入力**:
+- `task: string`
+- `options?: SelectAndExecuteOptions`:
+ - `workflow?: string`
+ - `createWorktree?: boolean`
+ - `autoPr?: boolean`
+- `agentOverrides?: TaskExecutionOptions`
+
+**データ出力**:
+- `{ execCwd, isWorktree, branch }`
+- タスク実行成功/失敗
+
+---
+
+### 4. Workflow Execution Layer
+
+#### 4.1 Task Execution (`src/commands/execution/taskExecution.ts`)
+
+**役割**: ワークフロー読み込みと実行の橋渡し
+
+**主要な処理**:
+1. `loadWorkflowByIdentifier()`: YAMLまたは名前からワークフロー設定を読み込み
+2. `executeWorkflow()` を呼び出し
+
+**データ入力**:
+- `ExecuteTaskOptions`:
+ - `task: string`
+ - `cwd: string` (実行ディレクトリ、cloneまたはプロジェクトルート)
+ - `workflowIdentifier: string`
+ - `projectCwd: string` (`.takt/`がある場所)
+ - `agentOverrides?: TaskExecutionOptions`
+
+**データ出力**:
+- `boolean` (成功/失敗)
+
+#### 4.2 Workflow Execution (`src/commands/execution/workflowExecution.ts`)
+
+**役割**: セッション管理、イベント購読、ログ記録
+
+**主要な処理**:
+
+1. **セッション管理**:
+ - `generateSessionId()`: ワークフローセッションID生成
+ - `loadAgentSessions()` / `loadWorktreeSessions()`: エージェントセッション復元
+ - `updateAgentSession()` / `updateWorktreeSession()`: セッション保存
+
+2. **ログ初期化**:
+ - `createSessionLog()`: セッションログオブジェクト作成
+ - `initNdjsonLog()`: NDJSON形式のログファイル初期化
+ - `updateLatestPointer()`: `latest.json` ポインタ更新
+
+3. **WorkflowEngine初期化**:
+ ```typescript
+ new WorkflowEngine(workflowConfig, cwd, task, {
+ onStream: streamHandler, // UI表示用ストリームハンドラ
+ initialSessions: savedSessions, // 保存済みセッションID
+ onSessionUpdate: sessionUpdateHandler,
+ onIterationLimit: iterationLimitHandler,
+ projectCwd,
+ language,
+ provider,
+ model
+ })
+ ```
+
+4. **イベント購読**:
+ - `step:start`: ステップ開始 → UI表示、NDJSON記録
+ - `step:complete`: ステップ完了 → UI表示、NDJSON記録、セッション更新
+ - `step:report`: レポートファイル出力
+ - `workflow:complete`: ワークフロー完了 → 通知
+ - `workflow:abort`: ワークフロー中断 → エラー通知
+
+5. **SIGINT処理**:
+ - 1回目: Graceful abort (`engine.abort()`)
+ - 2回目: 強制終了
+
+**データ入力**:
+- `WorkflowConfig`
+- `task: string`
+- `cwd: string`
+- `WorkflowExecutionOptions`
+
+**データ出力**:
+- `WorkflowExecutionResult`:
+ - `success: boolean`
+ - `reason?: string`
+
+---
+
+### 5. Engine Layer (`src/workflow/engine/WorkflowEngine.ts`)
+
+**役割**: ステートマシンによるワークフロー実行制御
+
+**主要な構成要素**:
+
+1. **State管理** (`WorkflowState`):
+ - `status`: 'running' | 'completed' | 'aborted'
+ - `currentStep`: 現在実行中のステップ名
+ - `iteration`: ワークフロー全体のイテレーション数
+ - `stepIterations`: Map (ステップごとの実行回数)
+ - `agentSessions`: Map (エージェントごとのセッションID)
+ - `stepOutputs`: Map (各ステップの出力)
+ - `userInputs`: string[] (blocked時のユーザー追加入力)
+
+2. **コンポーネント**:
+ - `OptionsBuilder`: エージェント実行オプション構築
+ - `StepExecutor`: 通常ステップの3フェーズ実行
+ - `ParallelRunner`: 並列ステップの実行
+
+3. **主要メソッド**:
+
+ **`run()`**: メインループ
+ ```typescript
+ while (state.status === 'running') {
+ // 1. Abort & Iteration チェック
+ if (abortRequested) { ... }
+ if (iteration >= maxIterations) { ... }
+
+ // 2. ステップ取得
+ const step = getStep(state.currentStep);
+
+ // 3. ループ検出
+ const loopCheck = loopDetector.check(step.name);
+
+ // 4. インストラクション構築 (非並列の場合)
+ const instruction = stepExecutor.buildInstruction(...);
+
+ // 5. イベント発行
+ emit('step:start', step, iteration, instruction);
+
+ // 6. ステップ実行
+ const { response, instruction } = await runStep(step, instruction);
+
+ // 7. イベント発行
+ emit('step:complete', step, response, instruction);
+
+ // 8. Blocked処理
+ if (response.status === 'blocked') { ... }
+
+ // 9. ルール評価
+ const nextStep = resolveNextStep(step, response);
+
+ // 10. 遷移
+ if (nextStep === COMPLETE_STEP) { break; }
+ if (nextStep === ABORT_STEP) { break; }
+ state.currentStep = nextStep;
+ }
+ ```
+
+ **`runStep()`**: ステップ実行の委譲
+ - 並列ステップ → `ParallelRunner.runParallelStep()`
+ - 通常ステップ → `StepExecutor.runNormalStep()`
+
+ **`resolveNextStep()`**: ルール評価によるステップ遷移決定
+ - `response.matchedRuleIndex` を使用
+ - `determineNextStepByRules()` で次ステップ名を取得
+
+**データ入力**:
+- `WorkflowConfig`
+- `cwd: string`
+- `task: string`
+- `WorkflowEngineOptions`
+
+**データ出力**:
+- `WorkflowState` (最終状態)
+- イベント発行 (各ステップの進捗)
+
+---
+
+### 6. Instruction Building & Step Execution Layer
+
+#### 6.1 Step Execution (`src/workflow/engine/StepExecutor.ts`)
+
+**役割**: 3フェーズモデルによるステップ実行
+
+**3フェーズの詳細**:
+
+**Phase 1: Main Execution**
+- 目的: エージェントのメインタスク実行
+- Tools: ステップで指定されたツール (ただし `step.report` がある場合は Write を除外)
+- インストラクション: `InstructionBuilder.build()`
+
+**Phase 2: Report Output** (オプション、`step.report` がある場合のみ)
+- 目的: レポートファイルへの出力
+- Tools: **Writeのみ**
+- インストラクション: `ReportInstructionBuilder.build()`
+- セッション: Phase 1と同じセッションを継続 (resume)
+
+**Phase 3: Status Judgment** (オプション、tag-based rulesがある場合のみ)
+- 目的: ステータスタグの出力
+- Tools: **なし** (判断のみ)
+- インストラクション: `StatusJudgmentBuilder.build()`
+- セッション: Phase 1と同じセッションを継続 (resume)
+- 出力: `[STEP:N]` 形式のタグ
+
+**主要メソッド**:
+
+**`runNormalStep()`**:
+```typescript
+// Phase 1
+const response = await runAgent(step.agent, instruction, options);
+updateAgentSession(step.agent, response.sessionId);
+
+// Phase 2 (if step.report)
+if (step.report) {
+ await runReportPhase(step, stepIteration, context);
+}
+
+// Phase 3 (if tag-based rules)
+let tagContent = '';
+if (needsStatusJudgmentPhase(step)) {
+ tagContent = await runStatusJudgmentPhase(step, context);
+}
+
+// Rule evaluation
+const match = await detectMatchedRule(step, response.content, tagContent, {...});
+```
+
+**`buildInstruction()`**:
+- `InstructionBuilder` を使用してインストラクション文字列を生成
+- コンテキスト情報を渡す
+
+#### 6.2 Instruction Building (`src/workflow/instruction/InstructionBuilder.ts`)
+
+**役割**: Phase 1用のインストラクション文字列生成
+
+**自動注入セクション**:
+
+1. **Execution Context** (実行環境メタデータ):
+ - Working directory
+ - Permission rules (edit mode)
+
+2. **Workflow Context**:
+ - Iteration (workflow-wide)
+ - Step Iteration (per-step)
+ - Step name
+ - Report Directory/File info
+
+3. **User Request** (タスク本文):
+ - `{task}` プレースホルダーがテンプレートにない場合のみ自動注入
+
+4. **Previous Response** (前ステップの出力):
+ - `step.passPreviousResponse === true` かつ
+ - `{previous_response}` プレースホルダーがテンプレートにない場合のみ自動注入
+
+5. **Additional User Inputs** (blocked時の追加入力):
+ - `{user_inputs}` プレースホルダーがテンプレートにない場合のみ自動注入
+
+6. **Instructions** (ステップ固有のテンプレート):
+ - `step.instructionTemplate` の内容
+ - プレースホルダー置換: `{task}`, `{previous_response}`, `{iteration}`, など
+
+7. **Status Output Rules** (tag-based rules用):
+ - `hasTagBasedRules(step)` の場合のみ
+ - `generateStatusRulesFromRules()` で生成
+
+**プレースホルダー置換**:
+- `{task}`: ユーザーリクエスト
+- `{previous_response}`: 前ステップの出力
+- `{user_inputs}`: 追加ユーザー入力
+- `{iteration}`: ワークフロー全体のイテレーション
+- `{max_iterations}`: 最大イテレーション
+- `{step_iteration}`: ステップのイテレーション
+- `{report_dir}`: レポートディレクトリ
+
+**ロケール対応**:
+- `language: 'en' | 'ja'`
+- セクション見出しや説明文が言語に応じて切り替わる
+
+---
+
+### 7. Provider Layer
+
+#### 7.1 Agent Runner (`src/agents/runner.ts`)
+
+**役割**: エージェント仕様の解決とプロバイダー呼び出し
+
+**主要な処理**:
+1. **エージェント仕様解決**:
+ - ビルトインエージェント (`coder`, `architect`, など)
+ - カスタムエージェント (`.takt/agents.yaml`)
+ - プロンプトファイル (`.md`)
+
+2. **プロバイダー取得**:
+ - `getProvider(providerType)`: ClaudeProvider / CodexProvider / MockProvider
+
+3. **エージェント呼び出し**:
+ - `provider.call(agentName, instruction, options)`
+
+**データ入力**:
+- `agent: string` (エージェント名またはパス)
+- `instruction: string` (構築済みインストラクション)
+- `AgentRunOptions`:
+ - `cwd: string`
+ - `sessionId?: string`
+ - `allowedTools?: string[]`
+ - `provider?: ProviderType`
+ - `model?: string`
+ - `onStream?: StreamHandler`
+
+**データ出力**:
+- `AgentResponse`:
+ - `agent: string`
+ - `status: 'success' | 'blocked'`
+ - `content: string`
+ - `sessionId?: string`
+ - `error?: string`
+ - `timestamp: Date`
+
+#### 7.2 Provider (`src/providers/`)
+
+**役割**: AIプロバイダー(Claude, Codex)とのSDK通信
+
+**主要なプロバイダー**:
+- `ClaudeProvider`: Claude Code SDK (`@anthropic-ai/claude-agent-sdk`)
+- `CodexProvider`: Codex API
+- `MockProvider`: テスト用
+
+**主要メソッド**:
+
+**`call()`**:
+```typescript
+async call(
+ agentName: string,
+ instruction: string,
+ options: ProviderCallOptions
+): Promise
+```
+
+**処理内容**:
+1. システムプロンプト構築
+2. SDK呼び出し (`callClaude()` / `callCodex()`)
+3. ストリーミング処理 (`onStream` callback)
+4. エラーハンドリング
+5. レスポンス変換
+
+**データ入力**:
+- `agentName: string`
+- `instruction: string`
+- `ProviderCallOptions`:
+ - `cwd: string`
+ - `sessionId?: string`
+ - `systemPrompt?: string`
+ - `allowedTools?: string[]`
+ - `model?: string`
+ - `onStream?: StreamHandler`
+
+**データ出力**:
+- `AgentResponse` (上記と同じ)
+
+---
+
+## データフローの段階
+
+### ステージ1: タスク入力
+
+**入力方法**:
+1. **直接タスク**: `takt "Fix the login bug"`
+2. **Issue参照**: `takt #123`
+3. **インタラクティブモード**: `takt` または `takt a`
+
+**データ変換**:
+- インタラクティブモード: `ConversationMessage[]` → `task: string`
+ - `buildTaskFromHistory()`: 会話履歴を結合
+
+**出力**: `task: string`
+
+---
+
+### ステージ2: 実行環境準備
+
+**ワークフロー選択**:
+- `--workflow` フラグ → 検証
+- なし → インタラクティブ選択 (`selectWorkflow()`)
+
+**Worktree作成** (オプション):
+- `confirmAndCreateWorktree()`:
+ - ユーザー確認または `--create-worktree` フラグ
+ - `summarizeTaskName()`: タスク → 英語スラグ (AI呼び出し)
+ - `createSharedClone()`: git clone --shared
+
+**データ**:
+- `workflowIdentifier: string`
+- `{ execCwd, isWorktree, branch }`
+
+---
+
+### ステージ3: ワークフロー実行初期化
+
+**セッション管理**:
+- `loadAgentSessions()`: 保存済みセッション復元
+- `generateSessionId()`: ワークフローセッションID生成
+- `initNdjsonLog()`: NDJSON ログファイル作成
+
+**WorkflowEngine作成**:
+```typescript
+new WorkflowEngine(workflowConfig, cwd, task, {
+ onStream,
+ initialSessions,
+ onSessionUpdate,
+ projectCwd,
+ language,
+ provider,
+ model
+})
+```
+
+**データ**:
+- `WorkflowState`: 初期状態
+ - `currentStep = config.initialStep`
+ - `iteration = 0`
+ - `agentSessions = initialSessions`
+
+---
+
+### ステージ4: ステップ実行ループ
+
+**各イテレーション**:
+
+1. **ステップ取得**: `getStep(state.currentStep)`
+2. **インストラクション構築**: `InstructionBuilder.build()`
+3. **ステップ実行**: 3フェーズ実行
+4. **ルール評価**: `detectMatchedRule()`
+5. **ステップ遷移**: `resolveNextStep()` → 次のステップ名
+
+**データ変換**:
+- `task + context` → `instruction: string`
+- `instruction` → `AgentResponse` (via Provider)
+- `AgentResponse + rules` → `matchedRuleIndex`
+- `matchedRuleIndex` → `nextStep: string`
+
+---
+
+### ステージ5: インストラクション生成
+
+**InstructionBuilder処理**:
+
+1. **コンテキスト収集**:
+ - `task`: 元のユーザーリクエスト
+ - `iteration`, `maxIterations`: イテレーション情報
+ - `stepIteration`: ステップごとの実行回数
+ - `cwd`, `projectCwd`: ディレクトリ情報
+ - `userInputs`: blocked時の追加入力
+ - `previousOutput`: 前ステップの出力
+ - `reportDir`: レポートディレクトリ
+
+2. **セクション組み立て**:
+ - 自動注入セクション (上記7つ)
+ - プレースホルダー置換
+
+3. **出力**: 完全なインストラクション文字列
+
+---
+
+### ステージ6: エージェント実行
+
+**Phase 1: Main Execution**:
+- `runAgent()` → `provider.call()`
+- ストリーミング → `onStream` callback → UI表示
+- 結果: `AgentResponse`
+
+**Phase 2: Report Output** (オプション):
+- 同じセッションを継続 (resume)
+- Write-only ツール
+- レポートファイル出力
+
+**Phase 3: Status Judgment** (オプション):
+- 同じセッションを継続 (resume)
+- ツールなし
+- `[STEP:N]` タグ出力
+
+---
+
+### ステージ7: ルール評価と遷移
+
+**ルール評価** (`detectMatchedRule()`):
+
+5段階のフォールバック:
+1. **Aggregate** (`all()`/`any()`) - 並列ステップ用
+2. **Phase 3 tag** - `[STEP:N]` from status judgment
+3. **Phase 1 tag** - `[STEP:N]` from main output (fallback)
+4. **AI judge** - `ai("condition text")` rules
+5. **AI judge fallback** - すべての条件をAIで評価
+
+**出力**: `{ index: number, method: RuleMatchMethod }`
+
+**遷移**:
+- `determineNextStepByRules()`: `rules[index].next` を取得
+- 特殊ステップ:
+ - `COMPLETE`: ワークフロー完了
+ - `ABORT`: ワークフロー中断
+- 通常ステップ: `state.currentStep = nextStep`
+
+---
+
+## 重要な変換ポイント
+
+### 1. 会話履歴 → タスク文字列
+
+**場所**: `src/commands/interactive/interactive.ts`
+
+```typescript
+function buildTaskFromHistory(history: ConversationMessage[]): string {
+ return history
+ .map((msg) => `${msg.role === 'user' ? 'User' : 'Assistant'}: ${msg.content}`)
+ .join('\n\n');
+}
+```
+
+**重要性**: インタラクティブモードで蓄積された会話全体が、後続のワークフロー実行で単一の `task` 文字列として扱われる。
+
+---
+
+### 2. タスク → ブランチスラグ (AI生成)
+
+**場所**: `src/task/summarize.ts` (呼び出し: `selectAndExecute.ts`, `taskExecution.ts`)
+
+```typescript
+await summarizeTaskName(task, { cwd })
+```
+
+**処理**:
+- タスク文字列をAIに渡す
+- 英語の短いスラグに要約 (例: `fix-login-bug`)
+- ブランチ名として使用
+
+**重要性**: ユーザーが日本語でタスクを書いても、Git-friendlyなブランチ名が自動生成される。
+
+---
+
+### 3. ワークフロー設定 → WorkflowState
+
+**場所**: `src/workflow/state-manager.ts`
+
+```typescript
+function createInitialState(
+ config: WorkflowConfig,
+ options: WorkflowEngineOptions
+): WorkflowState {
+ return {
+ status: 'running',
+ currentStep: config.initialStep,
+ iteration: 0,
+ stepIterations: new Map(),
+ agentSessions: new Map(Object.entries(options.initialSessions ?? {})),
+ stepOutputs: new Map(),
+ userInputs: [],
+ };
+}
+```
+
+**重要性**: YAMLで定義された静的な設定が、実行時のミュータブルな状態に変換される。
+
+---
+
+### 4. コンテキスト → インストラクション文字列
+
+**場所**: `src/workflow/instruction/InstructionBuilder.ts`
+
+**入力**:
+- `step: WorkflowStep`
+- `context: InstructionContext` (task, iteration, previousOutput, userInputs, など)
+
+**処理**:
+1. 7つのセクションを組み立て
+2. プレースホルダー置換
+3. ロケール対応
+
+**出力**: 完全なMarkdown形式のインストラクション文字列
+
+**重要性**: 散在するコンテキスト情報が、エージェントが理解できる単一の文字列に統合される。
+
+---
+
+### 5. AgentResponse → ルールマッチ
+
+**場所**: `src/workflow/evaluation/RuleEvaluator.ts`
+
+**入力**:
+- `step: WorkflowStep`
+- `content: string` (Phase 1 output)
+- `tagContent: string` (Phase 3 output)
+- `state: WorkflowState`
+
+**処理**:
+1. タグ検出 (`[STEP:0]`, `[STEP:1]`, ...)
+2. AI判断 (`ai("condition")` ルール)
+3. 集約評価 (`all()`, `any()`)
+
+**出力**: `{ index: number, method: RuleMatchMethod } | null`
+
+**重要性**: 自然言語の出力が、構造化されたステップ遷移決定に変換される。
+
+---
+
+### 6. ルールマッチ → 次ステップ名
+
+**場所**: `src/workflow/transitions.ts`
+
+```typescript
+function determineNextStepByRules(
+ step: WorkflowStep,
+ matchedRuleIndex: number
+): string | null {
+ const rule = step.rules?.[matchedRuleIndex];
+ return rule?.next ?? null;
+}
+```
+
+**重要性**: インデックス番号が、実際に実行すべきステップ名に変換される。
+
+---
+
+### 7. Provider Response → AgentResponse
+
+**場所**: `src/providers/claude.ts`, `src/providers/codex.ts`
+
+**入力**: SDKレスポンス (`ClaudeResult`)
+
+**処理**:
+- `status` 変換
+- `content` 抽出
+- `error` 伝播 (重要!)
+- `sessionId` 保存
+
+**出力**: `AgentResponse` (統一インターフェース)
+
+**重要性**: 異なるプロバイダーのレスポンスが統一形式に正規化される。
+
+---
+
+## まとめ
+
+TAKTのデータフローは、**7つのレイヤー**を通じて、ユーザーの自然な入力を段階的に変換し、最終的にAIエージェントの協調的な実行に変えていきます。
+
+**主要な設計原則**:
+
+1. **Progressive Transformation**: データは各レイヤーで少しずつ変換され、次のレイヤーに渡される
+2. **Context Accumulation**: タスク、イテレーション、ユーザー入力などのコンテキストが蓄積される
+3. **Session Continuity**: エージェントセッションIDが保存・復元され、会話の継続性を保つ
+4. **Event-Driven Architecture**: WorkflowEngineがイベントを発行し、UI、ログ、通知が連携
+5. **3-Phase Execution**: メイン実行、レポート出力、ステータス判断の3段階で、明確な責任分離
+6. **Rule-Based Routing**: ルール評価の5段階フォールバックで、柔軟かつ予測可能な遷移
+
+このアーキテクチャにより、TAKTは複雑な多エージェント協調を、ユーザーには透明で、開発者には拡張可能な形で実現しています。
\ No newline at end of file
diff --git a/docs/vertical-slice-migration-map.md b/docs/vertical-slice-migration-map.md
new file mode 100644
index 0000000..3b67970
--- /dev/null
+++ b/docs/vertical-slice-migration-map.md
@@ -0,0 +1,111 @@
+# Vertical Slice + Core ハイブリッド構成 マッピング案
+
+## 目的
+- CLI中心の機能(コマンド)を slice 化し、変更影響を局所化する。
+- Workflow Engine などのコアは内向き依存(Clean)で保護する。
+- Public API(`index.ts`)で境界を固定し、深い import を避ける。
+
+## 依存ルール(簡易)
+- `core` は外側に依存しない。
+- `infra` は `core` に依存できる。
+- `features` は `core` / `infra` / `shared` に依存できる。
+- `app` は配線専用(入口)。
+
+## 移行マップ
+
+### 1) app/cli(CLI入口・配線)
+```
+src/cli/index.ts -> src/app/cli/index.ts
+src/cli/program.ts -> src/app/cli/program.ts
+src/cli/commands.ts -> src/app/cli/commands.ts
+src/cli/routing.ts -> src/app/cli/routing.ts
+src/cli/helpers.ts -> src/app/cli/helpers.ts
+```
+- `app/cli/index.ts` は CLI エントリのみ。
+- ルーティングは `features` の Public API を呼ぶだけにする。
+
+### 2) features(コマンド単位)
+```
+src/commands/index.ts -> src/features/tasks/index.ts
+src/commands/runAllTasks.ts -> src/features/tasks/run/index.ts
+src/commands/watchTasks.ts -> src/features/tasks/watch/index.ts
+src/commands/addTask.ts -> src/features/tasks/add/index.ts
+src/commands/listTasks.ts -> src/features/tasks/list/index.ts
+src/commands/execution/selectAndExecute.ts -> src/features/tasks/execute/selectAndExecute.ts
+src/commands/execution/types.ts -> src/features/tasks/execute/types.ts
+
+src/commands/pipeline/executePipeline.ts -> src/features/pipeline/execute.ts
+src/commands/pipeline/index.ts -> src/features/pipeline/index.ts
+
+src/commands/switchWorkflow.ts -> src/features/config/switchWorkflow.ts
+src/commands/switchConfig.ts -> src/features/config/switchConfig.ts
+src/commands/ejectBuiltin.ts -> src/features/config/ejectBuiltin.ts
+```
+- `features/tasks` は run/watch/add/list の共通入口を持つ。
+- `features/pipeline` は pipeline モードの専用 slice。
+- `features/config` は設定系(switch/eject)を集約。
+
+### 3) core/workflow(中核ロジック)
+```
+src/workflow/engine/* -> src/core/workflow/engine/*
+src/workflow/instruction/* -> src/core/workflow/instruction/*
+src/workflow/evaluation/* -> src/core/workflow/evaluation/*
+src/workflow/types.ts -> src/core/workflow/types.ts
+src/workflow/constants.ts -> src/core/workflow/constants.ts
+src/workflow/index.ts -> src/core/workflow/index.ts
+```
+- `core/workflow/index.ts` だけを Public API として使用。
+- `engine/`, `instruction/`, `evaluation/` 間の依存は内向き(core 内のみ)。
+
+### 4) core/models(型・スキーマ)
+```
+src/models/schemas.ts -> src/core/models/schemas.ts
+src/models/types.ts -> src/core/models/types.ts
+src/models/workflow-types.ts -> src/core/models/workflow-types.ts
+src/models/index.ts -> src/core/models/index.ts
+```
+- `core/models/index.ts` を Public API 化。
+
+### 5) infra(外部I/O)
+```
+src/providers/* -> src/infra/providers/*
+src/github/* -> src/infra/github/*
+src/config/* -> src/infra/config/*
+src/task/* -> src/infra/task/*
+src/utils/session.ts -> src/infra/fs/session.ts
+src/utils/git/* -> src/infra/git/*
+```
+- GitHub API / FS / Git / Provider など外部依存は `infra` に集約。
+
+### 6) shared(横断ユーティリティ)
+```
+src/utils/error.ts -> src/shared/utils/error.ts
+src/utils/debug.ts -> src/shared/utils/debug.ts
+src/utils/ui.ts -> src/shared/ui/index.ts
+src/utils/* -> src/shared/utils/* (外部I/O以外)
+```
+- 共有は `shared` に集めるが、肥大化は避ける。
+
+### 7) docs(参照パス修正)
+```
+docs/data-flow.md -> パス参照を app/core/features に合わせて更新
+`src/cli.ts` 参照 -> `src/app/cli/index.ts` に更新
+`src/workflow/state-manager.ts` 参照 -> `src/core/workflow/engine/state-manager.ts`
+`src/workflow/transitions.ts` 参照 -> `src/core/workflow/engine/transitions.ts`
+```
+
+## Public API ルール
+- `core/*` と `features/*` は **必ず `index.ts` から import**。
+- 深い import(`../engine/xxx` など)は禁止。
+
+## 移行順序(推奨)
+1. `core/` に workflow + models を集約
+2. `infra/` に外部I/Oを移動
+3. `features/` にコマンド単位で集約
+4. `app/cli` にエントリを移す
+5. Public API を整理し、深い import を排除
+6. docs の参照を更新
+
+## 備考
+- `src/workflow/index.ts` は `core/workflow/index.ts` に移し、外部からはここだけを参照。
+- `src/models/workflow.ts` のようなプレースホルダは廃止するか、`core/models/index.ts` へ統合する。
diff --git a/package.json b/package.json
index 340608b..b0f6a93 100644
--- a/package.json
+++ b/package.json
@@ -7,7 +7,7 @@
"bin": {
"takt": "./bin/takt",
"takt-dev": "./bin/takt",
- "takt-cli": "./dist/cli.js"
+ "takt-cli": "./dist/cli/index.js"
},
"scripts": {
"build": "tsc",
diff --git a/src/__tests__/engine-abort.test.ts b/src/__tests__/engine-abort.test.ts
index 2bda3ab..aad3a42 100644
--- a/src/__tests__/engine-abort.test.ts
+++ b/src/__tests__/engine-abort.test.ts
@@ -22,7 +22,7 @@ vi.mock('../workflow/evaluation/index.js', () => ({
detectMatchedRule: vi.fn(),
}));
-vi.mock('../workflow/phase-runner.js', () => ({
+vi.mock('../workflow/engine/phase-runner.js', () => ({
needsStatusJudgmentPhase: vi.fn().mockReturnValue(false),
runReportPhase: vi.fn().mockResolvedValue(undefined),
runStatusJudgmentPhase: vi.fn().mockResolvedValue(''),
diff --git a/src/__tests__/engine-agent-overrides.test.ts b/src/__tests__/engine-agent-overrides.test.ts
index 1f51dcf..48913b1 100644
--- a/src/__tests__/engine-agent-overrides.test.ts
+++ b/src/__tests__/engine-agent-overrides.test.ts
@@ -15,7 +15,7 @@ vi.mock('../workflow/evaluation/index.js', () => ({
detectMatchedRule: vi.fn(),
}));
-vi.mock('../workflow/phase-runner.js', () => ({
+vi.mock('../workflow/engine/phase-runner.js', () => ({
needsStatusJudgmentPhase: vi.fn(),
runReportPhase: vi.fn(),
runStatusJudgmentPhase: vi.fn(),
diff --git a/src/__tests__/engine-blocked.test.ts b/src/__tests__/engine-blocked.test.ts
index f46c213..1e084e5 100644
--- a/src/__tests__/engine-blocked.test.ts
+++ b/src/__tests__/engine-blocked.test.ts
@@ -20,7 +20,7 @@ vi.mock('../workflow/evaluation/index.js', () => ({
detectMatchedRule: vi.fn(),
}));
-vi.mock('../workflow/phase-runner.js', () => ({
+vi.mock('../workflow/engine/phase-runner.js', () => ({
needsStatusJudgmentPhase: vi.fn().mockReturnValue(false),
runReportPhase: vi.fn().mockResolvedValue(undefined),
runStatusJudgmentPhase: vi.fn().mockResolvedValue(''),
diff --git a/src/__tests__/engine-error.test.ts b/src/__tests__/engine-error.test.ts
index 2fd202a..632e679 100644
--- a/src/__tests__/engine-error.test.ts
+++ b/src/__tests__/engine-error.test.ts
@@ -21,7 +21,7 @@ vi.mock('../workflow/evaluation/index.js', () => ({
detectMatchedRule: vi.fn(),
}));
-vi.mock('../workflow/phase-runner.js', () => ({
+vi.mock('../workflow/engine/phase-runner.js', () => ({
needsStatusJudgmentPhase: vi.fn().mockReturnValue(false),
runReportPhase: vi.fn().mockResolvedValue(undefined),
runStatusJudgmentPhase: vi.fn().mockResolvedValue(''),
diff --git a/src/__tests__/engine-happy-path.test.ts b/src/__tests__/engine-happy-path.test.ts
index cc3ec94..51cc97c 100644
--- a/src/__tests__/engine-happy-path.test.ts
+++ b/src/__tests__/engine-happy-path.test.ts
@@ -25,7 +25,7 @@ vi.mock('../workflow/evaluation/index.js', () => ({
detectMatchedRule: vi.fn(),
}));
-vi.mock('../workflow/phase-runner.js', () => ({
+vi.mock('../workflow/engine/phase-runner.js', () => ({
needsStatusJudgmentPhase: vi.fn().mockReturnValue(false),
runReportPhase: vi.fn().mockResolvedValue(undefined),
runStatusJudgmentPhase: vi.fn().mockResolvedValue(''),
diff --git a/src/__tests__/engine-parallel.test.ts b/src/__tests__/engine-parallel.test.ts
index 146cc6d..c2b3446 100644
--- a/src/__tests__/engine-parallel.test.ts
+++ b/src/__tests__/engine-parallel.test.ts
@@ -20,7 +20,7 @@ vi.mock('../workflow/evaluation/index.js', () => ({
detectMatchedRule: vi.fn(),
}));
-vi.mock('../workflow/phase-runner.js', () => ({
+vi.mock('../workflow/engine/phase-runner.js', () => ({
needsStatusJudgmentPhase: vi.fn().mockReturnValue(false),
runReportPhase: vi.fn().mockResolvedValue(undefined),
runStatusJudgmentPhase: vi.fn().mockResolvedValue(''),
diff --git a/src/__tests__/engine-test-helpers.ts b/src/__tests__/engine-test-helpers.ts
index 3e31eeb..1d1ab7d 100644
--- a/src/__tests__/engine-test-helpers.ts
+++ b/src/__tests__/engine-test-helpers.ts
@@ -17,7 +17,7 @@ import type { WorkflowConfig, WorkflowStep, AgentResponse, WorkflowRule } from '
import { runAgent } from '../agents/runner.js';
import { detectMatchedRule } from '../workflow/evaluation/index.js';
import type { RuleMatch } from '../workflow/evaluation/index.js';
-import { needsStatusJudgmentPhase, runReportPhase, runStatusJudgmentPhase } from '../workflow/phase-runner.js';
+import { needsStatusJudgmentPhase, runReportPhase, runStatusJudgmentPhase } from '../workflow/engine/phase-runner.js';
import { generateReportDir } from '../utils/session.js';
// --- Factory functions ---
diff --git a/src/__tests__/engine-worktree-report.test.ts b/src/__tests__/engine-worktree-report.test.ts
index 896f896..bd6f28f 100644
--- a/src/__tests__/engine-worktree-report.test.ts
+++ b/src/__tests__/engine-worktree-report.test.ts
@@ -21,7 +21,7 @@ vi.mock('../workflow/evaluation/index.js', () => ({
detectMatchedRule: vi.fn(),
}));
-vi.mock('../workflow/phase-runner.js', () => ({
+vi.mock('../workflow/engine/phase-runner.js', () => ({
needsStatusJudgmentPhase: vi.fn().mockReturnValue(false),
runReportPhase: vi.fn().mockResolvedValue(undefined),
runStatusJudgmentPhase: vi.fn().mockResolvedValue(''),
@@ -34,7 +34,7 @@ vi.mock('../utils/session.js', () => ({
// --- Imports (after mocks) ---
import { WorkflowEngine } from '../workflow/engine/WorkflowEngine.js';
-import { runReportPhase } from '../workflow/phase-runner.js';
+import { runReportPhase } from '../workflow/engine/phase-runner.js';
import {
makeResponse,
makeStep,
diff --git a/src/__tests__/instructionBuilder.test.ts b/src/__tests__/instructionBuilder.test.ts
index 09a4796..55e5875 100644
--- a/src/__tests__/instructionBuilder.test.ts
+++ b/src/__tests__/instructionBuilder.test.ts
@@ -13,8 +13,8 @@ import {
buildExecutionMetadata,
renderExecutionMetadata,
type InstructionContext,
-} from '../workflow/instruction-context.js';
-import { generateStatusRulesFromRules } from '../workflow/status-rules.js';
+} from '../workflow/instruction/instruction-context.js';
+import { generateStatusRulesFromRules } from '../workflow/instruction/status-rules.js';
// Backward-compatible function wrappers for test readability
function buildInstruction(step: WorkflowStep, ctx: InstructionContext): string {
diff --git a/src/__tests__/it-error-recovery.test.ts b/src/__tests__/it-error-recovery.test.ts
index f46225a..6374f47 100644
--- a/src/__tests__/it-error-recovery.test.ts
+++ b/src/__tests__/it-error-recovery.test.ts
@@ -25,7 +25,7 @@ vi.mock('../claude/client.js', async (importOriginal) => {
};
});
-vi.mock('../workflow/phase-runner.js', () => ({
+vi.mock('../workflow/engine/phase-runner.js', () => ({
needsStatusJudgmentPhase: vi.fn().mockReturnValue(false),
runReportPhase: vi.fn().mockResolvedValue(undefined),
runStatusJudgmentPhase: vi.fn().mockResolvedValue(''),
diff --git a/src/__tests__/it-instruction-builder.test.ts b/src/__tests__/it-instruction-builder.test.ts
index dd98e86..49b1cd2 100644
--- a/src/__tests__/it-instruction-builder.test.ts
+++ b/src/__tests__/it-instruction-builder.test.ts
@@ -18,7 +18,7 @@ vi.mock('../config/global/globalConfig.js', () => ({
import { InstructionBuilder } from '../workflow/instruction/InstructionBuilder.js';
import { ReportInstructionBuilder, type ReportInstructionContext } from '../workflow/instruction/ReportInstructionBuilder.js';
import { StatusJudgmentBuilder, type StatusJudgmentContext } from '../workflow/instruction/StatusJudgmentBuilder.js';
-import type { InstructionContext } from '../workflow/instruction-context.js';
+import type { InstructionContext } from '../workflow/instruction/instruction-context.js';
// Function wrappers for test readability
function buildInstruction(step: WorkflowStep, ctx: InstructionContext): string {
diff --git a/src/__tests__/it-pipeline-modes.test.ts b/src/__tests__/it-pipeline-modes.test.ts
index 539d0be..638c093 100644
--- a/src/__tests__/it-pipeline-modes.test.ts
+++ b/src/__tests__/it-pipeline-modes.test.ts
@@ -131,7 +131,7 @@ vi.mock('../prompt/index.js', () => ({
promptInput: vi.fn().mockResolvedValue(null),
}));
-vi.mock('../workflow/phase-runner.js', () => ({
+vi.mock('../workflow/engine/phase-runner.js', () => ({
needsStatusJudgmentPhase: vi.fn().mockReturnValue(false),
runReportPhase: vi.fn().mockResolvedValue(undefined),
runStatusJudgmentPhase: vi.fn().mockResolvedValue(''),
diff --git a/src/__tests__/it-pipeline.test.ts b/src/__tests__/it-pipeline.test.ts
index 6db621b..ebf7ab4 100644
--- a/src/__tests__/it-pipeline.test.ts
+++ b/src/__tests__/it-pipeline.test.ts
@@ -113,7 +113,7 @@ vi.mock('../prompt/index.js', () => ({
promptInput: vi.fn().mockResolvedValue(null),
}));
-vi.mock('../workflow/phase-runner.js', () => ({
+vi.mock('../workflow/engine/phase-runner.js', () => ({
needsStatusJudgmentPhase: vi.fn().mockReturnValue(false),
runReportPhase: vi.fn().mockResolvedValue(undefined),
runStatusJudgmentPhase: vi.fn().mockResolvedValue(''),
diff --git a/src/__tests__/it-three-phase-execution.test.ts b/src/__tests__/it-three-phase-execution.test.ts
index b80d21f..cca0ed4 100644
--- a/src/__tests__/it-three-phase-execution.test.ts
+++ b/src/__tests__/it-three-phase-execution.test.ts
@@ -30,7 +30,7 @@ const mockNeedsStatusJudgmentPhase = vi.fn();
const mockRunReportPhase = vi.fn();
const mockRunStatusJudgmentPhase = vi.fn();
-vi.mock('../workflow/phase-runner.js', () => ({
+vi.mock('../workflow/engine/phase-runner.js', () => ({
needsStatusJudgmentPhase: (...args: unknown[]) => mockNeedsStatusJudgmentPhase(...args),
runReportPhase: (...args: unknown[]) => mockRunReportPhase(...args),
runStatusJudgmentPhase: (...args: unknown[]) => mockRunStatusJudgmentPhase(...args),
diff --git a/src/__tests__/it-workflow-execution.test.ts b/src/__tests__/it-workflow-execution.test.ts
index 596e831..67bf1c1 100644
--- a/src/__tests__/it-workflow-execution.test.ts
+++ b/src/__tests__/it-workflow-execution.test.ts
@@ -29,7 +29,7 @@ vi.mock('../claude/client.js', async (importOriginal) => {
};
});
-vi.mock('../workflow/phase-runner.js', () => ({
+vi.mock('../workflow/engine/phase-runner.js', () => ({
needsStatusJudgmentPhase: vi.fn().mockReturnValue(false),
runReportPhase: vi.fn().mockResolvedValue(undefined),
runStatusJudgmentPhase: vi.fn().mockResolvedValue(''),
diff --git a/src/__tests__/it-workflow-patterns.test.ts b/src/__tests__/it-workflow-patterns.test.ts
index 8611e15..58f2ee6 100644
--- a/src/__tests__/it-workflow-patterns.test.ts
+++ b/src/__tests__/it-workflow-patterns.test.ts
@@ -24,7 +24,7 @@ vi.mock('../claude/client.js', async (importOriginal) => {
};
});
-vi.mock('../workflow/phase-runner.js', () => ({
+vi.mock('../workflow/engine/phase-runner.js', () => ({
needsStatusJudgmentPhase: vi.fn().mockReturnValue(false),
runReportPhase: vi.fn().mockResolvedValue(undefined),
runStatusJudgmentPhase: vi.fn().mockResolvedValue(''),
diff --git a/src/__tests__/parallel-logger.test.ts b/src/__tests__/parallel-logger.test.ts
index 893954f..9f454ac 100644
--- a/src/__tests__/parallel-logger.test.ts
+++ b/src/__tests__/parallel-logger.test.ts
@@ -3,7 +3,7 @@
*/
import { describe, it, expect, beforeEach } from 'vitest';
-import { ParallelLogger } from '../workflow/parallel-logger.js';
+import { ParallelLogger } from '../workflow/engine/parallel-logger.js';
import type { StreamEvent } from '../claude/types.js';
describe('ParallelLogger', () => {
diff --git a/src/__tests__/transitions.test.ts b/src/__tests__/transitions.test.ts
index 7ea899a..d81351f 100644
--- a/src/__tests__/transitions.test.ts
+++ b/src/__tests__/transitions.test.ts
@@ -3,7 +3,7 @@
*/
import { describe, it, expect } from 'vitest';
-import { determineNextStepByRules } from '../workflow/transitions.js';
+import { determineNextStepByRules } from '../workflow/engine/transitions.js';
import type { WorkflowStep } from '../models/types.js';
function createStepWithRules(rules: { condition: string; next: string }[]): WorkflowStep {
diff --git a/src/cli.ts b/src/cli.ts
deleted file mode 100644
index 0eae095..0000000
--- a/src/cli.ts
+++ /dev/null
@@ -1,323 +0,0 @@
-#!/usr/bin/env node
-
-/**
- * TAKT CLI - Task Agent Koordination Tool
- *
- * Usage:
- * takt {task} - Execute task with current workflow (continues session)
- * takt #99 - Execute task from GitHub issue
- * takt run - Run all pending tasks from .takt/tasks/
- * takt switch - Switch workflow interactively
- * takt clear - Clear agent conversation sessions (reset to initial state)
- * takt --help - Show help
- * takt config - Select permission mode interactively
- *
- * Pipeline (non-interactive):
- * takt --task "fix bug" -w magi --auto-pr
- * takt --task "fix bug" --issue 99 --auto-pr
- */
-
-import { createRequire } from 'node:module';
-import { Command } from 'commander';
-import { resolve } from 'node:path';
-import {
- initGlobalDirs,
- initProjectDirs,
- loadGlobalConfig,
- getEffectiveDebugConfig,
-} from './config/index.js';
-import { clearAgentSessions, getCurrentWorkflow, isVerboseMode } from './config/paths.js';
-import { setQuietMode } from './context.js';
-import { info, error, success, setLogLevel } from './utils/ui.js';
-import { initDebugLogger, createLogger, setVerboseConsole } from './utils/debug.js';
-import {
- runAllTasks,
- switchWorkflow,
- switchConfig,
- addTask,
- ejectBuiltin,
- watchTasks,
- listTasks,
- interactiveMode,
- executePipeline,
-} from './commands/index.js';
-import { DEFAULT_WORKFLOW_NAME } from './constants.js';
-import { checkForUpdates } from './utils/updateNotifier.js';
-import { getErrorMessage } from './utils/error.js';
-import { resolveIssueTask, isIssueReference } from './github/issue.js';
-import { selectAndExecuteTask } from './commands/execution/selectAndExecute.js';
-import type { TaskExecutionOptions, SelectAndExecuteOptions } from './commands/execution/types.js';
-import type { ProviderType } from './providers/index.js';
-
-const require = createRequire(import.meta.url);
-const { version: cliVersion } = require('../package.json') as { version: string };
-
-const log = createLogger('cli');
-
-checkForUpdates();
-
-/** Resolved cwd shared across commands via preAction hook */
-let resolvedCwd = '';
-
-/** Whether pipeline mode is active (--task specified, set in preAction) */
-let pipelineMode = false;
-
-/** Whether quiet mode is active (--quiet flag or config, set in preAction) */
-let quietMode = false;
-
-const program = new Command();
-
-function resolveAgentOverrides(): TaskExecutionOptions | undefined {
- const opts = program.opts();
- const provider = opts.provider as ProviderType | undefined;
- const model = opts.model as string | undefined;
-
- if (!provider && !model) {
- return undefined;
- }
-
- return { provider, model };
-}
-
-function parseCreateWorktreeOption(value?: string): boolean | undefined {
- if (!value) {
- return undefined;
- }
-
- const normalized = value.toLowerCase();
- if (normalized === 'yes' || normalized === 'true') {
- return true;
- }
- if (normalized === 'no' || normalized === 'false') {
- return false;
- }
-
- error('Invalid value for --create-worktree. Use yes or no.');
- process.exit(1);
-}
-
-program
- .name('takt')
- .description('TAKT: Task Agent Koordination Tool')
- .version(cliVersion);
-
-// --- Global options ---
-program
- .option('-i, --issue ', 'GitHub issue number (equivalent to #N)', (val: string) => parseInt(val, 10))
- .option('-w, --workflow ', 'Workflow name or path to workflow file')
- .option('-b, --branch ', 'Branch name (auto-generated if omitted)')
- .option('--auto-pr', 'Create PR after successful execution')
- .option('--repo ', 'Repository (defaults to current)')
- .option('--provider ', 'Override agent provider (claude|codex|mock)')
- .option('--model ', 'Override agent model')
- .option('-t, --task ', 'Task content (as alternative to GitHub issue)')
- .option('--pipeline', 'Pipeline mode: non-interactive, no worktree, direct branch creation')
- .option('--skip-git', 'Skip branch creation, commit, and push (pipeline mode)')
- .option('--create-worktree ', 'Skip the worktree prompt by explicitly specifying yes or no')
- .option('-q, --quiet', 'Minimal output mode: suppress AI output (for CI)');
-
-// Common initialization for all commands
-program.hook('preAction', async () => {
- resolvedCwd = resolve(process.cwd());
-
- // Pipeline mode: triggered by --pipeline flag
- const rootOpts = program.opts();
- pipelineMode = rootOpts.pipeline === true;
-
- await initGlobalDirs({ nonInteractive: pipelineMode });
- initProjectDirs(resolvedCwd);
-
- const verbose = isVerboseMode(resolvedCwd);
- let debugConfig = getEffectiveDebugConfig(resolvedCwd);
-
- if (verbose && (!debugConfig || !debugConfig.enabled)) {
- debugConfig = { enabled: true };
- }
-
- initDebugLogger(debugConfig, resolvedCwd);
-
- // Load config once for both log level and quiet mode
- const config = loadGlobalConfig();
-
- if (verbose) {
- setVerboseConsole(true);
- setLogLevel('debug');
- } else {
- setLogLevel(config.logLevel);
- }
-
- // Quiet mode: CLI flag takes precedence over config
- quietMode = rootOpts.quiet === true || config.minimalOutput === true;
- setQuietMode(quietMode);
-
- log.info('TAKT CLI starting', { version: cliVersion, cwd: resolvedCwd, verbose, pipelineMode, quietMode });
-});
-
-// isQuietMode is now exported from context.ts to avoid circular dependencies
-
-// --- Subcommands ---
-
-program
- .command('run')
- .description('Run all pending tasks from .takt/tasks/')
- .action(async () => {
- const workflow = getCurrentWorkflow(resolvedCwd);
- await runAllTasks(resolvedCwd, workflow, resolveAgentOverrides());
- });
-
-program
- .command('watch')
- .description('Watch for tasks and auto-execute')
- .action(async () => {
- await watchTasks(resolvedCwd, resolveAgentOverrides());
- });
-
-program
- .command('add')
- .description('Add a new task (interactive AI conversation)')
- .argument('[task]', 'Task description or GitHub issue reference (e.g. "#28")')
- .action(async (task?: string) => {
- await addTask(resolvedCwd, task);
- });
-
-program
- .command('list')
- .description('List task branches (merge/delete)')
- .action(async () => {
- await listTasks(resolvedCwd, resolveAgentOverrides());
- });
-
-program
- .command('switch')
- .description('Switch workflow interactively')
- .argument('[workflow]', 'Workflow name')
- .action(async (workflow?: string) => {
- await switchWorkflow(resolvedCwd, workflow);
- });
-
-program
- .command('clear')
- .description('Clear agent conversation sessions')
- .action(() => {
- clearAgentSessions(resolvedCwd);
- success('Agent sessions cleared');
- });
-
-program
- .command('eject')
- .description('Copy builtin workflow/agents to ~/.takt/ for customization')
- .argument('[name]', 'Specific builtin to eject')
- .action(async (name?: string) => {
- await ejectBuiltin(name);
- });
-
-program
- .command('config')
- .description('Configure settings (permission mode)')
- .argument('[key]', 'Configuration key')
- .action(async (key?: string) => {
- await switchConfig(resolvedCwd, key);
- });
-
-// --- Default action: task execution, interactive mode, or pipeline ---
-
-/**
- * Check if the input is a task description (should execute directly)
- * vs a short input that should enter interactive mode as initial input.
- *
- * Task descriptions: contain spaces, or are issue references (#N).
- * Short single words: routed to interactive mode as first message.
- */
-function isDirectTask(input: string): boolean {
- // Multi-word input is a task description
- if (input.includes(' ')) return true;
- // Issue references are direct tasks
- if (isIssueReference(input) || input.trim().split(/\s+/).every((t: string) => isIssueReference(t))) return true;
- return false;
-}
-
-
-program
- .argument('[task]', 'Task to execute (or GitHub issue reference like "#6")')
- .action(async (task?: string) => {
- const opts = program.opts();
- const agentOverrides = resolveAgentOverrides();
- const createWorktreeOverride = parseCreateWorktreeOption(opts.createWorktree as string | undefined);
- const selectOptions: SelectAndExecuteOptions = {
- autoPr: opts.autoPr === true,
- repo: opts.repo as string | undefined,
- workflow: opts.workflow as string | undefined,
- createWorktree: createWorktreeOverride,
- };
-
- // --- Pipeline mode (non-interactive): triggered by --pipeline ---
- if (pipelineMode) {
- const exitCode = await executePipeline({
- issueNumber: opts.issue as number | undefined,
- task: opts.task as string | undefined,
- workflow: (opts.workflow as string | undefined) ?? DEFAULT_WORKFLOW_NAME,
- branch: opts.branch as string | undefined,
- autoPr: opts.autoPr === true,
- repo: opts.repo as string | undefined,
- skipGit: opts.skipGit === true,
- cwd: resolvedCwd,
- provider: agentOverrides?.provider,
- model: agentOverrides?.model,
- });
-
- if (exitCode !== 0) {
- process.exit(exitCode);
- }
- return;
- }
-
- // --- Normal (interactive) mode ---
-
- // Resolve --task option to task text
- const taskFromOption = opts.task as string | undefined;
- if (taskFromOption) {
- await selectAndExecuteTask(resolvedCwd, taskFromOption, selectOptions, agentOverrides);
- return;
- }
-
- // Resolve --issue N to task text (same as #N)
- const issueFromOption = opts.issue as number | undefined;
- if (issueFromOption) {
- try {
- const resolvedTask = resolveIssueTask(`#${issueFromOption}`);
- await selectAndExecuteTask(resolvedCwd, resolvedTask, selectOptions, agentOverrides);
- } catch (e) {
- error(getErrorMessage(e));
- process.exit(1);
- }
- return;
- }
-
- if (task && isDirectTask(task)) {
- // Resolve #N issue references to task text
- let resolvedTask: string = task;
- if (isIssueReference(task) || task.trim().split(/\s+/).every((t: string) => isIssueReference(t))) {
- try {
- info('Fetching GitHub Issue...');
- resolvedTask = resolveIssueTask(task);
- } catch (e) {
- error(getErrorMessage(e));
- process.exit(1);
- }
- }
-
- await selectAndExecuteTask(resolvedCwd, resolvedTask, selectOptions, agentOverrides);
- return;
- }
-
- // Short single word or no task → interactive mode (with optional initial input)
- const result = await interactiveMode(resolvedCwd, task);
-
- if (!result.confirmed) {
- return;
- }
-
- await selectAndExecuteTask(resolvedCwd, result.task, selectOptions, agentOverrides);
- });
-
-program.parse();
diff --git a/src/cli/commands.ts b/src/cli/commands.ts
new file mode 100644
index 0000000..1078f98
--- /dev/null
+++ b/src/cli/commands.ts
@@ -0,0 +1,81 @@
+/**
+ * CLI subcommand definitions
+ *
+ * Registers all named subcommands (run, watch, add, list, switch, clear, eject, config).
+ */
+
+import { clearAgentSessions, getCurrentWorkflow } from '../config/paths.js';
+import { success } from '../utils/ui.js';
+import {
+ runAllTasks,
+ switchWorkflow,
+ switchConfig,
+ addTask,
+ ejectBuiltin,
+ watchTasks,
+ listTasks,
+} from '../commands/index.js';
+import { program, resolvedCwd } from './program.js';
+import { resolveAgentOverrides } from './helpers.js';
+
+program
+ .command('run')
+ .description('Run all pending tasks from .takt/tasks/')
+ .action(async () => {
+ const workflow = getCurrentWorkflow(resolvedCwd);
+ await runAllTasks(resolvedCwd, workflow, resolveAgentOverrides(program));
+ });
+
+program
+ .command('watch')
+ .description('Watch for tasks and auto-execute')
+ .action(async () => {
+ await watchTasks(resolvedCwd, resolveAgentOverrides(program));
+ });
+
+program
+ .command('add')
+ .description('Add a new task (interactive AI conversation)')
+ .argument('[task]', 'Task description or GitHub issue reference (e.g. "#28")')
+ .action(async (task?: string) => {
+ await addTask(resolvedCwd, task);
+ });
+
+program
+ .command('list')
+ .description('List task branches (merge/delete)')
+ .action(async () => {
+ await listTasks(resolvedCwd, resolveAgentOverrides(program));
+ });
+
+program
+ .command('switch')
+ .description('Switch workflow interactively')
+ .argument('[workflow]', 'Workflow name')
+ .action(async (workflow?: string) => {
+ await switchWorkflow(resolvedCwd, workflow);
+ });
+
+program
+ .command('clear')
+ .description('Clear agent conversation sessions')
+ .action(() => {
+ clearAgentSessions(resolvedCwd);
+ success('Agent sessions cleared');
+ });
+
+program
+ .command('eject')
+ .description('Copy builtin workflow/agents to ~/.takt/ for customization')
+ .argument('[name]', 'Specific builtin to eject')
+ .action(async (name?: string) => {
+ await ejectBuiltin(name);
+ });
+
+program
+ .command('config')
+ .description('Configure settings (permission mode)')
+ .argument('[key]', 'Configuration key')
+ .action(async (key?: string) => {
+ await switchConfig(resolvedCwd, key);
+ });
diff --git a/src/cli/helpers.ts b/src/cli/helpers.ts
new file mode 100644
index 0000000..94602d9
--- /dev/null
+++ b/src/cli/helpers.ts
@@ -0,0 +1,62 @@
+/**
+ * CLI helper functions
+ *
+ * Utility functions for option parsing and task classification.
+ */
+
+import type { Command } from 'commander';
+import type { TaskExecutionOptions } from '../commands/execution/types.js';
+import type { ProviderType } from '../providers/index.js';
+import { error } from '../utils/ui.js';
+import { isIssueReference } from '../github/issue.js';
+
+/**
+ * Resolve --provider and --model options into TaskExecutionOptions.
+ * Returns undefined if neither is specified.
+ */
+export function resolveAgentOverrides(program: Command): TaskExecutionOptions | undefined {
+ const opts = program.opts();
+ const provider = opts.provider as ProviderType | undefined;
+ const model = opts.model as string | undefined;
+
+ if (!provider && !model) {
+ return undefined;
+ }
+
+ return { provider, model };
+}
+
+/**
+ * Parse --create-worktree option value (yes/no/true/false).
+ * Returns undefined if not specified, boolean otherwise.
+ * Exits with error on invalid value.
+ */
+export function parseCreateWorktreeOption(value?: string): boolean | undefined {
+ if (!value) {
+ return undefined;
+ }
+
+ const normalized = value.toLowerCase();
+ if (normalized === 'yes' || normalized === 'true') {
+ return true;
+ }
+ if (normalized === 'no' || normalized === 'false') {
+ return false;
+ }
+
+ error('Invalid value for --create-worktree. Use yes or no.');
+ process.exit(1);
+}
+
+/**
+ * Check if the input is a task description (should execute directly)
+ * vs a short input that should enter interactive mode as initial input.
+ *
+ * Task descriptions: contain spaces, or are issue references (#N).
+ * Short single words: routed to interactive mode as first message.
+ */
+export function isDirectTask(input: string): boolean {
+ if (input.includes(' ')) return true;
+ if (isIssueReference(input) || input.trim().split(/\s+/).every((t: string) => isIssueReference(t))) return true;
+ return false;
+}
diff --git a/src/cli/index.ts b/src/cli/index.ts
new file mode 100644
index 0000000..b4a903f
--- /dev/null
+++ b/src/cli/index.ts
@@ -0,0 +1,18 @@
+#!/usr/bin/env node
+
+/**
+ * TAKT CLI entry point
+ *
+ * Import order matters: program setup → commands → routing → parse.
+ */
+
+import { checkForUpdates } from '../utils/updateNotifier.js';
+
+checkForUpdates();
+
+// Import in dependency order
+import { program } from './program.js';
+import './commands.js';
+import './routing.js';
+
+program.parse();
diff --git a/src/cli/program.ts b/src/cli/program.ts
new file mode 100644
index 0000000..1fc7240
--- /dev/null
+++ b/src/cli/program.ts
@@ -0,0 +1,89 @@
+/**
+ * Commander program setup
+ *
+ * Creates the Command instance, registers global options,
+ * and sets up the preAction hook for initialization.
+ */
+
+import { createRequire } from 'node:module';
+import { Command } from 'commander';
+import { resolve } from 'node:path';
+import {
+ initGlobalDirs,
+ initProjectDirs,
+ loadGlobalConfig,
+ getEffectiveDebugConfig,
+ isVerboseMode,
+} from '../config/index.js';
+import { setQuietMode } from '../context.js';
+import { setLogLevel } from '../utils/ui.js';
+import { initDebugLogger, createLogger, setVerboseConsole } from '../utils/debug.js';
+
+const require = createRequire(import.meta.url);
+const { version: cliVersion } = require('../../package.json') as { version: string };
+
+const log = createLogger('cli');
+
+/** Resolved cwd shared across commands via preAction hook */
+export let resolvedCwd = '';
+
+/** Whether pipeline mode is active (--task specified, set in preAction) */
+export let pipelineMode = false;
+
+export { cliVersion };
+
+export const program = new Command();
+
+program
+ .name('takt')
+ .description('TAKT: Task Agent Koordination Tool')
+ .version(cliVersion);
+
+// --- Global options ---
+program
+ .option('-i, --issue ', 'GitHub issue number (equivalent to #N)', (val: string) => parseInt(val, 10))
+ .option('-w, --workflow ', 'Workflow name or path to workflow file')
+ .option('-b, --branch ', 'Branch name (auto-generated if omitted)')
+ .option('--auto-pr', 'Create PR after successful execution')
+ .option('--repo ', 'Repository (defaults to current)')
+ .option('--provider ', 'Override agent provider (claude|codex|mock)')
+ .option('--model ', 'Override agent model')
+ .option('-t, --task ', 'Task content (as alternative to GitHub issue)')
+ .option('--pipeline', 'Pipeline mode: non-interactive, no worktree, direct branch creation')
+ .option('--skip-git', 'Skip branch creation, commit, and push (pipeline mode)')
+ .option('--create-worktree ', 'Skip the worktree prompt by explicitly specifying yes or no')
+ .option('-q, --quiet', 'Minimal output mode: suppress AI output (for CI)');
+
+// Common initialization for all commands
+program.hook('preAction', async () => {
+ resolvedCwd = resolve(process.cwd());
+
+ const rootOpts = program.opts();
+ pipelineMode = rootOpts.pipeline === true;
+
+ await initGlobalDirs({ nonInteractive: pipelineMode });
+ initProjectDirs(resolvedCwd);
+
+ const verbose = isVerboseMode(resolvedCwd);
+ let debugConfig = getEffectiveDebugConfig(resolvedCwd);
+
+ if (verbose && (!debugConfig || !debugConfig.enabled)) {
+ debugConfig = { enabled: true };
+ }
+
+ initDebugLogger(debugConfig, resolvedCwd);
+
+ const config = loadGlobalConfig();
+
+ if (verbose) {
+ setVerboseConsole(true);
+ setLogLevel('debug');
+ } else {
+ setLogLevel(config.logLevel);
+ }
+
+ const quietMode = rootOpts.quiet === true || config.minimalOutput === true;
+ setQuietMode(quietMode);
+
+ log.info('TAKT CLI starting', { version: cliVersion, cwd: resolvedCwd, verbose, pipelineMode, quietMode });
+});
diff --git a/src/cli/routing.ts b/src/cli/routing.ts
new file mode 100644
index 0000000..aa9abee
--- /dev/null
+++ b/src/cli/routing.ts
@@ -0,0 +1,98 @@
+/**
+ * Default action routing
+ *
+ * Handles the default (no subcommand) action: task execution,
+ * pipeline mode, or interactive mode.
+ */
+
+import { info, error } from '../utils/ui.js';
+import { getErrorMessage } from '../utils/error.js';
+import { resolveIssueTask, isIssueReference } from '../github/issue.js';
+import { selectAndExecuteTask } from '../commands/execution/selectAndExecute.js';
+import { executePipeline, interactiveMode } from '../commands/index.js';
+import { DEFAULT_WORKFLOW_NAME } from '../constants.js';
+import type { SelectAndExecuteOptions } from '../commands/execution/types.js';
+import { program, resolvedCwd, pipelineMode } from './program.js';
+import { resolveAgentOverrides, parseCreateWorktreeOption, isDirectTask } from './helpers.js';
+
+program
+ .argument('[task]', 'Task to execute (or GitHub issue reference like "#6")')
+ .action(async (task?: string) => {
+ const opts = program.opts();
+ const agentOverrides = resolveAgentOverrides(program);
+ const createWorktreeOverride = parseCreateWorktreeOption(opts.createWorktree as string | undefined);
+ const selectOptions: SelectAndExecuteOptions = {
+ autoPr: opts.autoPr === true,
+ repo: opts.repo as string | undefined,
+ workflow: opts.workflow as string | undefined,
+ createWorktree: createWorktreeOverride,
+ };
+
+ // --- Pipeline mode (non-interactive): triggered by --pipeline ---
+ if (pipelineMode) {
+ const exitCode = await executePipeline({
+ issueNumber: opts.issue as number | undefined,
+ task: opts.task as string | undefined,
+ workflow: (opts.workflow as string | undefined) ?? DEFAULT_WORKFLOW_NAME,
+ branch: opts.branch as string | undefined,
+ autoPr: opts.autoPr === true,
+ repo: opts.repo as string | undefined,
+ skipGit: opts.skipGit === true,
+ cwd: resolvedCwd,
+ provider: agentOverrides?.provider,
+ model: agentOverrides?.model,
+ });
+
+ if (exitCode !== 0) {
+ process.exit(exitCode);
+ }
+ return;
+ }
+
+ // --- Normal (interactive) mode ---
+
+ // Resolve --task option to task text
+ const taskFromOption = opts.task as string | undefined;
+ if (taskFromOption) {
+ await selectAndExecuteTask(resolvedCwd, taskFromOption, selectOptions, agentOverrides);
+ return;
+ }
+
+ // Resolve --issue N to task text (same as #N)
+ const issueFromOption = opts.issue as number | undefined;
+ if (issueFromOption) {
+ try {
+ const resolvedTask = resolveIssueTask(`#${issueFromOption}`);
+ await selectAndExecuteTask(resolvedCwd, resolvedTask, selectOptions, agentOverrides);
+ } catch (e) {
+ error(getErrorMessage(e));
+ process.exit(1);
+ }
+ return;
+ }
+
+ if (task && isDirectTask(task)) {
+ let resolvedTask: string = task;
+ if (isIssueReference(task) || task.trim().split(/\s+/).every((t: string) => isIssueReference(t))) {
+ try {
+ info('Fetching GitHub Issue...');
+ resolvedTask = resolveIssueTask(task);
+ } catch (e) {
+ error(getErrorMessage(e));
+ process.exit(1);
+ }
+ }
+
+ await selectAndExecuteTask(resolvedCwd, resolvedTask, selectOptions, agentOverrides);
+ return;
+ }
+
+ // Short single word or no task → interactive mode (with optional initial input)
+ const result = await interactiveMode(resolvedCwd, task);
+
+ if (!result.confirmed) {
+ return;
+ }
+
+ await selectAndExecuteTask(resolvedCwd, result.task, selectOptions, agentOverrides);
+ });
diff --git a/src/models/agent.ts b/src/models/agent.ts
index b3c4822..e363fd8 100644
--- a/src/models/agent.ts
+++ b/src/models/agent.ts
@@ -1,15 +1,7 @@
import { z } from 'zod/v4';
+import { AgentModelSchema, AgentConfigSchema } from './schemas.js';
-export const AgentModelSchema = z.enum(['opus', 'sonnet', 'haiku']).default('sonnet');
-
-export const AgentConfigSchema = z.object({
- name: z.string().min(1),
- description: z.string().optional(),
- model: AgentModelSchema,
- systemPrompt: z.string().optional(),
- allowedTools: z.array(z.string()).optional(),
- maxTurns: z.number().int().positive().optional(),
-});
+export { AgentModelSchema, AgentConfigSchema };
export type AgentModel = z.infer;
export type AgentConfig = z.infer;
diff --git a/src/models/config.ts b/src/models/config.ts
index 0948b42..479c91b 100644
--- a/src/models/config.ts
+++ b/src/models/config.ts
@@ -1,19 +1,7 @@
import { z } from 'zod/v4';
-import { AgentModelSchema } from './agent.js';
+import { TaktConfigSchema } from './schemas.js';
-const ClaudeConfigSchema = z.object({
- command: z.string().default('claude'),
- timeout: z.number().int().positive().default(300000),
-});
-
-export const TaktConfigSchema = z.object({
- defaultModel: AgentModelSchema,
- defaultWorkflow: z.string().default('default'),
- agentDirs: z.array(z.string()).default([]),
- workflowDirs: z.array(z.string()).default([]),
- sessionDir: z.string().optional(),
- claude: ClaudeConfigSchema.default({ command: 'claude', timeout: 300000 }),
-});
+export { TaktConfigSchema };
export type TaktConfig = z.infer;
diff --git a/src/models/index.ts b/src/models/index.ts
index b43dd20..915df0d 100644
--- a/src/models/index.ts
+++ b/src/models/index.ts
@@ -18,15 +18,6 @@ export type {
// Re-export from agent.ts
export * from './agent.js';
-// Re-export from workflow.ts (Zod schemas only, not types)
-export {
- WorkflowStepSchema,
- WorkflowConfigSchema,
- type WorkflowDefinition,
- type WorkflowContext,
- type StepResult,
-} from './workflow.js';
-
// Re-export from config.ts
export * from './config.js';
diff --git a/src/models/schemas.ts b/src/models/schemas.ts
index 4532da3..3f1d7d9 100644
--- a/src/models/schemas.ts
+++ b/src/models/schemas.ts
@@ -7,6 +7,35 @@
import { z } from 'zod/v4';
import { DEFAULT_LANGUAGE } from '../constants.js';
+/** Agent model schema (opus, sonnet, haiku) */
+export const AgentModelSchema = z.enum(['opus', 'sonnet', 'haiku']).default('sonnet');
+
+/** Agent configuration schema */
+export const AgentConfigSchema = z.object({
+ name: z.string().min(1),
+ description: z.string().optional(),
+ model: AgentModelSchema,
+ systemPrompt: z.string().optional(),
+ allowedTools: z.array(z.string()).optional(),
+ maxTurns: z.number().int().positive().optional(),
+});
+
+/** Claude CLI configuration schema */
+export const ClaudeConfigSchema = z.object({
+ command: z.string().default('claude'),
+ timeout: z.number().int().positive().default(300000),
+});
+
+/** TAKT global tool configuration schema */
+export const TaktConfigSchema = z.object({
+ defaultModel: AgentModelSchema,
+ defaultWorkflow: z.string().default('default'),
+ agentDirs: z.array(z.string()).default([]),
+ workflowDirs: z.array(z.string()).default([]),
+ sessionDir: z.string().optional(),
+ claude: ClaudeConfigSchema.default({ command: 'claude', timeout: 300000 }),
+});
+
/** Agent type schema */
export const AgentTypeSchema = z.enum(['coder', 'architect', 'supervisor', 'custom']);
diff --git a/src/models/workflow.ts b/src/models/workflow.ts
index 35842b2..e262334 100644
--- a/src/models/workflow.ts
+++ b/src/models/workflow.ts
@@ -1,49 +1,4 @@
-import { z } from 'zod/v4';
-import { AgentModelSchema } from './agent.js';
-
-export const WorkflowStepSchema = z.object({
- agent: z.string().min(1),
- model: AgentModelSchema.optional(),
- prompt: z.string().optional(),
- condition: z.string().optional(),
- onSuccess: z.string().optional(),
- onFailure: z.string().optional(),
-});
-
-export const WorkflowConfigSchema = z.object({
- name: z.string().min(1),
- description: z.string().optional(),
- version: z.string().optional().default('1.0.0'),
- steps: z.array(WorkflowStepSchema).min(1),
- entryPoint: z.string().optional(),
- variables: z.record(z.string(), z.string()).optional(),
-});
-
-export type WorkflowStep = z.infer;
-export type WorkflowConfig = z.infer;
-
-export interface WorkflowDefinition {
- name: string;
- description?: string;
- version: string;
- steps: WorkflowStep[];
- entryPoint?: string;
- variables?: Record;
- filePath?: string;
-}
-
-export interface WorkflowContext {
- workflowName: string;
- currentStep: string;
- variables: Record;
- history: StepResult[];
- userPrompt: string;
-}
-
-export interface StepResult {
- stepName: string;
- agentName: string;
- success: boolean;
- output: string;
- timestamp: Date;
-}
+// Workflow schemas and types are defined in:
+// - schemas.ts (Zod schemas for YAML parsing)
+// - workflow-types.ts (runtime types)
+// This file is kept as a namespace placeholder.
diff --git a/src/workflow/engine/OptionsBuilder.ts b/src/workflow/engine/OptionsBuilder.ts
index ce247ac..4bcb543 100644
--- a/src/workflow/engine/OptionsBuilder.ts
+++ b/src/workflow/engine/OptionsBuilder.ts
@@ -8,7 +8,7 @@
import { join } from 'node:path';
import type { WorkflowStep, WorkflowState, Language } from '../../models/types.js';
import type { RunAgentOptions } from '../../agents/runner.js';
-import type { PhaseRunnerContext } from '../phase-runner.js';
+import type { PhaseRunnerContext } from './phase-runner.js';
import type { WorkflowEngineOptions } from '../types.js';
export class OptionsBuilder {
diff --git a/src/workflow/engine/ParallelRunner.ts b/src/workflow/engine/ParallelRunner.ts
index cda282a..1e2f84e 100644
--- a/src/workflow/engine/ParallelRunner.ts
+++ b/src/workflow/engine/ParallelRunner.ts
@@ -11,10 +11,10 @@ import type {
AgentResponse,
} from '../../models/types.js';
import { runAgent } from '../../agents/runner.js';
-import { ParallelLogger } from '../parallel-logger.js';
-import { needsStatusJudgmentPhase, runReportPhase, runStatusJudgmentPhase } from '../phase-runner.js';
+import { ParallelLogger } from './parallel-logger.js';
+import { needsStatusJudgmentPhase, runReportPhase, runStatusJudgmentPhase } from './phase-runner.js';
import { detectMatchedRule } from '../evaluation/index.js';
-import { incrementStepIteration } from '../state-manager.js';
+import { incrementStepIteration } from './state-manager.js';
import { createLogger } from '../../utils/debug.js';
import type { OptionsBuilder } from './OptionsBuilder.js';
import type { StepExecutor } from './StepExecutor.js';
diff --git a/src/workflow/engine/StepExecutor.ts b/src/workflow/engine/StepExecutor.ts
index 9130945..987c7ac 100644
--- a/src/workflow/engine/StepExecutor.ts
+++ b/src/workflow/engine/StepExecutor.ts
@@ -16,9 +16,9 @@ import type {
} from '../../models/types.js';
import { runAgent } from '../../agents/runner.js';
import { InstructionBuilder, isReportObjectConfig } from '../instruction/InstructionBuilder.js';
-import { needsStatusJudgmentPhase, runReportPhase, runStatusJudgmentPhase } from '../phase-runner.js';
+import { needsStatusJudgmentPhase, runReportPhase, runStatusJudgmentPhase } from './phase-runner.js';
import { detectMatchedRule } from '../evaluation/index.js';
-import { incrementStepIteration, getPreviousOutput } from '../state-manager.js';
+import { incrementStepIteration, getPreviousOutput } from './state-manager.js';
import { createLogger } from '../../utils/debug.js';
import type { OptionsBuilder } from './OptionsBuilder.js';
diff --git a/src/workflow/engine/WorkflowEngine.ts b/src/workflow/engine/WorkflowEngine.ts
index d97a769..7409e36 100644
--- a/src/workflow/engine/WorkflowEngine.ts
+++ b/src/workflow/engine/WorkflowEngine.ts
@@ -17,14 +17,14 @@ import type {
} from '../../models/types.js';
import { COMPLETE_STEP, ABORT_STEP, ERROR_MESSAGES } from '../constants.js';
import type { WorkflowEngineOptions } from '../types.js';
-import { determineNextStepByRules } from '../transitions.js';
-import { LoopDetector } from '../loop-detector.js';
-import { handleBlocked } from '../blocked-handler.js';
+import { determineNextStepByRules } from './transitions.js';
+import { LoopDetector } from './loop-detector.js';
+import { handleBlocked } from './blocked-handler.js';
import {
createInitialState,
addUserInput as addUserInputToState,
incrementStepIteration,
-} from '../state-manager.js';
+} from './state-manager.js';
import { generateReportDir } from '../../utils/session.js';
import { getErrorMessage } from '../../utils/error.js';
import { createLogger } from '../../utils/debug.js';
diff --git a/src/workflow/blocked-handler.ts b/src/workflow/engine/blocked-handler.ts
similarity index 90%
rename from src/workflow/blocked-handler.ts
rename to src/workflow/engine/blocked-handler.ts
index bc34878..d04222c 100644
--- a/src/workflow/blocked-handler.ts
+++ b/src/workflow/engine/blocked-handler.ts
@@ -5,8 +5,8 @@
* requesting user input to continue.
*/
-import type { WorkflowStep, AgentResponse } from '../models/types.js';
-import type { UserInputRequest, WorkflowEngineOptions } from './types.js';
+import type { WorkflowStep, AgentResponse } from '../../models/types.js';
+import type { UserInputRequest, WorkflowEngineOptions } from '../types.js';
import { extractBlockedPrompt } from './transitions.js';
/**
diff --git a/src/workflow/loop-detector.ts b/src/workflow/engine/loop-detector.ts
similarity index 92%
rename from src/workflow/loop-detector.ts
rename to src/workflow/engine/loop-detector.ts
index 8e1fc71..1f0cd85 100644
--- a/src/workflow/loop-detector.ts
+++ b/src/workflow/engine/loop-detector.ts
@@ -5,8 +5,8 @@
* which may indicate an infinite loop.
*/
-import type { LoopDetectionConfig } from '../models/types.js';
-import type { LoopCheckResult } from './types.js';
+import type { LoopDetectionConfig } from '../../models/types.js';
+import type { LoopCheckResult } from '../types.js';
/** Default loop detection settings */
const DEFAULT_LOOP_DETECTION: Required = {
diff --git a/src/workflow/parallel-logger.ts b/src/workflow/engine/parallel-logger.ts
similarity index 98%
rename from src/workflow/parallel-logger.ts
rename to src/workflow/engine/parallel-logger.ts
index 31d14a3..5160868 100644
--- a/src/workflow/parallel-logger.ts
+++ b/src/workflow/engine/parallel-logger.ts
@@ -6,7 +6,7 @@
* aligned to the longest sub-step name.
*/
-import type { StreamCallback, StreamEvent } from '../claude/types.js';
+import type { StreamCallback, StreamEvent } from '../../claude/types.js';
/** ANSI color codes for sub-step prefixes (cycled in order) */
const COLORS = ['\x1b[36m', '\x1b[33m', '\x1b[35m', '\x1b[32m'] as const; // cyan, yellow, magenta, green
diff --git a/src/workflow/phase-runner.ts b/src/workflow/engine/phase-runner.ts
similarity index 88%
rename from src/workflow/phase-runner.ts
rename to src/workflow/engine/phase-runner.ts
index 6a0630e..cbfb518 100644
--- a/src/workflow/phase-runner.ts
+++ b/src/workflow/engine/phase-runner.ts
@@ -5,12 +5,12 @@
* as session-resume operations.
*/
-import type { WorkflowStep, Language } from '../models/types.js';
-import { runAgent, type RunAgentOptions } from '../agents/runner.js';
-import { ReportInstructionBuilder } from './instruction/ReportInstructionBuilder.js';
-import { StatusJudgmentBuilder } from './instruction/StatusJudgmentBuilder.js';
-import { hasTagBasedRules } from './rule-utils.js';
-import { createLogger } from '../utils/debug.js';
+import type { WorkflowStep, Language } from '../../models/types.js';
+import { runAgent, type RunAgentOptions } from '../../agents/runner.js';
+import { ReportInstructionBuilder } from '../instruction/ReportInstructionBuilder.js';
+import { StatusJudgmentBuilder } from '../instruction/StatusJudgmentBuilder.js';
+import { hasTagBasedRules } from '../evaluation/rule-utils.js';
+import { createLogger } from '../../utils/debug.js';
const log = createLogger('phase-runner');
diff --git a/src/workflow/engine/state-manager.ts b/src/workflow/engine/state-manager.ts
new file mode 100644
index 0000000..7716b00
--- /dev/null
+++ b/src/workflow/engine/state-manager.ts
@@ -0,0 +1,118 @@
+/**
+ * Workflow state management
+ *
+ * Manages the mutable state of a workflow execution including
+ * user inputs and agent sessions.
+ */
+
+import type { WorkflowState, WorkflowConfig, AgentResponse } from '../../models/types.js';
+import {
+ MAX_USER_INPUTS,
+ MAX_INPUT_LENGTH,
+} from '../constants.js';
+import type { WorkflowEngineOptions } from '../types.js';
+
+/**
+ * Manages workflow execution state.
+ *
+ * Encapsulates WorkflowState and provides methods for state mutations.
+ */
+export class StateManager {
+ readonly state: WorkflowState;
+
+ constructor(config: WorkflowConfig, options: WorkflowEngineOptions) {
+ // Restore agent sessions from options if provided
+ const agentSessions = new Map();
+ if (options.initialSessions) {
+ for (const [agent, sessionId] of Object.entries(options.initialSessions)) {
+ agentSessions.set(agent, sessionId);
+ }
+ }
+
+ // Initialize user inputs from options if provided
+ const userInputs = options.initialUserInputs
+ ? [...options.initialUserInputs]
+ : [];
+
+ this.state = {
+ workflowName: config.name,
+ currentStep: config.initialStep,
+ iteration: 0,
+ stepOutputs: new Map(),
+ userInputs,
+ agentSessions,
+ stepIterations: new Map(),
+ status: 'running',
+ };
+ }
+
+ /**
+ * Increment the iteration counter for a step and return the new value.
+ */
+ incrementStepIteration(stepName: string): number {
+ const current = this.state.stepIterations.get(stepName) ?? 0;
+ const next = current + 1;
+ this.state.stepIterations.set(stepName, next);
+ return next;
+ }
+
+ /**
+ * Add user input to state with truncation and limit handling.
+ */
+ addUserInput(input: string): void {
+ if (this.state.userInputs.length >= MAX_USER_INPUTS) {
+ this.state.userInputs.shift();
+ }
+ const truncated = input.slice(0, MAX_INPUT_LENGTH);
+ this.state.userInputs.push(truncated);
+ }
+
+ /**
+ * Get the most recent step output.
+ */
+ getPreviousOutput(): AgentResponse | undefined {
+ const outputs = Array.from(this.state.stepOutputs.values());
+ return outputs[outputs.length - 1];
+ }
+}
+
+// --- Backward-compatible function facades ---
+
+/**
+ * Create initial workflow state from config and options.
+ */
+export function createInitialState(
+ config: WorkflowConfig,
+ options: WorkflowEngineOptions,
+): WorkflowState {
+ return new StateManager(config, options).state;
+}
+
+/**
+ * Increment the iteration counter for a step and return the new value.
+ */
+export function incrementStepIteration(state: WorkflowState, stepName: string): number {
+ const current = state.stepIterations.get(stepName) ?? 0;
+ const next = current + 1;
+ state.stepIterations.set(stepName, next);
+ return next;
+}
+
+/**
+ * Add user input to state with truncation and limit handling.
+ */
+export function addUserInput(state: WorkflowState, input: string): void {
+ if (state.userInputs.length >= MAX_USER_INPUTS) {
+ state.userInputs.shift();
+ }
+ const truncated = input.slice(0, MAX_INPUT_LENGTH);
+ state.userInputs.push(truncated);
+}
+
+/**
+ * Get the most recent step output.
+ */
+export function getPreviousOutput(state: WorkflowState): AgentResponse | undefined {
+ const outputs = Array.from(state.stepOutputs.values());
+ return outputs[outputs.length - 1];
+}
diff --git a/src/workflow/transitions.ts b/src/workflow/engine/transitions.ts
similarity index 97%
rename from src/workflow/transitions.ts
rename to src/workflow/engine/transitions.ts
index 7dc34ab..22234b4 100644
--- a/src/workflow/transitions.ts
+++ b/src/workflow/engine/transitions.ts
@@ -6,7 +6,7 @@
import type {
WorkflowStep,
-} from '../models/types.js';
+} from '../../models/types.js';
/**
* Determine next step using rules-based detection.
diff --git a/src/workflow/rule-utils.ts b/src/workflow/evaluation/rule-utils.ts
similarity index 90%
rename from src/workflow/rule-utils.ts
rename to src/workflow/evaluation/rule-utils.ts
index 0d147ba..b2fdf0b 100644
--- a/src/workflow/rule-utils.ts
+++ b/src/workflow/evaluation/rule-utils.ts
@@ -2,7 +2,7 @@
* Shared rule utility functions used by both engine.ts and instruction-builder.ts.
*/
-import type { WorkflowStep } from '../models/types.js';
+import type { WorkflowStep } from '../../models/types.js';
/**
* Check whether a step has tag-based rules (i.e., rules that require
diff --git a/src/workflow/index.ts b/src/workflow/index.ts
index cb699b8..85d99e0 100644
--- a/src/workflow/index.ts
+++ b/src/workflow/index.ts
@@ -22,28 +22,28 @@ export type {
LoopCheckResult,
} from './types.js';
-// Transitions
-export { determineNextStepByRules, extractBlockedPrompt } from './transitions.js';
+// Transitions (engine/)
+export { determineNextStepByRules, extractBlockedPrompt } from './engine/transitions.js';
-// Loop detection
-export { LoopDetector } from './loop-detector.js';
+// Loop detection (engine/)
+export { LoopDetector } from './engine/loop-detector.js';
-// State management
+// State management (engine/)
export {
createInitialState,
addUserInput,
getPreviousOutput,
-} from './state-manager.js';
+} from './engine/state-manager.js';
+
+// Blocked handling (engine/)
+export { handleBlocked, type BlockedHandlerResult } from './engine/blocked-handler.js';
// Instruction building
export { InstructionBuilder, isReportObjectConfig } from './instruction/InstructionBuilder.js';
export { ReportInstructionBuilder, type ReportInstructionContext } from './instruction/ReportInstructionBuilder.js';
export { StatusJudgmentBuilder, type StatusJudgmentContext } from './instruction/StatusJudgmentBuilder.js';
-export { buildExecutionMetadata, renderExecutionMetadata, type InstructionContext, type ExecutionMetadata } from './instruction-context.js';
+export { buildExecutionMetadata, renderExecutionMetadata, type InstructionContext, type ExecutionMetadata } from './instruction/instruction-context.js';
// Rule evaluation
export { RuleEvaluator, type RuleMatch, type RuleEvaluatorContext, detectMatchedRule, evaluateAggregateConditions } from './evaluation/index.js';
export { AggregateEvaluator } from './evaluation/AggregateEvaluator.js';
-
-// Blocked handling
-export { handleBlocked, type BlockedHandlerResult } from './blocked-handler.js';
diff --git a/src/workflow/instruction/InstructionBuilder.ts b/src/workflow/instruction/InstructionBuilder.ts
index 42554e5..ab341ea 100644
--- a/src/workflow/instruction/InstructionBuilder.ts
+++ b/src/workflow/instruction/InstructionBuilder.ts
@@ -9,10 +9,10 @@
*/
import type { WorkflowStep, Language, ReportConfig, ReportObjectConfig } from '../../models/types.js';
-import { hasTagBasedRules } from '../rule-utils.js';
-import type { InstructionContext } from '../instruction-context.js';
-import { buildExecutionMetadata, renderExecutionMetadata } from '../instruction-context.js';
-import { generateStatusRulesFromRules } from '../status-rules.js';
+import { hasTagBasedRules } from '../evaluation/rule-utils.js';
+import type { InstructionContext } from './instruction-context.js';
+import { buildExecutionMetadata, renderExecutionMetadata } from './instruction-context.js';
+import { generateStatusRulesFromRules } from './status-rules.js';
import { escapeTemplateChars, replaceTemplatePlaceholders } from './escape.js';
/**
diff --git a/src/workflow/instruction/ReportInstructionBuilder.ts b/src/workflow/instruction/ReportInstructionBuilder.ts
index 78ba5f9..0960102 100644
--- a/src/workflow/instruction/ReportInstructionBuilder.ts
+++ b/src/workflow/instruction/ReportInstructionBuilder.ts
@@ -11,8 +11,8 @@
*/
import type { WorkflowStep, Language } from '../../models/types.js';
-import type { InstructionContext } from '../instruction-context.js';
-import { METADATA_STRINGS } from '../instruction-context.js';
+import type { InstructionContext } from './instruction-context.js';
+import { METADATA_STRINGS } from './instruction-context.js';
import { replaceTemplatePlaceholders } from './escape.js';
import { isReportObjectConfig, renderReportContext, renderReportOutputInstruction } from './InstructionBuilder.js';
diff --git a/src/workflow/instruction/StatusJudgmentBuilder.ts b/src/workflow/instruction/StatusJudgmentBuilder.ts
index 7c0f52f..17690e8 100644
--- a/src/workflow/instruction/StatusJudgmentBuilder.ts
+++ b/src/workflow/instruction/StatusJudgmentBuilder.ts
@@ -10,7 +10,7 @@
*/
import type { WorkflowStep, Language } from '../../models/types.js';
-import { generateStatusRulesFromRules } from '../status-rules.js';
+import { generateStatusRulesFromRules } from './status-rules.js';
/** Localized strings for status judgment phase */
const STATUS_JUDGMENT_STRINGS = {
diff --git a/src/workflow/instruction/escape.ts b/src/workflow/instruction/escape.ts
index 11aa2be..839618e 100644
--- a/src/workflow/instruction/escape.ts
+++ b/src/workflow/instruction/escape.ts
@@ -5,7 +5,7 @@
*/
import type { WorkflowStep } from '../../models/types.js';
-import type { InstructionContext } from '../instruction-context.js';
+import type { InstructionContext } from './instruction-context.js';
/**
* Escape special characters in dynamic content to prevent template injection.
diff --git a/src/workflow/instruction-context.ts b/src/workflow/instruction/instruction-context.ts
similarity index 98%
rename from src/workflow/instruction-context.ts
rename to src/workflow/instruction/instruction-context.ts
index 0b55997..e265306 100644
--- a/src/workflow/instruction-context.ts
+++ b/src/workflow/instruction/instruction-context.ts
@@ -5,7 +5,7 @@
* and renders execution metadata (working directory, rules) as markdown.
*/
-import type { AgentResponse, Language } from '../models/types.js';
+import type { AgentResponse, Language } from '../../models/types.js';
/**
* Context for building instruction from template.
diff --git a/src/workflow/status-rules.ts b/src/workflow/instruction/status-rules.ts
similarity index 97%
rename from src/workflow/status-rules.ts
rename to src/workflow/instruction/status-rules.ts
index 975af2a..0e00201 100644
--- a/src/workflow/status-rules.ts
+++ b/src/workflow/instruction/status-rules.ts
@@ -5,7 +5,7 @@
* based on the step's rule configuration.
*/
-import type { WorkflowRule, Language } from '../models/types.js';
+import type { WorkflowRule, Language } from '../../models/types.js';
/** Localized strings for rules-based status prompt */
const RULES_PROMPT_STRINGS = {
diff --git a/src/workflow/state-manager.ts b/src/workflow/state-manager.ts
deleted file mode 100644
index ad094d4..0000000
--- a/src/workflow/state-manager.ts
+++ /dev/null
@@ -1,75 +0,0 @@
-/**
- * Workflow state management
- *
- * Manages the mutable state of a workflow execution including
- * user inputs and agent sessions.
- */
-
-import type { WorkflowState, WorkflowConfig, AgentResponse } from '../models/types.js';
-import {
- MAX_USER_INPUTS,
- MAX_INPUT_LENGTH,
-} from './constants.js';
-import type { WorkflowEngineOptions } from './types.js';
-
-/**
- * Create initial workflow state from config and options.
- */
-export function createInitialState(
- config: WorkflowConfig,
- options: WorkflowEngineOptions
-): WorkflowState {
- // Restore agent sessions from options if provided
- const agentSessions = new Map();
- if (options.initialSessions) {
- for (const [agent, sessionId] of Object.entries(options.initialSessions)) {
- agentSessions.set(agent, sessionId);
- }
- }
-
- // Initialize user inputs from options if provided
- const userInputs = options.initialUserInputs
- ? [...options.initialUserInputs]
- : [];
-
- return {
- workflowName: config.name,
- currentStep: config.initialStep,
- iteration: 0,
- stepOutputs: new Map(),
- userInputs,
- agentSessions,
- stepIterations: new Map(),
- status: 'running',
- };
-}
-
-/**
- * Increment the iteration counter for a step and return the new value.
- */
-export function incrementStepIteration(state: WorkflowState, stepName: string): number {
- const current = state.stepIterations.get(stepName) ?? 0;
- const next = current + 1;
- state.stepIterations.set(stepName, next);
- return next;
-}
-
-/**
- * Add user input to state with truncation and limit handling.
- */
-export function addUserInput(state: WorkflowState, input: string): void {
- if (state.userInputs.length >= MAX_USER_INPUTS) {
- state.userInputs.shift(); // Remove oldest
- }
- const truncated = input.slice(0, MAX_INPUT_LENGTH);
- state.userInputs.push(truncated);
-}
-
-/**
- * Get the most recent step output.
- */
-export function getPreviousOutput(state: WorkflowState): AgentResponse | undefined {
- const outputs = Array.from(state.stepOutputs.values());
- return outputs[outputs.length - 1];
-}
-