From 8d977328e44fdc10f6a42c7201c171d64cb65af3 Mon Sep 17 00:00:00 2001 From: Lex Christopherson Date: Sun, 15 Feb 2026 11:35:39 -0600 Subject: [PATCH] fix(tools): write large JSON payloads to tmpfile to prevent truncation (closes #493) When init commands include many file contents (state, roadmap, requirements, etc.), the JSON output can exceed Claude Code's Bash tool buffer (~50KB), causing parse errors. The output() function now auto-detects large payloads and writes to a tmpfile, returning @file:/path instead. All workflows that consume init output handle both formats. Co-Authored-By: Claude Opus 4.6 --- get-shit-done/bin/gsd-tools.cjs | 11 ++++++++++- get-shit-done/workflows/execute-plan.md | 10 +++++++++- get-shit-done/workflows/plan-phase.md | 10 +++++++++- get-shit-done/workflows/progress.md | 10 +++++++++- 4 files changed, 37 insertions(+), 4 deletions(-) diff --git a/get-shit-done/bin/gsd-tools.cjs b/get-shit-done/bin/gsd-tools.cjs index 4ebff20ac..a3f225a76 100755 --- a/get-shit-done/bin/gsd-tools.cjs +++ b/get-shit-done/bin/gsd-tools.cjs @@ -468,7 +468,16 @@ function output(result, raw, rawValue) { if (raw && rawValue !== undefined) { process.stdout.write(String(rawValue)); } else { - process.stdout.write(JSON.stringify(result, null, 2)); + const json = JSON.stringify(result, null, 2); + // Large payloads exceed Claude Code's Bash tool buffer (~50KB). + // Write to tmpfile and output the path prefixed with @file: so callers can detect it. + if (json.length > 50000) { + const tmpPath = path.join(require('os').tmpdir(), `gsd-${Date.now()}.json`); + fs.writeFileSync(tmpPath, json, 'utf-8'); + process.stdout.write('@file:' + tmpPath); + } else { + process.stdout.write(json); + } } process.exit(0); } diff --git a/get-shit-done/workflows/execute-plan.md b/get-shit-done/workflows/execute-plan.md index 7a489adbd..b6bfcc16d 100644 --- a/get-shit-done/workflows/execute-plan.md +++ b/get-shit-done/workflows/execute-plan.md @@ -15,7 +15,15 @@ Read config.json for planning behavior settings. Load execution context (uses `init execute-phase` for full context, including file contents): ```bash -INIT=$(node ~/.claude/get-shit-done/bin/gsd-tools.cjs init execute-phase "${PHASE}" --include state,config) +INIT_RAW=$(node ~/.claude/get-shit-done/bin/gsd-tools.cjs init execute-phase "${PHASE}" --include state,config) +# Large payloads are written to a tmpfile — output starts with @file:/path +if [[ "$INIT_RAW" == @file:* ]]; then + INIT_FILE="${INIT_RAW#@file:}" + INIT=$(cat "$INIT_FILE") + rm -f "$INIT_FILE" +else + INIT="$INIT_RAW" +fi ``` Extract from init JSON: `executor_model`, `commit_docs`, `phase_dir`, `phase_number`, `plans`, `summaries`, `incomplete_plans`. diff --git a/get-shit-done/workflows/plan-phase.md b/get-shit-done/workflows/plan-phase.md index 987ced402..930aac30a 100644 --- a/get-shit-done/workflows/plan-phase.md +++ b/get-shit-done/workflows/plan-phase.md @@ -15,7 +15,15 @@ Read all files referenced by the invoking prompt's execution_context before star Load all context in one call (include file contents to avoid redundant reads): ```bash -INIT=$(node ~/.claude/get-shit-done/bin/gsd-tools.cjs init plan-phase "$PHASE" --include state,roadmap,requirements,context,research,verification,uat) +INIT_RAW=$(node ~/.claude/get-shit-done/bin/gsd-tools.cjs init plan-phase "$PHASE" --include state,roadmap,requirements,context,research,verification,uat) +# Large payloads are written to a tmpfile — output starts with @file:/path +if [[ "$INIT_RAW" == @file:* ]]; then + INIT_FILE="${INIT_RAW#@file:}" + INIT=$(cat "$INIT_FILE") + rm -f "$INIT_FILE" +else + INIT="$INIT_RAW" +fi ``` Parse JSON for: `researcher_model`, `planner_model`, `checker_model`, `research_enabled`, `plan_checker_enabled`, `commit_docs`, `phase_found`, `phase_dir`, `phase_number`, `phase_name`, `phase_slug`, `padded_phase`, `has_research`, `has_context`, `has_plans`, `plan_count`, `planning_exists`, `roadmap_exists`. diff --git a/get-shit-done/workflows/progress.md b/get-shit-done/workflows/progress.md index 8c70ebf42..12ae46e58 100644 --- a/get-shit-done/workflows/progress.md +++ b/get-shit-done/workflows/progress.md @@ -12,7 +12,15 @@ Read all files referenced by the invoking prompt's execution_context before star **Load progress context (with file contents to avoid redundant reads):** ```bash -INIT=$(node ~/.claude/get-shit-done/bin/gsd-tools.cjs init progress --include state,roadmap,project,config) +INIT_RAW=$(node ~/.claude/get-shit-done/bin/gsd-tools.cjs init progress --include state,roadmap,project,config) +# Large payloads are written to a tmpfile — output starts with @file:/path +if [[ "$INIT_RAW" == @file:* ]]; then + INIT_FILE="${INIT_RAW#@file:}" + INIT=$(cat "$INIT_FILE") + rm -f "$INIT_FILE" +else + INIT="$INIT_RAW" +fi ``` Extract from init JSON: `project_exists`, `roadmap_exists`, `state_exists`, `phases`, `current_phase`, `next_phase`, `milestone_version`, `completed_count`, `phase_count`, `paused_at`.