forge-workflow 0.0.9 → 0.0.10

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 (102) hide show
  1. package/.claude/commands/dev.md +2 -2
  2. package/.claude/commands/plan.md +2 -2
  3. package/.claude/commands/ship.md +2 -2
  4. package/.claude/commands/status.md +4 -4
  5. package/.cline/workflows/dev.md +2 -2
  6. package/.cline/workflows/plan.md +2 -2
  7. package/.cline/workflows/ship.md +2 -2
  8. package/.cline/workflows/status.md +4 -4
  9. package/.codex/skills/dev/SKILL.md +2 -2
  10. package/.codex/skills/plan/SKILL.md +2 -2
  11. package/.codex/skills/ship/SKILL.md +2 -2
  12. package/.codex/skills/status/SKILL.md +4 -4
  13. package/.cursor/commands/dev.md +2 -2
  14. package/.cursor/commands/plan.md +2 -2
  15. package/.cursor/commands/ship.md +2 -2
  16. package/.cursor/commands/status.md +4 -4
  17. package/.github/prompts/dev.prompt.md +2 -2
  18. package/.github/prompts/plan.prompt.md +2 -2
  19. package/.github/prompts/ship.prompt.md +2 -2
  20. package/.github/prompts/status.prompt.md +4 -4
  21. package/.github/workflows/beads-to-github.yml +43 -10
  22. package/.github/workflows/github-to-beads.yml +10 -7
  23. package/.kilocode/workflows/dev.md +2 -2
  24. package/.kilocode/workflows/plan.md +2 -2
  25. package/.kilocode/workflows/ship.md +2 -2
  26. package/.kilocode/workflows/status.md +4 -4
  27. package/.opencode/commands/dev.md +2 -2
  28. package/.opencode/commands/plan.md +2 -2
  29. package/.opencode/commands/ship.md +2 -2
  30. package/.opencode/commands/status.md +4 -4
  31. package/.roo/commands/dev.md +2 -2
  32. package/.roo/commands/plan.md +2 -2
  33. package/.roo/commands/ship.md +2 -2
  34. package/.roo/commands/status.md +4 -4
  35. package/AGENTS.md +1 -0
  36. package/CLAUDE.md +12 -0
  37. package/bin/forge.js +10 -5
  38. package/docs/BEADS_GITHUB_SYNC.md +26 -0
  39. package/docs/TOOLCHAIN.md +130 -148
  40. package/lib/beads-bootstrap.js +225 -0
  41. package/lib/beads-health-check.js +55 -10
  42. package/lib/beads-setup.js +104 -28
  43. package/lib/beads-sync-scaffold.js +11 -6
  44. package/lib/commands/_issue.js +11 -1
  45. package/lib/commands/issues.js +49 -0
  46. package/lib/commands/recommend.js +22 -1
  47. package/lib/commands/setup.js +16 -10
  48. package/lib/commands/status.js +181 -0
  49. package/lib/commands/team.js +11 -1
  50. package/lib/commands/test.js +37 -2
  51. package/lib/commands/validate.js +14 -8
  52. package/lib/commands/worktree.js +27 -54
  53. package/lib/dep-guard/keyword-ripple.js +184 -0
  54. package/lib/detect-worktree.js +9 -10
  55. package/lib/forge-issues.js +326 -0
  56. package/lib/issue-sync/authority.js +100 -0
  57. package/lib/issue-sync/github-pull.js +184 -0
  58. package/lib/issue-sync/import-primitives.js +98 -0
  59. package/lib/issue-sync/legacy-link-bridge.js +436 -0
  60. package/lib/issue-sync/link-store.js +292 -0
  61. package/lib/issue-sync/project-github.js +123 -0
  62. package/lib/issue-sync/reconcile.js +195 -0
  63. package/lib/issue-sync/schema.js +126 -0
  64. package/lib/lefthook-check.js +5 -2
  65. package/lib/project-memory.js +564 -0
  66. package/lib/runtime-health.js +100 -12
  67. package/lib/smart-status/conflicts.js +205 -0
  68. package/lib/smart-status/scoring.js +177 -0
  69. package/lib/status/beads-snapshot.js +102 -0
  70. package/lib/status/presenter.js +65 -0
  71. package/lib/workflow/enforce-stage.js +3 -1
  72. package/lib/workflow/state-manager.js +164 -8
  73. package/package.json +12 -4
  74. package/scripts/beads-context.sh +124 -5
  75. package/scripts/beads-context.test.js +21 -4
  76. package/scripts/beads-migrate-to-dolt.sh +7 -0
  77. package/scripts/beads-upgrade-smoke.sh +263 -0
  78. package/scripts/behavioral-judge.sh +115 -11
  79. package/scripts/benchmark.js +349 -63
  80. package/scripts/dep-guard-analyze.js +52 -17
  81. package/scripts/dep-guard-keyword-ripple.js +29 -0
  82. package/scripts/dep-guard-render-review.js +86 -0
  83. package/scripts/dep-guard.sh +45 -232
  84. package/scripts/forge-team/lib/sync-github.sh +160 -28
  85. package/scripts/forge-team/tests/sync-github.test.sh +195 -58
  86. package/scripts/github-beads-sync/index.mjs +122 -98
  87. package/scripts/github-beads-sync/mapping.mjs +54 -0
  88. package/scripts/github-beads-sync/reverse-sync-cli.mjs +2 -2
  89. package/scripts/github-beads-sync/reverse-sync.mjs +31 -7
  90. package/scripts/lib/beads-migrate-to-dolt.mjs +503 -0
  91. package/scripts/preflight.sh +181 -0
  92. package/scripts/smart-status-score.js +31 -0
  93. package/scripts/smart-status-sessions.js +51 -0
  94. package/scripts/smart-status.sh +74 -329
  95. package/scripts/sync-agentic-workflow.js +48 -0
  96. package/scripts/test-ci-shard.js +244 -0
  97. package/scripts/test-dashboard.js +188 -52
  98. package/scripts/test-full-suite.js +186 -0
  99. package/scripts/test-profile.js +278 -0
  100. package/scripts/test.js +219 -28
  101. package/scripts/validate.js +143 -0
  102. package/scripts/validate.sh +18 -1
