From dad627ef0307e7d677719132ac31a5b758f453e4 Mon Sep 17 00:00:00 2001 From: nrslib <38722970+nrslib@users.noreply.github.com> Date: Mon, 26 Jan 2026 19:08:22 +0900 Subject: [PATCH] =?UTF-8?q?=E3=83=A2=E3=83=87=E3=83=AB=E3=82=92=E9=81=B8?= =?UTF-8?q?=E6=8A=9E=E5=8F=AF=E8=83=BD=E3=81=AB=E5=A4=89=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CLAUDE.md | 17 +++++++ README.md | 82 +++++++++++++++++++++++++++++++-- package.json | 1 + resources/global/en/config.yaml | 5 ++ resources/global/ja/config.yaml | 5 ++ src/agents/runner.ts | 26 ++++++++--- src/codex/client.ts | 33 ++++++++++++- src/config/globalConfig.ts | 4 ++ src/config/workflowLoader.ts | 1 + src/models/schemas.ts | 2 + src/models/types.ts | 3 ++ src/workflow/engine.ts | 1 + 12 files changed, 169 insertions(+), 11 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 950404b..db0c502 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -107,6 +107,8 @@ steps: - name: step-name agent: ~/.takt/agents/default/coder.md # Path to agent prompt agent_name: coder # Display name (optional) + provider: codex # claude|codex (optional) + model: opus # Model name (optional) instruction_template: | {task} {previous_response} @@ -131,6 +133,21 @@ steps: | `{git_diff}` | Current git diff (uncommitted changes) | | `{report_dir}` | Report directory name (e.g., `20250126-143052-task-summary`) | +### Model Resolution + +Model is resolved in the following priority order: + +1. **Workflow step `model`** - Highest priority (specified in step YAML) +2. **Custom agent `model`** - Agent-level model in `.takt/agents.yaml` +3. **Global config `model`** - Default model in `~/.takt/config.yaml` +4. **Provider default** - Falls back to provider's default (Claude: sonnet, Codex: gpt-5.2-codex) + +Example `~/.takt/config.yaml`: +```yaml +provider: claude +model: opus # Default model for all steps (unless overridden) +``` + ## TypeScript Notes - ESM modules with `.js` extensions in imports diff --git a/README.md b/README.md index a8532a2..e28163d 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,18 @@ # TAKT -**T**ask **A**gent **K**oordination **T**ool - Multi-agent orchestration system for Claude Code (Codex support planned). +đŸ‡ŻđŸ‡” [æ—„æœŹèȘžăƒ‰ă‚­ăƒ„ュント](./docs/README.ja.md) + +**T**ask **A**gent **K**oordination **T**ool - Multi-agent orchestration system for Claude Code and OpenAI Codex. > **Note**: This project is developed at my own pace. See [Disclaimer](#disclaimer) for details. +TAKT is built with TAKT (dogfooding). + ## Requirements -- [Claude Code](https://docs.anthropic.com/en/docs/claude-code) must be installed and configured +- [Claude Code](https://docs.anthropic.com/en/docs/claude-code) or Codex must be installed and configured + +TAKT supports both Claude Code and Codex as providers; you can choose the provider during setup. ## Installation @@ -50,8 +56,20 @@ name: default max_iterations: 10 steps: + - name: plan + agent: planner + provider: claude # Optional: claude or codex + model: opus # Claude: opus/sonnet/haiku, Codex: gpt-5.2-codex/gpt-5.1-codex + instruction_template: | + {task} + transitions: + - condition: done + next_step: implement + - name: implement agent: coder + provider: codex + model: gpt-5.2-codex # Codex model example instruction_template: | {task} transitions: @@ -62,6 +80,7 @@ steps: - name: review agent: architect + model: sonnet # Model alias (no provider = uses global default) transitions: - condition: approved next_step: COMPLETE @@ -84,20 +103,76 @@ agents: - name: my-reviewer prompt_file: .takt/prompts/reviewer.md allowed_tools: [Read, Glob, Grep] + provider: claude # Optional: claude or codex + model: opus # Claude: opus/sonnet/haiku or full name (claude-opus-4-5-20251101) status_patterns: approved: "\\[APPROVE\\]" rejected: "\\[REJECT\\]" + + - name: my-codex-agent + prompt_file: .takt/prompts/analyzer.md + provider: codex + model: gpt-5.2-codex # Codex: gpt-5.2-codex, gpt-5.1-codex, etc. ``` +## Model Selection + +### Claude Models + +You can specify models using either **aliases** or **full model names**: + +**Aliases** (recommended for simplicity): +- `opus` - Claude Opus 4.5 (highest reasoning capability) +- `sonnet` - Claude Sonnet 4.5 (balanced, best for most tasks) +- `haiku` - Claude Haiku 4.5 (fast and efficient) +- `opusplan` - Opus for planning, Sonnet for execution +- `default` - Recommended model for your account type + +**Full model names** (recommended for production): +- `claude-opus-4-5-20251101` +- `claude-sonnet-4-5-20250929` +- `claude-haiku-4-5-20250101` + +### Codex Models + +Available Codex models: +- `gpt-5.2-codex` - Latest agentic coding model (default) +- `gpt-5.1-codex` - Previous generation +- `gpt-5.1-codex-max` - Optimized for long-running tasks +- `gpt-5.1-codex-mini` - Smaller, cost-effective version +- `codex-1` - Specialized model aligned with coding preferences + ## Project Structure ``` ~/.takt/ -├── config.yaml # Global config +├── config.yaml # Global config (provider, model, workflows, etc.) ├── workflows/ # Workflow definitions └── agents/ # Agent prompt files ``` +### Global Configuration + +Configure default provider and model in `~/.takt/config.yaml`: + +```yaml +# ~/.takt/config.yaml +language: en +default_workflow: default +log_level: info +provider: claude # Default provider: claude or codex +model: sonnet # Default model (optional) +trusted_directories: + - /path/to/trusted/dir +``` + +**Model Resolution Priority:** +1. Workflow step `model` (highest priority) +2. Custom agent `model` +3. Global config `model` +4. Provider default (Claude: sonnet, Codex: gpt-5.2-codex) + + ## Practical Usage Guide ### Resuming Sessions with `-r` @@ -296,7 +371,6 @@ This ensures the project works correctly in a clean Node.js 20 environment. ## Documentation -- đŸ‡ŻđŸ‡” [æ—„æœŹèȘžăƒ‰ă‚­ăƒ„ュント](./docs/README.ja.md) - Japanese documentation - [Workflow Guide](./docs/workflows.md) - Create and customize workflows - [Agent Guide](./docs/agents.md) - Configure custom agents - [Changelog](./CHANGELOG.md) - Version history diff --git a/package.json b/package.json index 82c8ac5..10817ab 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ "types": "dist/index.d.ts", "bin": { "takt": "./bin/takt", + "takt-dev": "./bin/takt", "takt-cli": "./dist/cli.js" }, "scripts": { diff --git a/resources/global/en/config.yaml b/resources/global/en/config.yaml index 891f41f..880636f 100644 --- a/resources/global/en/config.yaml +++ b/resources/global/en/config.yaml @@ -16,6 +16,11 @@ log_level: info # Provider runtime: claude or codex provider: claude +# Default model (optional) +# Claude: opus, sonnet, haiku, opusplan, default, or full model name +# Codex: gpt-5.2-codex, gpt-5.1-codex, etc. +# model: sonnet + # Debug settings (optional) # debug: # enabled: false diff --git a/resources/global/ja/config.yaml b/resources/global/ja/config.yaml index fa34808..80bd3f6 100644 --- a/resources/global/ja/config.yaml +++ b/resources/global/ja/config.yaml @@ -16,6 +16,11 @@ log_level: info # ăƒ—ăƒ­ăƒă‚€ăƒ€ăƒŒ: claude ăŸăŸăŻ codex provider: claude +# ăƒ‡ăƒ•ă‚©ăƒ«ăƒˆăƒąăƒ‡ăƒ« (ă‚Șăƒ—ă‚·ăƒ§ăƒł) +# Claude: opus, sonnet, haiku, opusplan, default, ăŸăŸăŻăƒ•ăƒ«ăƒąăƒ‡ăƒ«ć +# Codex: gpt-5.2-codex, gpt-5.1-codex ăȘど +# model: sonnet + # ăƒ‡ăƒăƒƒă‚°èš­ćźš (ă‚Șăƒ—ă‚·ăƒ§ăƒł) # debug: # enabled: false diff --git a/src/agents/runner.ts b/src/agents/runner.ts index 13bc421..0d9a7c7 100644 --- a/src/agents/runner.ts +++ b/src/agents/runner.ts @@ -54,6 +54,18 @@ function resolveProvider(cwd: string, options?: RunAgentOptions, agentConfig?: C return 'claude'; } +function resolveModel(cwd: string, options?: RunAgentOptions, agentConfig?: CustomAgentConfig): string | undefined { + if (options?.model) return options.model; + if (agentConfig?.model) return agentConfig.model; + try { + const globalConfig = loadGlobalConfig(); + if (globalConfig.model) return globalConfig.model; + } catch { + // Ignore missing global config + } + return undefined; +} + /** Get git diff for review context */ export function getGitDiff(cwd: string): string { try { @@ -91,7 +103,7 @@ export async function runCustomAgent( cwd: options.cwd, sessionId: options.sessionId, allowedTools, - model: options.model || agentConfig.model, + model: resolveModel(options.cwd, options, agentConfig), onStream: options.onStream, onPermissionRequest: options.onPermissionRequest, onAskUserQuestion: options.onAskUserQuestion, @@ -106,7 +118,7 @@ export async function runCustomAgent( cwd: options.cwd, sessionId: options.sessionId, allowedTools, - model: options.model || agentConfig.model, + model: resolveModel(options.cwd, options, agentConfig), onStream: options.onStream, onPermissionRequest: options.onPermissionRequest, onAskUserQuestion: options.onAskUserQuestion, @@ -119,11 +131,12 @@ export async function runCustomAgent( const systemPrompt = loadAgentPrompt(agentConfig); const tools = allowedTools; const provider = resolveProvider(options.cwd, options, agentConfig); + const model = resolveModel(options.cwd, options, agentConfig); if (provider === 'codex') { const callOptions: CodexCallOptions = { cwd: options.cwd, sessionId: options.sessionId, - model: options.model || agentConfig.model, + model, statusPatterns: agentConfig.statusPatterns, onStream: options.onStream, }; @@ -134,7 +147,7 @@ export async function runCustomAgent( cwd: options.cwd, sessionId: options.sessionId, allowedTools: tools, - model: options.model || agentConfig.model, + model, statusPatterns: agentConfig.statusPatterns, onStream: options.onStream, onPermissionRequest: options.onPermissionRequest, @@ -196,12 +209,13 @@ export async function runAgent( const systemPrompt = loadAgentPromptFromPath(options.agentPath); const tools = options.allowedTools; const provider = resolveProvider(options.cwd, options); + const model = resolveModel(options.cwd, options); if (provider === 'codex') { const callOptions: CodexCallOptions = { cwd: options.cwd, sessionId: options.sessionId, - model: options.model, + model, systemPrompt, onStream: options.onStream, }; @@ -212,7 +226,7 @@ export async function runAgent( cwd: options.cwd, sessionId: options.sessionId, allowedTools: tools, - model: options.model, + model, systemPrompt, onStream: options.onStream, onPermissionRequest: options.onPermissionRequest, diff --git a/src/codex/client.ts b/src/codex/client.ts index d54d225..8412904 100644 --- a/src/codex/client.ts +++ b/src/codex/client.ts @@ -367,6 +367,7 @@ export async function callCodex( const { events } = await thread.runStreamed(fullPrompt); let content = ''; + const contentOffsets = new Map(); let success = true; let failureMessage = ''; const startedItems = new Set(); @@ -406,6 +407,20 @@ export async function callCodex( if (event.type === 'item.updated') { const item = event.item as CodexItem | undefined; if (item) { + if (item.type === 'agent_message' && typeof item.text === 'string') { + const itemId = item.id; + const text = item.text; + if (itemId) { + const prev = contentOffsets.get(itemId) ?? 0; + if (text.length > prev) { + if (prev === 0 && content.length > 0) { + content += '\n'; + } + content += text.slice(prev); + contentOffsets.set(itemId, text.length); + } + } + } emitCodexItemUpdate(item, options.onStream, startedItems, outputOffsets, textOffsets, thinkingOffsets); } continue; @@ -415,7 +430,23 @@ export async function callCodex( const item = event.item as CodexItem | undefined; if (item) { if (item.type === 'agent_message' && typeof item.text === 'string') { - content = item.text; + const itemId = item.id; + const text = item.text; + if (itemId) { + const prev = contentOffsets.get(itemId) ?? 0; + if (text.length > prev) { + if (prev === 0 && content.length > 0) { + content += '\n'; + } + content += text.slice(prev); + contentOffsets.set(itemId, text.length); + } + } else if (text) { + if (content.length > 0) { + content += '\n'; + } + content += text; + } } emitCodexItemCompleted( item, diff --git a/src/config/globalConfig.ts b/src/config/globalConfig.ts index 2daccce..4e4f5ca 100644 --- a/src/config/globalConfig.ts +++ b/src/config/globalConfig.ts @@ -30,6 +30,7 @@ export function loadGlobalConfig(): GlobalConfig { defaultWorkflow: parsed.default_workflow, logLevel: parsed.log_level, provider: parsed.provider, + model: parsed.model, debug: parsed.debug ? { enabled: parsed.debug.enabled, logFile: parsed.debug.log_file, @@ -47,6 +48,9 @@ export function saveGlobalConfig(config: GlobalConfig): void { log_level: config.logLevel, provider: config.provider, }; + if (config.model) { + raw.model = config.model; + } if (config.debug) { raw.debug = { enabled: config.debug.enabled, diff --git a/src/config/workflowLoader.ts b/src/config/workflowLoader.ts index ee1ca70..5e9d3f1 100644 --- a/src/config/workflowLoader.ts +++ b/src/config/workflowLoader.ts @@ -68,6 +68,7 @@ function normalizeWorkflowConfig(raw: unknown, workflowDir: string): WorkflowCon agentPath: resolveAgentPathForWorkflow(step.agent, workflowDir), allowedTools: step.allowed_tools, provider: step.provider, + model: step.model, instructionTemplate: step.instruction_template || step.instruction || '{task}', transitions: step.transitions.map((t) => ({ condition: t.condition, diff --git a/src/models/schemas.ts b/src/models/schemas.ts index 97a7e95..bf0757f 100644 --- a/src/models/schemas.ts +++ b/src/models/schemas.ts @@ -61,6 +61,7 @@ export const WorkflowStepRawSchema = z.object({ agent_name: z.string().optional(), allowed_tools: z.array(z.string()).optional(), provider: z.enum(['claude', 'codex']).optional(), + model: z.string().optional(), instruction: z.string().optional(), instruction_template: z.string().optional(), pass_previous_response: z.boolean().optional().default(true), @@ -115,6 +116,7 @@ export const GlobalConfigSchema = z.object({ default_workflow: z.string().optional().default('default'), log_level: z.enum(['debug', 'info', 'warn', 'error']).optional().default('info'), provider: z.enum(['claude', 'codex']).optional().default('claude'), + model: z.string().optional(), debug: DebugConfigSchema.optional(), }); diff --git a/src/models/types.ts b/src/models/types.ts index 2964711..dc284e4 100644 --- a/src/models/types.ts +++ b/src/models/types.ts @@ -66,6 +66,8 @@ export interface WorkflowStep { agentPath?: string; /** Provider override for this step */ provider?: 'claude' | 'codex'; + /** Model override for this step */ + model?: string; instructionTemplate: string; transitions: WorkflowTransition[]; passPreviousResponse: boolean; @@ -143,6 +145,7 @@ export interface GlobalConfig { defaultWorkflow: string; logLevel: 'debug' | 'info' | 'warn' | 'error'; provider?: 'claude' | 'codex'; + model?: string; debug?: DebugConfig; } diff --git a/src/workflow/engine.ts b/src/workflow/engine.ts index c5324d0..958c8f9 100644 --- a/src/workflow/engine.ts +++ b/src/workflow/engine.ts @@ -149,6 +149,7 @@ export class WorkflowEngine extends EventEmitter { agentPath: step.agentPath, allowedTools: step.allowedTools, provider: step.provider, + model: step.model, onStream: this.options.onStream, onPermissionRequest: this.options.onPermissionRequest, onAskUserQuestion: this.options.onAskUserQuestion,