clawforge-cli 1.5.3__tar.gz → 1.5.5__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (145) hide show
  1. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/CHANGELOG.md +24 -0
  2. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/Formula/clawforge.rb +3 -1
  3. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/PKG-INFO +1 -1
  4. clawforge_cli-1.5.5/VERSION +1 -0
  5. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/bin/spawn-agent.sh +8 -3
  6. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/package.json +1 -1
  7. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/pyproject.toml +1 -1
  8. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/tests/test-dx.sh +3 -3
  9. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/tests/test-observability.sh +1 -1
  10. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/tests/test-power.sh +1 -1
  11. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/tests/test-practical.sh +1 -1
  12. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/tests/test-reliability.sh +1 -1
  13. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/tests/test-web.sh +1 -1
  14. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/tui/agent.go +3 -0
  15. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/tui/dashboard.go +8 -4
  16. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/tui/keybindings.go +89 -3
  17. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/tui/model.go +2 -1
  18. clawforge_cli-1.5.3/VERSION +0 -1
  19. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/.github/workflows/ci.yml +0 -0
  20. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/.github/workflows/publish-npm.yml +0 -0
  21. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/.github/workflows/publish-pypi.yml +0 -0
  22. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/.github/workflows/update-homebrew.yml +0 -0
  23. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/.github/workflows/version-sync.yml +0 -0
  24. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/.gitignore +0 -0
  25. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/LICENSE +0 -0
  26. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/PRD-v05.md +0 -0
  27. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/PRD-v06.md +0 -0
  28. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/PRD-v07.md +0 -0
  29. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/README.md +0 -0
  30. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/SKILL.md +0 -0
  31. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/bin/attach.sh +0 -0
  32. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/bin/check-agents.sh +0 -0
  33. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/bin/clawforge +0 -0
  34. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/bin/clawforge-web +0 -0
  35. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/bin/clean.sh +0 -0
  36. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/bin/completions.sh +0 -0
  37. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/bin/config.sh +0 -0
  38. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/bin/conflicts.sh +0 -0
  39. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/bin/cost.sh +0 -0
  40. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/bin/dashboard.sh +0 -0
  41. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/bin/deps.sh +0 -0
  42. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/bin/diff.sh +0 -0
  43. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/bin/doctor.sh +0 -0
  44. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/bin/eval.sh +0 -0
  45. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/bin/export.sh +0 -0
  46. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/bin/history.sh +0 -0
  47. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/bin/init.sh +0 -0
  48. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/bin/learn.sh +0 -0
  49. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/bin/logs.sh +0 -0
  50. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/bin/memory.sh +0 -0
  51. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/bin/merge-helper.sh +0 -0
  52. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/bin/multi-review.sh +0 -0
  53. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/bin/notify.sh +0 -0
  54. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/bin/on-complete.sh +0 -0
  55. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/bin/parse-cost.sh +0 -0
  56. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/bin/pr.sh +0 -0
  57. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/bin/profile.sh +0 -0
  58. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/bin/replay.sh +0 -0
  59. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/bin/resume.sh +0 -0
  60. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/bin/review-mode.sh +0 -0
  61. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/bin/review-pr.sh +0 -0
  62. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/bin/routing.sh +0 -0
  63. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/bin/scope-task.sh +0 -0
  64. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/bin/sprint.sh +0 -0
  65. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/bin/steer.sh +0 -0
  66. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/bin/stop.sh +0 -0
  67. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/bin/summary.sh +0 -0
  68. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/bin/swarm.sh +0 -0
  69. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/bin/templates.sh +0 -0
  70. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/bin/web.sh +0 -0
  71. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/clawforge/__init__.py +0 -0
  72. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/clawforge/cli.py +0 -0
  73. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/completions/_clawforge +0 -0
  74. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/completions/clawforge.bash +0 -0
  75. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/completions/clawforge.fish +0 -0
  76. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/config/defaults.json +0 -0
  77. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/config/prompt-templates/default.md +0 -0
  78. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/config/routing-defaults.json +0 -0
  79. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/docs/IMPLEMENTATION-v04.md +0 -0
  80. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/docs/PRD-v04.md +0 -0
  81. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/docs/PUBLISHING-CHECKLIST.md +0 -0
  82. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/docs/README.md +0 -0
  83. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/docs/RELEASE.md +0 -0
  84. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/docs/architecture.md +0 -0
  85. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/docs/command-reference.md +0 -0
  86. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/docs/configuration.md +0 -0
  87. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/docs/dashboard.md +0 -0
  88. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/docs/evaluation.md +0 -0
  89. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/docs/faq.md +0 -0
  90. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/docs/fleet-ops.md +0 -0
  91. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/docs/getting-started.md +0 -0
  92. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/docs/scenarios.md +0 -0
  93. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/docs/troubleshooting.md +0 -0
  94. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/docs/workflow-modes.md +0 -0
  95. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/evals/run-log.example.jsonl +0 -0
  96. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/evals/run-log.schema.json +0 -0
  97. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/evals/scorecard.md +0 -0
  98. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/install.sh +0 -0
  99. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/lib/common.sh +0 -0
  100. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/lib/templates/bugfix.json +0 -0
  101. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/lib/templates/migration.json +0 -0
  102. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/lib/templates/refactor.json +0 -0
  103. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/lib/templates/security-audit.json +0 -0
  104. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/lib/templates/test-coverage.json +0 -0
  105. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/registry/conflicts.jsonl +0 -0
  106. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/registry/costs.jsonl +0 -0
  107. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/tests/run-all-tests.sh +0 -0
  108. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/tests/test-ci-loop.sh +0 -0
  109. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/tests/test-clean.sh +0 -0
  110. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/tests/test-cli.sh +0 -0
  111. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/tests/test-conflicts.sh +0 -0
  112. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/tests/test-cost.sh +0 -0
  113. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/tests/test-dashboard.sh +0 -0
  114. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/tests/test-deps.sh +0 -0
  115. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/tests/test-eval.sh +0 -0
  116. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/tests/test-foundation.sh +0 -0
  117. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/tests/test-history.sh +0 -0
  118. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/tests/test-init.sh +0 -0
  119. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/tests/test-learn.sh +0 -0
  120. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/tests/test-management.sh +0 -0
  121. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/tests/test-memory.sh +0 -0
  122. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/tests/test-merge.sh +0 -0
  123. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/tests/test-modes.sh +0 -0
  124. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/tests/test-multi-repo.sh +0 -0
  125. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/tests/test-notify.sh +0 -0
  126. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/tests/test-openclaw.sh +0 -0
  127. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/tests/test-registry.sh +0 -0
  128. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/tests/test-review.sh +0 -0
  129. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/tests/test-routing.sh +0 -0
  130. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/tests/test-scope.sh +0 -0
  131. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/tests/test-spawn.sh +0 -0
  132. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/tests/test-templates.sh +0 -0
  133. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/tests/test-tui.sh +0 -0
  134. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/tests/test-watch.sh +0 -0
  135. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/tui/PRD.md +0 -0
  136. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/tui/animation.go +0 -0
  137. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/tui/filter.go +0 -0
  138. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/tui/go.mod +0 -0
  139. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/tui/go.sum +0 -0
  140. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/tui/main.go +0 -0
  141. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/tui/steer.go +0 -0
  142. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/tui/styles.go +0 -0
  143. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/web/go.mod +0 -0
  144. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/web/index.html +0 -0
  145. {clawforge_cli-1.5.3 → clawforge_cli-1.5.5}/web/main.go +0 -0
