CCX Flow allows an external process to inject context into the current Claude Code main session via local HTTP, without patching the CC binary and without starting a new CC instance.
Current context
- Full Session ID: !
node -e "console.log(process.env.CLAUDE_SESSION_ID || '')" - Project root directory: !
node -e "const {execSync}=require('child_process'); const {homedir}=require('os'); try { const root=execSync('git rev-parse --show-toplevel 2>/dev/null',{encoding:'utf8'}).trim(); if (root && root !== '/' && root !== homedir()) { console.log(root); process.exit(0); } } catch {} console.log(process.cwd());" - Flow registry directory:
<project-root>/.tmp/ccx-flow/ skill-path: usually~/.claude/skills- Flow Bridge artifacts:
<skill-path>/scripts/flow-bootstrap.jsand<skill-path>/scripts/flow-bridge.js
Enable Flow
- Compute
sessionShortId = ${CLAUDE_SESSION_ID}the first 8 characters of. - Compute the stable port:
port =!npx @lionad/port-key ccx-flow-${CLAUDE_SESSION_ID.slice(0, 8)}
- Generate a random token:
token =!node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
- Check whether the registry file
<project-root>/.tmp/ccx-flow/<sessionShortId>.jsonexists and corresponds to a process that is still alive:- If it exists and the pid is alive, skip to step 7 and inform the user using the existing port/token/pid in the registry.
- If it does not exist or the pid is dead, continue to the next step.
- Call
TeamCreateToolto create a team:
Record the returned{ "operation": "spawnTeam", "team_name": "ccx-flow-<sessionShortId>" }team_name(CC may uniquify it; use the returned name in subsequent steps). - Call
AgentToolto create a placeholder teammateccx-flow-bridge:{ "description": "CCX Flow placeholder teammate", "prompt": "You are ccx-flow-bridge, a placeholder teammate. Do not perform any tasks, do not call tools, do not send messages. Stay idle and wait. Acknowledge once with a single 'OK' and then remain silent.", "name": "ccx-flow-bridge", "team_name": "<teamName>", "model": "haiku", "run_in_background": true } - Start Flow Bridge:
node <skill-path>/scripts/flow-bootstrap.js \ --team <teamName> \ --port <port> \ --token <token> \ --session-id ${CLAUDE_SESSION_ID} \ --registry <project-root>/.tmp/ccx-flow/<sessionShortId>.json- If there is already a live process in the registry, the output is
FLOW_BRIDGE_ALREADY_RUNNING ...。 - otherwise the output is
FLOW_BRIDGE_STARTED ...。
- If there is already a live process in the registry, the output is
- Extract the actual
port、token、pid, inform the user, and provide a curl example:curl -X POST http://127.0.0.1:<port>/inject \ -H "Authorization: Bearer <token>" \ -H "Content-Type: application/json" \ -d '{"text": "Current hour: 14:00, system load normal."}'
Disable Flow (--off)
When the user calls /ccx-flow --off when:
- Read
<project-root>/.tmp/ccx-flow/all registry files under. - For each registry entry:
- kill its bridge process (SIGTERM first, then SIGKILL if it has not exited after 2 seconds).
- Delete the corresponding CC team directory (
~/.claude/teams/<sanitized-team-name>)。 - Delete the registry file.
- Report to the user which sessions were cleaned up.
Note:
--offonly cleans up resources managed by cc-expand; it will not stop the user's own third-party Flow processes.
Security boundary
- Flow Bridge only listens on
127.0.0.1。 - Every request must carry
Authorization: Bearer <token>。 - The injected text enters the main session as ordinary teammate_message XML, and the LLM understands it as natural language; it will not automatically execute slash commands, TeamDeleteTool, or kill teammate.