@@ -3,16 +3,17 @@ set -euo pipefail
3
3
 
4
4
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
5
5
 
6
- # ── Setup temp dir for test isolation ────────────────────────────────────
7
6
  TEST_TMP="$(mktemp -d)"
8
7
  trap 'rm -rf "$TEST_TMP" "$mock_dir"' EXIT
9
8
 
10
- # ── Create mock dir and log file ─────────────────────────────────────────
11
- mock_dir="$(mktemp -d /c/tmp/forge-sync-test.XXXXXX)"
9
+ mock_dir="$(mktemp -d)"
10
+ portable_bin="$(mktemp -d)"
12
11
  log_file="$mock_dir/calls.log"
13
12
  touch "$log_file"
13
+ REAL_GREP="$(command -v grep)"
14
+ ORIGINAL_PATH="$PATH"
15
+ export REAL_GREP
14
16
 
15
- # ── Mock gh ──────────────────────────────────────────────────────────────
16
17
  cat > "$mock_dir/gh" << 'MOCK'
17
18
  #!/usr/bin/env bash
18
19
  echo "$*" >> "$LOG_FILE"
@@ -23,45 +24,69 @@ case "$1 $2" in
23
24
  "issue comment") echo ""; exit 0 ;;
24
25
  "api user") echo "testuser"; exit 0 ;;
25
26
  esac
26
- echo "unknown gh command: $*" >&2; exit 1
27
+ echo "unknown gh command: $*" >&2
28
+ exit 1
27
29
  MOCK
28
30
  chmod +x "$mock_dir/gh"
29
31
 
30
- # ── Mock bd ──────────────────────────────────────────────────────────────
31
32
  cat > "$mock_dir/bd" << 'MOCK'
32
33
  #!/usr/bin/env bash
33
34
  echo "$*" >> "$LOG_FILE"
34
35
  case "$1" in
35
36
  show)
36
- echo "$2 · Test Issue [github_issue:42]"
37
- echo "Title: Test Issue"
37
+ if [[ "${3:-}" == "--json" ]]; then
38
+ cat <<'JSON'
39
+ {"id":"beads-001","github":{"number":42,"nodeId":"I_kwDOForge42","url":"https://github.com/test/repo/issues/42"},"shared":{"title":"Test Issue"}}
40
+ JSON
41
+ else
42
+ echo "o $2 - Test Issue"
43
+ echo "Title: Test Issue"
44
+ fi
38
45
  ;;