@@ -1,5 +1,29 @@
1
1
  # Changelog
2
2
 
3
+ ## v1.5.5 — TUI Observability: Logs + Diff Keys
4
+
5
+ ### Added
6
+ - `l` key in TUI dashboard: show last 50 lines of agent log in preview pane (reads log file; falls back to tmux capture for live sessions)
7
+ - `d` key in TUI dashboard: show `git diff --stat HEAD` for selected agent's worktree
8
+ - `Enter` now falls back to showing log file in preview when session is gone (instead of silent no-op)
9
+ - Agent log files auto-captured to `~/.clawforge/registry/logs/<branch>.log` via `tee` on spawn
10
+ - `log_path` stored in registry and surfaced to TUI
11
+ - `Esc` closes preview/log panel
12
+ - Navigation clears stale static preview content
13
+
14
+ ### Fixed
15
+ - `Enter` on finished/dead sessions silently did nothing — now shows log output
16
+
17
+ ## v1.5.4 — TUI Attach + Agent Session Persistence
18
+
19
+ ### Fixed
20
+ - **Agent sessions stay alive after completion**: `spawn-agent.sh` now keeps the tmux session open after the agent finishes, so you can attach and review output via Enter key
21
+ - **Enter key handles missing sessions**: TUI gracefully handles dead/missing tmux sessions instead of silently failing
22
+ - **`--verbose` flag for claude**: Agent output is more visible when attached
23
+
24
+ ### Changed
25
+ - Spawn uses `claude --verbose -p` (still headless, but with visible progress) + session persistence (`read` prompt on completion)
26
+
3
27
  ## v1.5.3 — TUI Fixes
