"""c_org_node_trigger (03-C §3.3): subprocess wrapper skeleton. U-005 scope: shell out `claude --agent -p ...` and return the text response -- "骨架,不含完整状态机" per development-plan.md. No task-lifecycle state machine yet: TASK_NOT_READY/CONCURRENCY_LIMIT_REACHED/USAGE_LIMIT_REACHED handling is U-010's job, not this one. Identity comes entirely from `.claude/agents/.md` in the target project (see src/agent_profile.py) -- this module only assembles the prompt and parses the response, no role-isolation logic of its own (03-C §3.3). """ import json import subprocess from src.agent_profile import ORG_NODES class OrgNodeTriggerError(Exception): """Raised when the claude subprocess exits non-zero or reports is_error.""" def trigger_org_node(org_node, prompt, target_folder_path, timeout=300): """Runs `claude --agent -p ` with cwd=target_folder_path. `.claude/agents/.md` must already be deployed in target_folder_path (agent_profile.deploy_agent_definitions) -- this function does not deploy it. Returns the response text (the JSON `result` field). """ if org_node not in ORG_NODES: raise ValueError(org_node) result = subprocess.run( [ "claude", "--agent", org_node, "-p", prompt, "--output-format", "json", "--permission-mode", "auto", ], cwd=target_folder_path, stdin=subprocess.DEVNULL, capture_output=True, text=True, timeout=timeout, ) if result.returncode != 0: # 2026-07-31 fix: a real Coder call once failed with returncode=1 and # empty stderr, and the exception message (stderr-only, as it used to # be) gave zero diagnostic signal -- whatever claude actually said # about the failure was in stdout, which this branch was discarding. # Both are now included (truncated -- stdout in JSON mode can be # large) so a future real failure is actually debuggable. raise OrgNodeTriggerError( "claude --agent {} exited {}\nstderr: {}\nstdout (truncated): {}".format( org_node, result.returncode, result.stderr, result.stdout[:4000] ) ) payload = json.loads(result.stdout) if payload.get("is_error"): raise OrgNodeTriggerError("claude --agent {} reported is_error: {}".format(org_node, payload)) return payload["result"]