39
46
  set-state) echo "State set" ;;
40
47
  esac
41
48
  MOCK
42
49
  chmod +x "$mock_dir/bd"
43
50
 
44
- # ── Mock bd that returns NO github_issue ─────────────────────────────────
45
51
  cat > "$mock_dir/bd-no-gh" << 'MOCK'
46
52
  #!/usr/bin/env bash
47
53
  echo "$*" >> "$LOG_FILE"
48
54
  case "$1" in
49
55
  show)
50
- echo "$2 · Test Issue"
51
- echo "Title: Test Issue"
56
+ if [[ "${3:-}" == "--json" ]]; then
57
+ cat <<'JSON'
58
+ {"id":"beads-999","shared":{"title":"Test Issue"}}
59
+ JSON
60
+ else
61
+ echo "o $2 - Test Issue"
62
+ echo "Title: Test Issue"
63
+ fi
52
64
  ;;
53
65
  set-state) echo "State set" ;;
54
66
  esac
55
67
  MOCK
56
68
  chmod +x "$mock_dir/bd-no-gh"
57
69
 
58
- # Export env for mocks
70
+ cat > "$portable_bin/grep" << 'MOCK'
71
+ #!/usr/bin/env bash
72
+ for arg in "$@"; do
73
+ case "$arg" in
74
+ -P|--perl-regexp|-*P*)
75
+ echo "grep: PCRE support disabled for test" >&2
76
+ exit 2
77
+ ;;
78
+ esac
79
+ done
80
+ exec "$REAL_GREP" "$@"
81
+ MOCK
82
+ chmod +x "$portable_bin/grep"
83
+
59
84
  export LOG_FILE="$log_file"
60
85
  export GH_CMD="$mock_dir/gh"
61
86
  export BD_CMD="$mock_dir/bd"
62
87
  export TEAM_MAP_ROOT="$TEST_TMP"
88
+ mapping_file="$TEAM_MAP_ROOT/.github/beads-mapping.json"
63
89
 
64
- # Source the library under test
65
90
  source "$SCRIPT_DIR/lib/sync-github.sh"
66
91
 
67
92
  PASS=0
@@ -70,119 +95,193 @@ FAIL=0
70
95
  assert_eq() {
71
96
  local label="$1" expected="$2" actual="$3"
72
97
  if [[ "$actual" == "$expected" ]]; then
73
- PASS=$((PASS + 1)); echo " PASS: $label"
98
+ PASS=$((PASS + 1))
99
+ echo " PASS: $label"
74
100
  else
75
- FAIL=$((FAIL + 1)); echo " FAIL: $label (expected '$expected', got '$actual')"
101
+ FAIL=$((FAIL + 1))
102
+ echo " FAIL: $label (expected '$expected', got '$actual')"
76
103
  fi
77
104
  }
78
105
 
79
106
  assert_contains() {
80
107
  local label="$1" expected="$2" actual="$3"
81
108
  if [[ "$actual" == *"$expected"* ]]; then
82
- PASS=$((PASS + 1)); echo " PASS: $label"
109
+ PASS=$((PASS + 1))
110
+ echo " PASS: $label"
111
+ else
112
+ FAIL=$((FAIL + 1))
113
+ echo " FAIL: $label (expected to contain '$expected', got '$actual')"
114
+ fi
115
+ }
116
+
117
+ assert_not_contains() {
118
+ local label="$1" unexpected="$2" actual="$3"
119
+ if [[ "$actual" == *"$unexpected"* ]]; then
120
+ FAIL=$((FAIL + 1))
121
+ echo " FAIL: $label (did not expect '$unexpected', got '$actual')"
83
122
  else
84
- FAIL=$((FAIL + 1)); echo " FAIL: $label (expected to contain '$expected', got '$actual')"
123
+ PASS=$((PASS + 1))
124
+ echo " PASS: $label"
85
125
  fi
86
126
  }
87
127
 
88
128
  assert_exit() {
89
129
  local label="$1" expected="$2" actual="$3"
90
130
  if [[ "$actual" -eq "$expected" ]]; then
91
- PASS=$((PASS + 1)); echo " PASS: $label"
131
+ PASS=$((PASS + 1))
132
+ echo " PASS: $label"
92
133
  else
93
- FAIL=$((FAIL + 1)); echo " FAIL: $label (expected exit $expected, got $actual)"
134
+ FAIL=$((FAIL + 1))
135
+ echo " FAIL: $label (expected exit $expected, got $actual)"
94
136
  fi
95
137
  }