4
28
 
5
29
  ### Fixed
@@ -1,7 +1,9 @@
1
1
  class Clawforge < Formula
2
2
  desc "Multi-mode coding workflow CLI for orchestrating AI coding agents"
3
3
  homepage "https://github.com/cyperx84/clawforge"
4
- url "https://github.com/cyperx84/clawforge/archive/refs/tags/v1.5.2.tar.gz"
4
+ url "https://github.com/cyperx84/clawforge/archive/refs/tags/v1.5.4.tar.gz"
5
+ sha256 "549b3ebf95c953cd4f366b5a251786c994726761c9d4ed6255b996b6458cf695"
6
+ sha256 "2730fca0066fda9d3c864802c4346ab7c39d0daee39a254cc5cebd2694d4a1db"
5
7
  sha256 "a026cb0df5b7c3efae439a9ca912e728843deb56c70441591c9064f81a1041ae"
6
8
  sha256 "27cca187828366d138dbde81f99f313e065c656be7073452ccc016eb5dba16db"
7
9
  sha256 "ce9da13357fc54d20e59da5851ac04a4ebbff071ec5a7ce1b17b0cbe877b3416"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: clawforge-cli
3
- Version: 1.5.3
3
+ Version: 1.5.5
4
4
  Summary: Multi-mode coding workflow CLI for orchestrating AI coding agents
5
5
  Project-URL: Homepage, https://github.com/cyperx84/clawforge
6
6
  Project-URL: Repository, https://github.com/cyperx84/clawforge
@@ -0,0 +1 @@
1
+ 1.5.5
@@ -114,6 +114,8 @@ else
114
114
  fi
115
115
 
116
116
  TMUX_SESSION="agent-${SAFE_BRANCH}"
117
+ LOGFILE="${CLAWFORGE_DIR}/registry/logs/${SAFE_BRANCH}.log"
118
+ mkdir -p "$(dirname "$LOGFILE")"
117
119
  MAX_RETRIES=$(config_get max_retries 3)
118
120
 
119
121
  log_info "Spawning agent: $RESOLVED_AGENT ($MODEL)"
