Skip to content

feat(flow): bash step parseEnvelope — unwrap claude --print JSON envelope (#90/#68)#168

Open
siddharth-bhansali wants to merge 1 commit into
mainfrom
feature/int-3263-cli-bash-step-parseenvelope-unwrap-claude-print-json
Open

feat(flow): bash step parseEnvelope — unwrap claude --print JSON envelope (#90/#68)#168
siddharth-bhansali wants to merge 1 commit into
mainfrom
feature/int-3263-cli-bash-step-parseenvelope-unwrap-claude-print-json

Conversation

@siddharth-bhansali

Copy link
Copy Markdown
Collaborator

What

Adds parseEnvelope: true to bash steps, so flows that call claude --print --output-format json stop hand-rolling envelope-unwrap logic.

Closes #90. Closes #68 (the One-CLI side — see note below).

The problem

claude --print --output-format json returns a CLI envelope, not the model's JSON:

{ "type": "result", "result": "```json\n{ ...the actual answer... }\n```" }

Every flow that consumes it re-implements the same three defensive layers — envelope unwrap, code-fence strip, preamble removal — ~20 copies of an unwrap() helper in the reference pipeline. Miss any layer → silent data loss or a crash.

The fix

With parseEnvelope: true, the bash step does it once, centrally: extract .result → strip code fences → slice off any conversational preamble/trailer → parse the inner JSON as $.steps.<id>.output. It accepts the full envelope, a bare result string, or already-clean JSON. If the unwrapped payload isn't valid JSON, the step fails with a clear, snippet-bearing error instead of passing broken data downstream (the explicit ask in #68).

{ "type": "bash", "bash": {
    "command": "cat /tmp/data.json | claude --print 'Analyze' --output-format json",
    "timeout": 180000, "parseEnvelope": true } }

On #68

#68 also asks that claude --print itself emit clean JSON — that's an Anthropic-CLI change, outside this repo. This PR fixes the part One controls (flow-side unwrap), so #68 closes as covered here; the upstream-CLI ask can live on if still wanted.

Implementation

  • flow-types.ts: FlowBashConfig.parseEnvelope.
  • flow-engine.ts: unwrapClaudeEnvelope + stripToJson (reuse existing stripCodeFences); wired into executeBashStep (precedence: parseEnvelope > parseJson > raw stdout).
  • flow-schema.ts: bash descriptor field + the one guide flows example switched to parseEnvelope.
  • Docs: flows.md (bash step + the file-write→bash→code AI pattern), guide-content.ts.

Tests

flow-bash-envelope.test.ts (+10): unwrapClaudeEnvelope across fenced / bare / preamble / trailer / bare-string / already-clean / invalid-throws, plus real-bash integration through executeFlow (a printf envelope unwraps to clean output; bad JSON rejects the run). 180/180 suite pass; typecheck + build green. v1.47.13.

🤖 Generated with Claude Code

…lope (#90, #68)

Flows calling `claude --print --output-format json` via bash steps get a CLI
envelope `{type:"result", result:"```json\n{...}\n```"}` and every flow
re-implements the same three defensive layers (envelope unwrap, code-fence
strip, preamble removal) — ~20 hand-rolled copies in the reference pipeline.

Add `parseEnvelope: true` on bash steps: after the command runs, extract
`.result`, strip code fences, slice off any conversational preamble/trailer,
and parse the inner JSON as `$.steps.<id>.output`. Accepts the full envelope, a
bare result string, or already-clean JSON. If the unwrapped payload isn't valid
JSON the step FAILS with a clear, snippet-bearing error instead of passing
broken data downstream (per #68).

#68's other half — making `claude --print` itself emit clean JSON — is an
Anthropic-CLI change, not ours; closed as covered on the One side by this.

- flow-types.ts: FlowBashConfig.parseEnvelope.
- flow-engine.ts: unwrapClaudeEnvelope + stripToJson (reuses stripCodeFences);
  wired into executeBashStep (parseEnvelope > parseJson > raw stdout).
- flow-schema.ts: bash descriptor field + example switched to parseEnvelope.
- docs: flows.md (bash + AI pattern), guide-content.ts.
- tests: flow-bash-envelope.test.ts — unwrap (fences/preamble/trailer/bare/
  passthrough/invalid-throws) + real-bash integration through executeFlow.
  180/180 suite pass; typecheck + build green. v1.47.13.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@linear

linear Bot commented Jun 23, 2026

Copy link
Copy Markdown

INT-3263

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

1 participant