96
138
 
97
- # ── Test 1: sync_issue_create — creates GitHub issue, calls bd set-state ─
98
- echo "── Test 1: sync_issue_create ──"
99
- > "$log_file" # clear log
139
+ echo "== Test 1: sync_issue_create =="
140
+ > "$log_file"
100
141
  rc=0
101
142
  sync_issue_create "beads-001" || rc=$?
102
143
  assert_exit "exit code 0" 0 "$rc"
103
- # Check gh was called with issue create
104
144
  log_contents="$(cat "$log_file")"
105
145
  assert_contains "gh issue create called" "issue create" "$log_contents"
106
146
  assert_contains "title includes Test Issue" "Test Issue" "$log_contents"
107
147
  assert_contains "body includes beads-001" "beads-001" "$log_contents"
108
- # Check bd set-state was called with github_issue=42
109
- assert_contains "bd set-state called" "set-state" "$log_contents"
110
- assert_contains "github_issue=42 set" "github_issue=42" "$log_contents"
148
+ assert_contains "sync create writes legacy github_issue state" "github_issue=42" "$log_contents"
149
+ assert_contains "sync create calls set-state for hook compatibility" "set-state" "$log_contents"
150
+ assert_contains "sync create persists mapping entry" '"42": "beads-001"' "$(cat "$mapping_file")"
111
151
 
112
- # ── Test 2: sync_issue_claim — updates GitHub assignee ───────────────────
113
- echo ""
114
- echo "── Test 2: sync_issue_claim ──"
152
+ echo
153
+ echo "== Test 2: sync_issue_claim =="
115
154
  > "$log_file"
116
155
  unset _GITHUB_USER_CACHE
117
156
  rc=0
118
157
  sync_issue_claim "beads-001" || rc=$?
119
158
  assert_exit "exit code 0" 0 "$rc"
120
159
  log_contents="$(cat "$log_file")"
160
+ assert_contains "canonical lookup uses --json" "--json" "$log_contents"
121
161
  assert_contains "gh issue edit called" "issue edit" "$log_contents"
122
162
  assert_contains "add-assignee used" "--add-assignee" "$log_contents"
123
163
  assert_contains "assignee is testuser" "testuser" "$log_contents"
124
164
 
125
- # ── Test 3: sync_issue_status — removes old labels, adds new one ─────────
126
- echo ""
127
- echo "── Test 3: sync_issue_status ──"
165
+ echo
166
+ echo "== Test 3: sync_issue_status =="
128
167
  > "$log_file"
129
168
  rc=0
130
169
  sync_issue_status "beads-001" "in_progress" || rc=$?
131
170
  assert_exit "exit code 0" 0 "$rc"
132
171
  log_contents="$(cat "$log_file")"
172
+ assert_contains "canonical lookup uses --json" "--json" "$log_contents"
133
173
  assert_contains "gh issue edit called" "issue edit" "$log_contents"
134
174
  assert_contains "removes status/open" "--remove-label status/open" "$log_contents"
135
175
  assert_contains "adds status/in-progress" "--add-label status/in-progress" "$log_contents"
136
176
 
137
- # ── Test 4: sync_issue_close — closes GitHub issue ───────────────────────
138
- echo ""
139
- echo "── Test 4: sync_issue_close ──"
177
+ echo
178
+ echo "== Test 4: sync_issue_close =="
140
179
  > "$log_file"
141
180
  rc=0
142
181
  sync_issue_close "beads-001" || rc=$?
143
182
  assert_exit "exit code 0" 0 "$rc"
144
183
  log_contents="$(cat "$log_file")"
184
+ assert_contains "canonical lookup uses --json" "--json" "$log_contents"
145
185
  assert_contains "gh issue close called" "issue close" "$log_contents"
146
186
  assert_contains "closes issue 42" " 42" "$log_contents"
147
187
 
148
- # ── Test 5: sync_issue_deps — adds comment on GitHub issue ───────────────
149
- echo ""
150
- echo "── Test 5: sync_issue_deps ──"
188
+ echo
189
+ echo "== Test 5: sync_issue_deps =="
151
190
  > "$log_file"
152
191
  rc=0
153
192
  sync_issue_deps "beads-001" "beads-002" || rc=$?