@@ -183,6 +185,7 @@ TASK_JSON=$(jq -n \
183
185
  --arg desc "$TASK" \
184
186
  --arg repo "$REPO_ABS" \
185
187
  --arg wt "$WORKTREE_DIR" \
188
+ --arg log "$LOGFILE" \
186
189
  --arg branch "$BRANCH" \
187
190
  --argjson started "$NOW" \
188
191
  --argjson maxRetries "$MAX_RETRIES" \
@@ -194,6 +197,7 @@ TASK_JSON=$(jq -n \
194
197
  description: $desc,
195
198
  repo: $repo,
196
199
  worktree: $wt,
200
+ log_path: $log,
197
201
  branch: $branch,
198
202
  startedAt: $started,
199
203
  status: "spawned",
@@ -220,11 +224,12 @@ fi
220
224
  # Kill existing session if present (idempotent)
221
225
  tmux kill-session -t "$TMUX_SESSION" 2>/dev/null || true
222
226
 
223
- # Build agent command
227
+ # Build agent command — interactive mode so tmux attach works
228
+ ESCAPED_PROMPT=$(echo "$FULL_PROMPT" | sed 's/"/\\"/g')
224
229
  if [[ "$RESOLVED_AGENT" == "claude" ]]; then
225
- AGENT_CMD="claude --model ${MODEL} --dangerously-skip-permissions -p \"$(echo "$FULL_PROMPT" | sed 's/"/\\"/g')\""
230
+ AGENT_CMD="claude --model ${MODEL} --dangerously-skip-permissions --verbose -p \"${ESCAPED_PROMPT}\" 2>&1 | tee \"${LOGFILE}\"; echo; echo \"[ClawForge] Agent finished. Press Enter to close.\"; read"
226
231
  elif [[ "$RESOLVED_AGENT" == "codex" ]]; then
227
- AGENT_CMD="codex --model ${MODEL} --dangerously-bypass-approvals-and-sandbox \"$(echo "$FULL_PROMPT" | sed 's/"/\\"/g')\""
232
+ AGENT_CMD="codex --model ${MODEL} --dangerously-bypass-approvals-and-sandbox \"${ESCAPED_PROMPT}\" 2>&1 | tee \"${LOGFILE}\"; echo; echo \"[ClawForge] Agent finished. Press Enter to close.\"; read"
228
233
  fi
229
234
 
230
235
  # Create tmux session and launch
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cyperx/clawforge",
3
- "version": "1.5.3",
3
+ "version": "1.5.5",
4
4
  "description": "Multi-mode coding workflow CLI for orchestrating AI coding agents",
5
5
  "bin": {
6
6
  "@cyperx/clawforge": "./bin/clawforge"
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "clawforge-cli"
3
- version = "1.5.3"
3
+ version = "1.5.5"
4
4
  description = "Multi-mode coding workflow CLI for orchestrating AI coding agents"
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -134,10 +134,10 @@ assert_contains "help shows Developer Experience" "Developer Experience" "$cli_h
134
134
  # Test 11: version
135
135
  echo "Test 11: version"
136
136
  version=$(cat "${SCRIPT_DIR}/../VERSION")
137
- if [[ "$version" == "1.5.3" ]]; then
138
- echo " ✅ version is 1.5.3"; PASS=$((PASS+1))
137
+ if [[ "$version" == "1.5.5" ]]; then
138
+ echo " ✅ version is 1.5.5"; PASS=$((PASS+1))
139
139
  else
140
- echo " ❌ version is $version, expected 1.5.3"; FAIL=$((FAIL+1))
140
+ echo " ❌ version is $version, expected 1.5.5"; FAIL=$((FAIL+1))
141
141
  fi
142
142
 
143
143
  echo ""
@@ -77,7 +77,7 @@ assert_contains "dashboard help shows p key" "preview" "$tui_help"
77
77
  # Test 7: version
78
78
  echo "Test 7: version"
79
79
  version=$(cat "${SCRIPT_DIR}/../VERSION")
80
- assert_eq "version is 1.5.3" "1.5.3" "$version"
80
+ assert_eq "version is 1.5.5" "1.5.5" "$version"
81
81
 
82
82
  echo ""
83
83
  echo "Results: $PASS passed, $FAIL failed"
@@ -121,7 +121,7 @@ assert_contains "help shows Power Features" "Power Features" "$cli_help"
121
121
  # Test 13: version
122
122
  echo "Test 13: version"
123
123
  version=$(cat "${SCRIPT_DIR}/../VERSION")
124
- assert_eq "version is 1.5.3" "1.5.3" "$version"
124
+ assert_eq "version is 1.5.5" "1.5.5" "$version"
125
125
 
126
126
  echo ""
127
127
  echo "Results: $PASS passed, $FAIL failed"
@@ -96,7 +96,7 @@ assert_contains "watch checks terminal states" "done|failed|timeout|cancelled" "
96
96
  # Test 9: version
97
97
  echo "Test 9: version"
98
98
  version=$(cat "${SCRIPT_DIR}/../VERSION")
99
- assert_eq "version is 1.5.3" "1.5.3" "$version"
99
+ assert_eq "version is 1.5.5" "1.5.5" "$version"
100
100
 
101
101
  echo ""
102
102
  echo "Results: $PASS passed, $FAIL failed"
@@ -96,7 +96,7 @@ assert_contains "help shows Reliability" "Reliability" "$cli_help"
96
96
  # Test 11: version bump
97
97
  echo "Test 11: version"
98
98
  version=$(cat "${SCRIPT_DIR}/../VERSION")
99
- assert_eq "version is 1.5.3" "1.5.3" "$version"
99
+ assert_eq "version is 1.5.5" "1.5.5" "$version"
100
100
 
101
101
  # Test 12: doctor --fix runs without errors
102
102
  echo "Test 12: doctor --fix"
@@ -107,7 +107,7 @@ assert_contains "help shows Web Dashboard" "Web Dashboard" "$cli_help"
107
107
  # Test 7: version
108
108
  echo "Test 7: version"
109
109
  version=$(cat "${SCRIPT_DIR}/../VERSION")
110
- assert_eq "version is 1.5.3" "1.5.3" "$version"
110
+ assert_eq "version is 1.5.5" "1.5.5" "$version"
111
111
 
112
112
  echo ""
113
113
  echo "Results: $PASS passed, $FAIL failed"
@@ -25,6 +25,7 @@ type Agent struct {
25
25
  Conflicts int
26
26
  Description string
27
27
  Worktree string
28
+ LogPath string
28
29
  TmuxSession string
29
30
  }
30
31
 
@@ -42,6 +43,7 @@ type registryTask struct {
42
43
  Model string `json:"model"`
43
44
  Repo string `json:"repo"`
44
45
  CIStatus string `json:"ci_status"`
46
+ LogPath string `json:"log_path"`
45
47
  }
46
48
 
47
49
  type registryFile struct {
@@ -122,6 +124,7 @@ func LoadAgents() []Agent {
122
124
  Description: t.Description,
123
125
  Task: t.Description,
124
126
  Worktree: t.Worktree,
127
+ LogPath: t.LogPath,
125
128
  TmuxSession: t.TmuxSession,
126
129
  CI: ciIndicator(t.CIStatus),
127
130
  Cost: costs[t.ID],
@@ -114,11 +114,15 @@ func renderDashboard(m Model) string {
114
114
  fmt.Sprintf("── Preview: %s ──", selAgent.ID))
115
115
  b.WriteString(previewHeader)
116
116
  b.WriteString("\n")
117
- if selAgent.Preview != "" {
118
- b.WriteString(lipgloss.NewStyle().Faint(true).Render(selAgent.Preview))
119
- } else {
120
- b.WriteString(lipgloss.NewStyle().Faint(true).Render("(no output / not running)"))
117
+ // Static content (from l/d keys) takes priority over live tmux preview.
118
+ content := m.previewContent
119
+ if content == "" {
120
+ content = selAgent.Preview
121
121
  }
122
+ if content == "" {
123
+ content = "(no output / not running)"
124
+ }
125
+ b.WriteString(lipgloss.NewStyle().Faint(true).Render(content))
122
126
  b.WriteString("\n")
123
127
  }
124
128
  }
@@ -2,6 +2,7 @@ package main
2
2
 
3
3
  import (
4
4
  "fmt"
5
+ "os"
5
6
  "os/exec"
6
7
  "strings"
7
8
 
@@ -48,33 +49,54 @@ func handleKeyPress(m Model, msg tea.KeyPressMsg) (Model, tea.Cmd) {
48
49
  case "j", "down":
49
50
  if count > 0 && m.selected < count-1 {
50
51
  m.selected++
52
+ m.previewContent = "" // clear static preview on navigation
51
53
  }
52
54
  return m, nil
53
55
 
54
56
  case "k", "up":
55
57
  if m.selected > 0 {
56
58
  m.selected--
59
+ m.previewContent = "" // clear static preview on navigation
57
60
  }
58
61
  return m, nil
59
62
 
60
63
  case "g":
61
64
  m.selected = 0
65
+ m.previewContent = ""
62
66
  return m, nil
63
67
 
64
68
  case "G":
65
69
  if count > 0 {
66
70
  m.selected = count - 1
71
+ m.previewContent = ""
67
72
  }
68
73
  return m, nil
69
74
 
70
75
  case "enter":
71
76
  // Attach to selected agent's tmux session.
77
+ // If no live session, fall back to showing log in preview.
72
78
  if count > 0 {
73
79
  agent := agents[m.selected]
74
80
  session := agent.TmuxSession
75
81
  if session == "" {
76
82
  session = "clawforge-" + agent.ID
77
83
  }
84
+ // Check if tmux session exists before trying to attach.
85
+ check := exec.Command("tmux", "has-session", "-t", session)
86
+ if err := check.Run(); err != nil {
87
+ // No live session — show log file in preview instead.
88
+ m.showPreview = true
89
+ if agent.LogPath != "" {
90
+ if data, rerr := os.ReadFile(agent.LogPath); rerr == nil && len(data) > 0 {
91
+ m.previewContent = "(log)\n" + tailLines(string(data), 40)
92
+ } else {
93
+ m.previewContent = "(no log captured — session has ended)"
94
+ }
95
+ } else {
96
+ m.previewContent = "(no live session and no log path recorded)"
97
+ }
98
+ return m, nil
99
+ }
78
100
  cmd := exec.Command("tmux", "attach-session", "-t", session)
79
101
  return m, tea.ExecProcess(cmd, func(err error) tea.Msg {
80
102
  return attachDoneMsg{err}
@@ -132,6 +154,7 @@ func handleKeyPress(m Model, msg tea.KeyPressMsg) (Model, tea.Cmd) {
132
154
  case "r":
133
155
  // Force refresh.
134
156
  m.agents = LoadAgents()
157
+ m.previewContent = ""
135
158
  // Clamp selection.
136
159
  filtered := m.filteredAgents()
137
160
  if m.selected >= len(filtered) {
@@ -139,8 +162,56 @@ func handleKeyPress(m Model, msg tea.KeyPressMsg) (Model, tea.Cmd) {
139
162
  }
140
163
  return m, nil
141
164
 
165
+ case "l":
166
+ // Show last 50 lines of agent log in preview pane.
167
+ if count > 0 {
168
+ agent := agents[m.selected]
169
+ m.showPreview = true
170
+ if agent.LogPath != "" {
171
+ if data, err := os.ReadFile(agent.LogPath); err == nil && len(data) > 0 {
172
+ m.previewContent = "(log) " + agent.ID + "\n" + tailLines(string(data), 50)
173
+ } else {
174
+ m.previewContent = "(no log yet for " + agent.ID + ")"
175
+ }
176
+ } else if agent.TmuxSession != "" {
177
+ // Fallback: capture from live tmux pane.
178
+ out, err := exec.Command("tmux", "capture-pane", "-t", agent.TmuxSession, "-p", "-S", "-50").Output()
179
+ if err == nil && len(out) > 0 {
180
+ m.previewContent = "(tmux) " + agent.ID + "\n" + string(out)
181
+ } else {
182
+ m.previewContent = "(no output captured for " + agent.ID + ")"
183
+ }
184
+ } else {
185
+ m.previewContent = "(no log path or tmux session for " + agent.ID + ")"
186
+ }
187
+ }
188
+ return m, nil
189
+
190
+ case "d":
191
+ // Show git diff --stat for selected agent's worktree.
192
+ if count > 0 {
193
+ agent := agents[m.selected]
194
+ m.showPreview = true
195
+ if agent.Worktree != "" {
196
+ out, err := exec.Command("git", "-C", agent.Worktree, "diff", "--stat", "HEAD").Output()
197
+ if err == nil && len(out) > 0 {
198
+ m.previewContent = "(diff) " + agent.ID + "\n" + string(out)
199
+ } else {
200
+ out2, _ := exec.Command("git", "-C", agent.Worktree, "status", "--short").Output()
201
+ m.previewContent = "(diff) " + agent.ID + " — no diff yet\n" + string(out2)
202
+ }
203
+ } else {
204
+ m.previewContent = "(no worktree recorded for " + agent.ID + ")"
205
+ }
206
+ }
207
+ return m, nil
208
+
142
209
  case "p":
210
+ // Toggle live preview pane (clears any static log/diff content).
143
211
  m.showPreview = !m.showPreview
212
+ if !m.showPreview {
213
+ m.previewContent = ""
214
+ }
144
215
  return m, nil
145
216
 
146
217
  case "n":
@@ -165,6 +236,10 @@ func handleKeyPress(m Model, msg tea.KeyPressMsg) (Model, tea.Cmd) {
165
236
  if m.showHelp {
166
237
  m.showHelp = false
167
238
  }
239
+ if m.previewContent != "" {
240
+ m.previewContent = ""
241
+ m.showPreview = false
242
+ }
168
243
  return m, nil
169
244
  }
170
245
 
@@ -200,18 +275,20 @@ func renderHelpOverlay(width int) string {
200
275
  desc string
201
276
  }{
202
277
  {"j/k", "Navigate agent list"},
203
- {"Enter", "Attach to selected agent's tmux session"},
278
+ {"Enter", "Attach to tmux session (or show log if session gone)"},
279
+ {"l", "Show agent log in preview pane"},
280
+ {"d", "Show git diff in preview pane"},
204
281
  {"s", "Steer selected agent (prompts for message)"},
205
282
  {"x", "Stop selected agent"},
206
283
  {"/", "Filter agents"},
207
284
  {"1/2/3", "Views: all / running / finished"},
208
285
  {"Tab", "Cycle views"},
209
286
  {"n", "Nudge selected running agent"},
210
- {"p", "Toggle output preview pane"},
287
+ {"p", "Toggle live output preview pane"},
211
288
  {"r", "Force refresh"},
212
289
  {"g/G", "Go to top/bottom"},
213
290
  {"?", "Toggle help overlay"},
214
- {"Esc", "Close modal/overlay"},
291
+ {"Esc", "Close modal/overlay/preview"},
215
292
  {"q", "Quit dashboard"},
216
293
  }
217
294
 
@@ -227,3 +304,12 @@ func renderHelpOverlay(width int) string {
227
304
  content := strings.Join(lines, "\n")
228
305
  return helpOverlayStyle.Render(content)
229
306
  }
307
+
308
+ // tailLines returns the last n lines of s.
309
+ func tailLines(s string, n int) string {
310
+ lines := strings.Split(strings.TrimRight(s, "\n"), "\n")
311
+ if len(lines) <= n {
312
+ return strings.Join(lines, "\n")
313
+ }
314
+ return strings.Join(lines[len(lines)-n:], "\n")
315
+ }
@@ -28,7 +28,8 @@ type Model struct {
28
28
  width int
29
29
  height int
30
30
  steerMode bool
31
- showPreview bool
31
+ showPreview bool
32
+ previewContent string // static content to show in preview (log, diff)
32
33
  steerInput string
33
34
  confirmStop bool
34
35
  frame int
@@ -1 +0,0 @@
1
- 1.5.3
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes