Final rebrand: rename remaining Rust source file to complete the gsd → forge transition. All parser references already use forge_parser after earlier commits. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5 KiB
5 KiB
HeadlessJsonResult Reference
When using --output-format json, SF collects events silently and emits a single HeadlessJsonResult JSON object to stdout at process exit. This is the structured result for orchestrator decision-making.
Obtaining the Result
# Capture the JSON result
RESULT=$(sf headless --output-format json next 2>/dev/null)
EXIT=$?
# Parse fields with jq
echo "$RESULT" | jq '.status'
echo "$RESULT" | jq '.cost.total'
echo "$RESULT" | jq '.nextAction'
Important: Progress text goes to stderr. The JSON result goes to stdout. Redirect stderr to /dev/null when parsing stdout.
Field Reference
Top-Level Fields
| Field | Type | Description |
|---|---|---|
status |
"success" | "error" | "blocked" | "cancelled" | "timeout" |
Final session status. Maps directly to exit codes. |
exitCode |
number |
Process exit code: 0 (success), 1 (error/timeout), 10 (blocked), 11 (cancelled). |
sessionId |
string | undefined |
Session identifier. Pass to --resume <id> to continue this session. |
duration |
number |
Session wall-clock duration in milliseconds. |
cost |
CostObject |
Token usage and cost breakdown. See below. |
toolCalls |
number |
Total number of tool calls made during the session. |
events |
number |
Total number of events processed during the session. |
milestone |
string | undefined |
Active milestone ID (e.g. "M001"). |
phase |
string | undefined |
Current SF phase at session end (e.g. "executing", "blocked", "complete"). |
nextAction |
string | undefined |
Recommended next action from the state machine (e.g. "dispatch", "complete"). |
artifacts |
string[] | undefined |
Paths to artifacts created or modified during the session. |
commits |
string[] | undefined |
Git commit SHAs created during the session. |
Status → Exit Code Mapping
| Status | Exit Code | Constant | Meaning |
|---|---|---|---|
success |
0 |
EXIT_SUCCESS |
Unit or milestone completed successfully |
error |
1 |
EXIT_ERROR |
Runtime error or LLM failure |
timeout |
1 |
EXIT_ERROR |
--timeout deadline exceeded |
blocked |
10 |
EXIT_BLOCKED |
Execution blocked — needs human intervention |
cancelled |
11 |
EXIT_CANCELLED |
Cancelled by user or orchestrator |
Cost Object
| Field | Type | Description |
|---|---|---|
cost.total |
number |
Total cost in USD for the session. |
cost.input_tokens |
number |
Number of input tokens consumed. |
cost.output_tokens |
number |
Number of output tokens generated. |
cost.cache_read_tokens |
number |
Number of tokens served from prompt cache. |
cost.cache_write_tokens |
number |
Number of tokens written to prompt cache. |
Parsing Patterns
Decision-Making After Each Step
RESULT=$(sf headless --output-format json next 2>/dev/null)
EXIT=$?
case $EXIT in
0)
PHASE=$(echo "$RESULT" | jq -r '.phase')
NEXT=$(echo "$RESULT" | jq -r '.nextAction')
echo "Success — phase: $PHASE, next: $NEXT"
;;
1)
STATUS=$(echo "$RESULT" | jq -r '.status')
echo "Failed — status: $STATUS"
;;
10)
echo "Blocked — needs intervention"
sf headless query | jq '.state'
;;
11)
echo "Cancelled"
;;
esac
Cost Tracking
RESULT=$(sf headless --output-format json next 2>/dev/null)
COST=$(echo "$RESULT" | jq -r '.cost.total')
INPUT=$(echo "$RESULT" | jq -r '.cost.input_tokens')
OUTPUT=$(echo "$RESULT" | jq -r '.cost.output_tokens')
echo "Cost: \$$COST (${INPUT} in / ${OUTPUT} out)"
Session Resumption
# First run — capture session ID
RESULT=$(sf headless --output-format json next 2>/dev/null)
SESSION_ID=$(echo "$RESULT" | jq -r '.sessionId')
# Resume the same session later
sf headless --resume "$SESSION_ID" --output-format json next 2>/dev/null
Artifact Collection
RESULT=$(sf headless --output-format json auto 2>/dev/null)
# List files created/modified
echo "$RESULT" | jq -r '.artifacts[]?'
# List commits made
echo "$RESULT" | jq -r '.commits[]?'
Example Result
{
"status": "success",
"exitCode": 0,
"sessionId": "abc123def456",
"duration": 45200,
"cost": {
"total": 0.42,
"input_tokens": 15000,
"output_tokens": 3500,
"cache_read_tokens": 8000,
"cache_write_tokens": 2000
},
"toolCalls": 12,
"events": 87,
"milestone": "M001",
"phase": "executing",
"nextAction": "dispatch",
"artifacts": [
".sf/milestones/M001/slices/S01/tasks/T01-SUMMARY.md"
],
"commits": [
"a1b2c3d"
]
}
Combined with query for Full Picture
The HeadlessJsonResult captures what happened during a session. Use query for the current project state:
# What happened in this step?
RESULT=$(sf headless --output-format json next 2>/dev/null)
echo "$RESULT" | jq '{status, cost: .cost.total, phase}'
# What's the overall project state now?
sf headless query | jq '{phase: .state.phase, progress: .state.progress, totalCost: .cost.total}'