154
193
  assert_exit "exit code 0" 0 "$rc"
155
194
  log_contents="$(cat "$log_file")"
195
+ assert_contains "canonical lookup uses --json" "--json" "$log_contents"
156
196
  assert_contains "gh issue comment called" "issue comment" "$log_contents"
157
197
  assert_contains "Blocked by referenced" "Blocked by #42" "$log_contents"
158
198
 
159
- # ── Test 6: _get_github_issue_number — extracts number from bd show ──────
160
- echo ""
161
- echo "── Test 6: _get_github_issue_number ──"
199
+ echo
200
+ echo "== Test 6: _get_github_issue_number =="
162
201
  result="$(_get_github_issue_number "beads-001")"
163
202
  assert_eq "extracts issue number 42" "42" "$result"
203
+ log_contents="$(cat "$log_file")"
204
+ assert_contains "_get_github_issue_number uses JSON" "--json" "$log_contents"
164
205
 
165
- # ── Test 7: Missing GitHub issue number → returns error ──────────────────
166
- echo ""
167
- echo "── Test 7: Missing GitHub issue number ──"
206
+ echo
207
+ echo "== Test 7: Mapping fallback when canonical GitHub number missing =="
208
+ mkdir -p "$(dirname "$mapping_file")"
209
+ cat > "$mapping_file" << 'JSON'
210
+ {
211
+ "77": "beads-999"
212
+ }
213
+ JSON
214
+ export BD_CMD="$mock_dir/bd-no-gh"
215
+ result="$(_get_github_issue_number "beads-999")"
216
+ assert_eq "mapping fallback returns issue number 77" "77" "$result"
217
+ export BD_CMD="$mock_dir/bd"
218
+
219
+ echo
220
+ echo "== Test 8: Mapping fallback returns newest issue number =="
221
+ cat > "$mapping_file" << 'JSON'
222
+ {
223
+ "77": "beads-999",
224
+ "91": "beads-999",
225
+ "88": "beads-other"
226
+ }
227
+ JSON
228
+ export BD_CMD="$mock_dir/bd-no-gh"
229
+ result="$(_get_github_issue_number "beads-999")"
230
+ assert_eq "mapping fallback returns newest matching issue number" "91" "$result"
231
+ export BD_CMD="$mock_dir/bd"
232
+
233
+ echo
234
+ echo "== Test 9: Missing canonical GitHub number =="
235
+ rm -f "$mapping_file"
168
236
  export BD_CMD="$mock_dir/bd-no-gh"
169
237
  rc=0
170
238
  result="$(_get_github_issue_number "beads-999" 2>/dev/null)" || rc=$?
171
- assert_exit "returns error when no github_issue" 1 "$rc"
172
- # Restore normal bd mock
239
+ assert_exit "returns error when no canonical github number" 1 "$rc"
240
+ export BD_CMD="$mock_dir/bd"
241
+
242
+ echo
243
+ echo "== Test 10: Legacy github_issue state fallback =="
244
+ cat > "$mock_dir/bd-legacy" << 'MOCK'
245
+ #!/usr/bin/env bash
246
+ echo "$*" >> "$LOG_FILE"
247
+ case "$1" in
248
+ show)
249
+ if [[ "${3:-}" == "--json" ]]; then
250
+ cat <<'JSON'
251
+ {"id":"beads-legacy","github_issue":43,"shared":{"title":"Legacy Issue"}}
252
+ JSON
253
+ else
254
+ echo "o $2 - Legacy Issue"
255
+ echo "github_issue:43"
256
+ echo "Title: Legacy Issue"
257
+ fi
258
+ ;;
259
+ set-state) echo "State set" ;;
260
+ esac
261
+ MOCK
262
+ chmod +x "$mock_dir/bd-legacy"
263
+ export BD_CMD="$mock_dir/bd-legacy"
264
+ rc=0
265
+ result="$(_get_github_issue_number "beads-legacy" 2>/dev/null)" || rc=$?
266
+ assert_exit "legacy github_issue exits successfully" 0 "$rc"
267
+ assert_eq "legacy github_issue returns number" "43" "$result"
173
268
  export BD_CMD="$mock_dir/bd"
174
269
 
175
- # ── Test 8: Injection in issue title → sanitized before gh call ──────────
176
- echo ""
177
- echo "── Test 8: Injection in title sanitized ──"
178
- # Create a bd mock that returns injection-laden title
270
+ echo
271
+ echo "== Test 11: Injection in title sanitized =="
179
272
  cat > "$mock_dir/bd-inject" << 'MOCK'
180
273
  #!/usr/bin/env bash
181
274
  echo "$*" >> "$LOG_FILE"
182
275
  case "$1" in
183
276
  show)
184
- echo '○ beads-inject · Evil $(rm -rf /) Issue [github_issue:99]'
185
- echo 'Title: Evil $(rm -rf /) Issue'
277
+ if [[ "${3:-}" == "--json" ]]; then
278
+ cat <<'JSON'
279
+ {"id":"beads-inject","github":{"number":99,"url":"https://github.com/test/repo/issues/99"},"shared":{"title":"Evil $(rm -rf /) Issue"}}
280
+ JSON
281
+ else
282
+ echo 'o beads-inject - Evil $(rm -rf /) Issue'
283
+ echo 'Title: Evil $(rm -rf /) Issue'
284
+ fi
186
285
  ;;
187
286
  set-state) echo "State set" ;;
188
287
  esac
@@ -194,16 +293,54 @@ rc=0
194
293
  sync_issue_create "beads-inject" || rc=$?
195
294
  assert_exit "exit code 0 with injection" 0 "$rc"
196
295
  log_contents="$(cat "$log_file")"
197
- # The $(...) pattern must NOT appear in the gh call
198
296
  if [[ "$log_contents" == *'$(rm'* ]]; then
199
- FAIL=$((FAIL + 1)); echo " FAIL: injection NOT sanitized — \$(rm found in gh call"
297
+ FAIL=$((FAIL + 1))
298
+ echo " FAIL: injection NOT sanitized - \$\(rm found in gh call"
200
299
  else
201
- PASS=$((PASS + 1)); echo " PASS: injection sanitized — no \$(rm in gh call"
300
+ PASS=$((PASS + 1))
301
+ echo " PASS: injection sanitized - no \$\(rm in gh call"
202
302
  fi
203
- # Restore normal bd mock
204
303
  export BD_CMD="$mock_dir/bd"
205
304
 
206
- # ── Results ──────────────────────────────────────────────────────────────
207
- echo ""
305
+ echo
306
+ echo "== Test 11: sync_issue_create without grep -P =="
307
+ export PATH="$portable_bin:$ORIGINAL_PATH"
308
+ > "$log_file"
309
+ rc=0
310
+ sync_issue_create "beads-001" || rc=$?
311
+ assert_exit "sync_issue_create works when grep -P is unavailable" 0 "$rc"
312
+ log_contents="$(cat "$log_file")"
313
+ assert_contains "gh issue create called without PCRE grep" "issue create" "$log_contents"
314
+ export PATH="$ORIGINAL_PATH"
315
+
316
+ echo
317
+ echo "== Test 12: sync_issue_create with summary-only bd show =="
318
+ cat > "$mock_dir/bd-summary-only" << 'MOCK'
319
+ #!/usr/bin/env bash
320
+ echo "$*" >> "$LOG_FILE"
321
+ case "$1" in
322
+ show)
323
+ if [[ "${3:-}" == "--json" ]]; then
324
+ cat <<'JSON'
325
+ {"id":"beads-summary","github":{"number":77,"url":"https://github.com/test/repo/issues/77"},"shared":{"title":"Summary Only Title"}}
326
+ JSON
327
+ else
328
+ echo "o $2 - Summary Only Title"
329
+ fi
330
+ ;;
331
+ set-state) echo "State set" ;;
332
+ esac
333
+ MOCK
334
+ chmod +x "$mock_dir/bd-summary-only"
335
+ export BD_CMD="$mock_dir/bd-summary-only"
336
+ > "$log_file"
337
+ rc=0
338
+ sync_issue_create "beads-summary" || rc=$?
339
+ assert_exit "sync_issue_create works with summary-only bd show" 0 "$rc"
340
+ log_contents="$(cat "$log_file")"
341
+ assert_contains "summary-only title is passed to gh issue create" "Summary Only Title" "$log_contents"
342
+ export BD_CMD="$mock_dir/bd"
343
+
344
+ echo
208
345
  echo "Results: $PASS passed, $FAIL failed"
209
346
  [[ $FAIL -eq 0 ]] && exit 0 || exit 1