forge-workflow 0.0.5 → 0.0.6

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 (129) hide show
  1. package/.claude/commands/dev.md +6 -1
  2. package/.claude/commands/plan.md +59 -14
  3. package/.claude/commands/premerge.md +10 -0
  4. package/.claude/commands/review.md +7 -1
  5. package/.claude/commands/ship.md +95 -47
  6. package/.claude/commands/status.md +42 -0
  7. package/.claude/commands/validate.md +7 -1
  8. package/.claude/commands/verify.md +52 -4
  9. package/.claude/rules/workflow.md +16 -0
  10. package/.claude/scripts/greptile-resolve.sh +32 -0
  11. package/.cline/workflows/dev.md +6 -1
  12. package/.cline/workflows/plan.md +59 -14
  13. package/.cline/workflows/premerge.md +10 -0
  14. package/.cline/workflows/review.md +7 -1
  15. package/.cline/workflows/ship.md +95 -47
  16. package/.cline/workflows/status.md +42 -0
  17. package/.cline/workflows/validate.md +7 -1
  18. package/.cline/workflows/verify.md +52 -4
  19. package/.codex/skills/dev/SKILL.md +6 -1
  20. package/.codex/skills/plan/SKILL.md +59 -14
  21. package/.codex/skills/premerge/SKILL.md +10 -0
  22. package/.codex/skills/review/SKILL.md +7 -1
  23. package/.codex/skills/ship/SKILL.md +95 -47
  24. package/.codex/skills/status/SKILL.md +42 -0
  25. package/.codex/skills/validate/SKILL.md +7 -1
  26. package/.codex/skills/verify/SKILL.md +52 -4
  27. package/.cursor/commands/dev.md +6 -1
  28. package/.cursor/commands/plan.md +59 -14
  29. package/.cursor/commands/premerge.md +10 -0
  30. package/.cursor/commands/review.md +7 -1
  31. package/.cursor/commands/ship.md +95 -47
  32. package/.cursor/commands/status.md +42 -0
  33. package/.cursor/commands/validate.md +7 -1
  34. package/.cursor/commands/verify.md +52 -4
  35. package/.github/prompts/dev.prompt.md +6 -1
  36. package/.github/prompts/plan.prompt.md +59 -14
  37. package/.github/prompts/premerge.prompt.md +10 -0
  38. package/.github/prompts/review.prompt.md +7 -1
  39. package/.github/prompts/ship.prompt.md +95 -47
  40. package/.github/prompts/status.prompt.md +42 -0
  41. package/.github/prompts/validate.prompt.md +7 -1
  42. package/.github/prompts/verify.prompt.md +52 -4
  43. package/.kilocode/workflows/dev.md +6 -1
  44. package/.kilocode/workflows/plan.md +59 -14
  45. package/.kilocode/workflows/premerge.md +10 -0
  46. package/.kilocode/workflows/review.md +7 -1
  47. package/.kilocode/workflows/ship.md +95 -47
  48. package/.kilocode/workflows/status.md +42 -0
  49. package/.kilocode/workflows/validate.md +7 -1
  50. package/.kilocode/workflows/verify.md +52 -4
  51. package/.opencode/commands/dev.md +6 -1
  52. package/.opencode/commands/plan.md +59 -14
  53. package/.opencode/commands/premerge.md +10 -0
  54. package/.opencode/commands/review.md +7 -1
  55. package/.opencode/commands/ship.md +95 -47
  56. package/.opencode/commands/status.md +42 -0
  57. package/.opencode/commands/validate.md +7 -1
  58. package/.opencode/commands/verify.md +52 -4
  59. package/.roo/commands/dev.md +6 -1
  60. package/.roo/commands/plan.md +59 -14
  61. package/.roo/commands/premerge.md +10 -0
  62. package/.roo/commands/review.md +7 -1
  63. package/.roo/commands/ship.md +95 -47
  64. package/.roo/commands/status.md +42 -0
  65. package/.roo/commands/validate.md +7 -1
  66. package/.roo/commands/verify.md +52 -4
  67. package/AGENTS.md +97 -0
  68. package/CLAUDE.md +10 -0
  69. package/README.md +2 -2
  70. package/bin/forge-cmd.js +5 -1
  71. package/bin/forge-preflight.js +15 -2
  72. package/bin/forge.js +178 -9
  73. package/docs/ENHANCED_ONBOARDING.md +96 -86
  74. package/docs/ROADMAP.md +2 -2
  75. package/docs/TOOLCHAIN.md +23 -0
  76. package/docs/VALIDATION.md +1 -1
  77. package/lefthook.yml +11 -0
  78. package/lib/agents-config.js +2 -2
  79. package/lib/commands/_registry.js +134 -0
  80. package/lib/commands/clean.js +181 -0
  81. package/lib/commands/dev.js +58 -0
  82. package/lib/commands/push.js +196 -0
  83. package/lib/commands/recommend.js +1 -1
  84. package/lib/commands/sync.js +55 -0
  85. package/lib/commands/team.js +37 -0
  86. package/lib/commands/test.js +207 -0
  87. package/lib/commands/worktree.js +310 -0
  88. package/lib/docs-command.js +51 -0
  89. package/lib/docs-copy.js +50 -0
  90. package/lib/freshness-token.js +148 -0
  91. package/lib/greptile-match.js +80 -0
  92. package/lib/reset.js +309 -0
  93. package/lib/task-ownership.js +117 -0
  94. package/package.json +2 -2
  95. package/scripts/beads-context.sh +157 -22
  96. package/scripts/beads-context.test.js +5 -1
  97. package/scripts/check-forge-token.js +98 -0
  98. package/scripts/conflict-detect.sh +2 -2
  99. package/scripts/dep-guard.sh +6 -28
  100. package/scripts/file-index.sh +117 -23
  101. package/scripts/forge-team/index.sh +86 -0
  102. package/scripts/forge-team/lib/agent-prompt.sh +52 -0
  103. package/scripts/forge-team/lib/claim.sh +256 -0
  104. package/scripts/forge-team/lib/dashboard.sh +341 -0
  105. package/scripts/forge-team/lib/epic.sh +332 -0
  106. package/scripts/forge-team/lib/hooks.sh +253 -0
  107. package/scripts/forge-team/lib/identity.sh +235 -0
  108. package/scripts/forge-team/lib/sync-github.sh +317 -0
  109. package/scripts/forge-team/lib/verify.sh +284 -0
  110. package/scripts/forge-team/lib/workload.sh +296 -0
  111. package/scripts/forge-team/tests/agent-prompt.test.sh +72 -0
  112. package/scripts/forge-team/tests/claim.test.sh +179 -0
  113. package/scripts/forge-team/tests/dashboard.test.sh +170 -0
  114. package/scripts/forge-team/tests/dispatcher.test.sh +79 -0
  115. package/scripts/forge-team/tests/epic.test.sh +176 -0
  116. package/scripts/forge-team/tests/hooks.test.sh +239 -0
  117. package/scripts/forge-team/tests/identity.test.sh +176 -0
  118. package/scripts/forge-team/tests/integration.test.sh +371 -0
  119. package/scripts/forge-team/tests/sync-github.test.sh +209 -0
  120. package/scripts/forge-team/tests/verify.test.sh +314 -0
  121. package/scripts/forge-team/tests/workflow-integration.test.sh +43 -0
  122. package/scripts/forge-team/tests/workload.test.sh +209 -0
  123. package/scripts/lib/eval-runner.js +39 -0
  124. package/scripts/lib/jsonl-lock.sh +48 -0
  125. package/scripts/lib/sanitize.sh +116 -0
  126. package/scripts/pr-coordinator.sh +706 -0
  127. package/scripts/smart-status.sh +37 -10
  128. package/scripts/sync-utils.sh +24 -29
  129. package/scripts/test.js +3 -1
@@ -0,0 +1,86 @@
1
+ #!/usr/bin/env bash
2
+ # forge-team — Team orchestration plugin for Forge.
3
+ #
4
+ # Subcommands:
5
+ # workload Show team workload by developer
6
+ # epic Epic progress rollup
7
+ # dashboard Team health dashboard
8
+ # add Add developer to team map
9
+ # verify Check 1:1 Beads<>GitHub enforcement
10
+ # sync Manual GitHub<>Beads sync
11
+ # claim Claim an issue with pre-check
12
+ # help Show usage
13
+ #
14
+ # Exit codes: 0=success, 1=error, 2=validation error
15
+
16
+ set -euo pipefail
17
+
18
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
19
+
20
+ # Source libs (graceful if missing — stubs used in early dev)
21
+ [[ -f "$SCRIPT_DIR/lib/agent-prompt.sh" ]] && source "$SCRIPT_DIR/lib/agent-prompt.sh"
22
+ [[ -f "$SCRIPT_DIR/lib/identity.sh" ]] && source "$SCRIPT_DIR/lib/identity.sh"
23
+ [[ -f "$SCRIPT_DIR/lib/workload.sh" ]] && source "$SCRIPT_DIR/lib/workload.sh"
24
+ [[ -f "$SCRIPT_DIR/lib/epic.sh" ]] && source "$SCRIPT_DIR/lib/epic.sh"
25
+ [[ -f "$SCRIPT_DIR/lib/dashboard.sh" ]] && source "$SCRIPT_DIR/lib/dashboard.sh"
26
+ [[ -f "$SCRIPT_DIR/lib/hooks.sh" ]] && source "$SCRIPT_DIR/lib/hooks.sh"
27
+ [[ -f "$SCRIPT_DIR/lib/verify.sh" ]] && source "$SCRIPT_DIR/lib/verify.sh"
28
+ [[ -f "$SCRIPT_DIR/lib/claim.sh" ]] && source "$SCRIPT_DIR/lib/claim.sh"
29
+
30
+ # Source shared forge utilities
31
+ FORGE_SCRIPTS="$(cd "$SCRIPT_DIR/.." && pwd)"
32
+ [[ -f "$FORGE_SCRIPTS/lib/sanitize.sh" ]] && source "$FORGE_SCRIPTS/lib/sanitize.sh"
33
+
34
+ # ── Stub implementations ──
35
+ # cmd_workload provided by lib/workload.sh
36
+ # cmd_epic provided by lib/epic.sh
37
+ # cmd_dashboard provided by lib/dashboard.sh
38
+ cmd_add() { auto_detect_identity "$@"; }
39
+ # cmd_verify provided by lib/verify.sh
40
+ cmd_sync() { forge_team_sync "$@"; }
41
+ cmd_claim() { forge_team_claim "$@"; }
42
+
43
+ cmd_help() {
44
+ cat <<'EOF'
45
+ Usage: forge team <subcommand> [args...]
46
+
47
+ Subcommands:
48
+ workload [--developer=<user>] [--me] Show team workload
49
+ epic <issue-id> Epic progress rollup
50
+ dashboard Team health dashboard
51
+ add [--github=<user>] Add developer to team map
52
+ verify Check 1:1 Beads<>GitHub sync
53
+ sync Manual GitHub<>Beads sync
54
+ claim <issue-id> [--force] Claim issue with pre-check
55
+ help Show this message
56
+ EOF
57
+ }
58
+
59
+ # ── Main dispatcher ──
60
+ main() {
61
+ if [[ $# -lt 1 ]]; then
62
+ cmd_help >&2
63
+ exit 1
64
+ fi
65
+
66
+ local subcommand="$1"
67
+ shift
68
+
69
+ case "$subcommand" in
70
+ workload) cmd_workload "$@" ;;
71
+ epic) cmd_epic "$@" ;;
72
+ dashboard) cmd_dashboard "$@" ;;
73
+ add) cmd_add "$@" ;;
74
+ verify) cmd_verify "$@" ;;
75
+ sync) cmd_sync "$@" ;;
76
+ claim) cmd_claim "$@" ;;
77
+ help) cmd_help ;;
78
+ *)
79
+ echo "Error: unknown subcommand '$subcommand'" >&2
80
+ cmd_help >&2
81
+ exit 1
82
+ ;;
83
+ esac
84
+ }
85
+
86
+ main "$@"
@@ -0,0 +1,52 @@
1
+ # agent-prompt.sh — Shared library for structured agent communication
2
+ # All user-facing output from `forge team` goes through these helpers.
3
+ # Uses non-guessable prefix to prevent injection from GitHub data.
4
+ #
5
+ # Usage: source this file from other scripts.
6
+ # This library does NOT set errexit/pipefail.
7
+
8
+ # Guard against double-sourcing (readonly would error on second source)
9
+ if [[ -n "${_AGENT_PROMPT_LIB_LOADED:-}" ]]; then
10
+ return 0 2>/dev/null || true
11
+ fi
12
+ _AGENT_PROMPT_LIB_LOADED=1
13
+
14
+ readonly FORGE_AGENT_PREFIX="FORGE_AGENT_7f3a"
15
+
16
+ # Output a prompt for the AI agent to ask the user.
17
+ # Format: FORGE_AGENT_7f3a:PROMPT: <message> (to stderr)
18
+ agent_prompt() {
19
+ echo "${FORGE_AGENT_PREFIX}:PROMPT: $1" >&2
20
+ }
21
+
22
+ # Informational output.
23
+ # Format: FORGE_AGENT_7f3a:INFO: <message> (to stderr)
24
+ agent_info() {
25
+ echo "${FORGE_AGENT_PREFIX}:INFO: $1" >&2
26
+ }
27
+
28
+ # Error output.
29
+ # Format: FORGE_AGENT_7f3a:ERROR: <message> (to stderr)
30
+ agent_error() {
31
+ echo "${FORGE_AGENT_PREFIX}:ERROR: $1" >&2
32
+ }
33
+
34
+ # Strip any occurrence of the FORGE_AGENT_7f3a: prefix from input strings.
35
+ # Prevents injection from GitHub issue titles or other external data.
36
+ # Takes input via stdin or as $1 arg, outputs sanitized text to stdout.
37
+ sanitize_for_agent() {
38
+ local input
39
+ if [[ $# -gt 0 ]]; then
40
+ input="$1"
41
+ else
42
+ input="$(cat)"
43
+ fi
44
+ # Loop until no more prefix occurrences (prevents split-prefix bypass)
45
+ local prev
46
+ while true; do
47
+ prev="$input"
48
+ input="${input//${FORGE_AGENT_PREFIX}:/}"
49
+ [[ "$input" == "$prev" ]] && break
50
+ done
51
+ printf '%s' "$input"
52
+ }
@@ -0,0 +1,256 @@
1
+ #!/usr/bin/env bash
2
+ # scripts/forge-team/lib/claim.sh — Claim locking with pre-claim check
3
+ #
4
+ # Before allowing issue claim, checks GitHub assignee and uses flock/mkdir
5
+ # to prevent race conditions between concurrent agents.
6
+ #
7
+ # Functions:
8
+ # pre_claim_check — Check if issue is claimable
9
+ # claim_with_lock — Atomic claim with file locking
10
+ # forge_team_claim — Top-level dispatcher entry point
11
+ #
12
+ # Env overrides (for testing):
13
+ # GH_CMD — Path to gh binary
14
+ # BD_CMD — Path to bd binary
15
+ #
16
+ # This file does NOT set errexit/pipefail — callers manage their own shell options.
17
+
18
+ # Guard against double-sourcing
19
+ if [[ -n "${_CLAIM_LIB_LOADED:-}" ]]; then
20
+ return 0 2>/dev/null || true
21
+ fi
22
+ _CLAIM_LIB_LOADED=1
23
+
24
+ _CLAIM_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
25
+
26
+ # Source dependencies
27
+ if [[ -f "$_CLAIM_DIR/agent-prompt.sh" ]]; then
28
+ source "$_CLAIM_DIR/agent-prompt.sh"
29
+ fi
30
+
31
+ if [[ -f "$_CLAIM_DIR/identity.sh" ]]; then
32
+ source "$_CLAIM_DIR/identity.sh"
33
+ fi
34
+
35
+ # ── Internal helpers ─────────────────────────────────────────────────────
36
+
37
+ # _claim_error <message>
38
+ _claim_error() {
39
+ if declare -f agent_error &>/dev/null; then
40
+ agent_error "$1"
41
+ else
42
+ echo "ERROR: $1" >&2
43
+ fi
44
+ }
45
+
46
+ # _claim_info <message>
47
+ _claim_info() {
48
+ if declare -f agent_info &>/dev/null; then
49
+ agent_info "$1"
50
+ else
51
+ echo "INFO: $1" >&2
52
+ fi
53
+ }
54
+
55
+ # _claim_prompt <message>
56
+ _claim_prompt() {
57
+ if declare -f agent_prompt &>/dev/null; then
58
+ agent_prompt "$1"
59
+ else
60
+ echo "PROMPT: $1" >&2
61
+ fi
62
+ }
63
+
64
+ # _get_github_issue_number <beads-id>
65
+ # Extracts github_issue:N from bd show output. Returns number or empty.
66
+ _get_github_issue_number() {
67
+ local beads_id="$1"
68
+ local bd_cmd="${BD_CMD:-bd}"
69
+ local bd_output
70
+ bd_output="$("$bd_cmd" show "$beads_id" 2>/dev/null)" || return 1
71
+
72
+ local issue_num
73
+ issue_num="$(printf '%s' "$bd_output" | grep -oP 'github_issue:\K[0-9]+' || true)"
74
+ if [[ -z "$issue_num" ]]; then
75
+ return 1
76
+ fi
77
+ printf '%s' "$issue_num"
78
+ }
79
+
80
+ # _get_github_assignee <issue-number>
81
+ # Returns the GitHub login of the assignee, or empty if unassigned.
82
+ _get_github_assignee() {
83
+ local issue_num="$1"
84
+ local gh_cmd="${GH_CMD:-gh}"
85
+ local assignee
86
+ assignee="$("$gh_cmd" issue view "$issue_num" --json assignees --jq '.assignees[0].login // empty' 2>/dev/null)" || return 1
87
+ printf '%s' "$assignee"
88
+ }
89
+
90
+ # _claim_lock_dir — Returns the path to the claim lock directory
91
+ _claim_lock_dir() {
92
+ local root="${TEAM_MAP_ROOT:-.}"
93
+ printf '%s' "$root/.beads"
94
+ }
95
+
96
+ # ── Public API ───────────────────────────────────────────────────────────
97
+
98
+ # pre_claim_check <beads-id>
99
+ # Checks if issue is claimable. Returns 0 if clear, 1 if blocked.
100
+ pre_claim_check() {
101
+ local beads_id="${1:-}"
102
+ if [[ -z "$beads_id" ]]; then
103
+ _claim_error "Usage: pre_claim_check <beads-id>"
104
+ return 1
105
+ fi
106
+
107
+ # Get current user first (validates gh auth)
108
+ local current_user
109
+ if ! current_user="$(get_github_user)"; then
110
+ _claim_error "GitHub CLI not authenticated. Run 'gh auth login' first."
111
+ return 1
112
+ fi
113
+
114
+ # Get GitHub issue number from beads
115
+ local issue_num
116
+ if ! issue_num="$(_get_github_issue_number "$beads_id")"; then
117
+ _claim_error "No github_issue number found for $beads_id"
118
+ return 1
119
+ fi
120
+
121
+ if [[ -z "$issue_num" ]]; then
122
+ _claim_error "No github_issue number found for $beads_id"
123
+ return 1
124
+ fi
125
+
126
+ # Query GitHub assignee
127
+ local assignee
128
+ assignee="$(_get_github_assignee "$issue_num")" || true
129
+
130
+ # If unassigned or assigned to current user: proceed
131
+ if [[ -z "$assignee" ]] || [[ "$assignee" == "$current_user" ]]; then
132
+ return 0
133
+ fi
134
+
135
+ # Assigned to someone else: prompt
136
+ _claim_prompt "$beads_id is claimed by $assignee. Override? Run: forge team claim $beads_id --force"
137
+ return 1
138
+ }
139
+
140
+ # claim_with_lock <beads-id> [--force]
141
+ # Atomic claim using flock/mkdir locking pattern.
142
+ # With --force, skips the re-check of assignee inside the lock.
143
+ claim_with_lock() {
144
+ local beads_id="${1:-}"
145
+ local force="${2:-}"
146
+ if [[ -z "$beads_id" ]]; then
147
+ _claim_error "Usage: claim_with_lock <beads-id>"
148
+ return 1
149
+ fi
150
+
151
+ local gh_cmd="${GH_CMD:-gh}"
152
+ local bd_cmd="${BD_CMD:-bd}"
153
+ local lock_dir
154
+ lock_dir="$(_claim_lock_dir)"
155
+ mkdir -p "$lock_dir"
156
+
157
+ local lock_file="$lock_dir/claim.lock"
158
+
159
+ # Get current user
160
+ local current_user
161
+ if ! current_user="$(get_github_user)"; then
162
+ _claim_error "GitHub CLI not authenticated."
163
+ return 1
164
+ fi
165
+
166
+ # Get GitHub issue number
167
+ local issue_num
168
+ if ! issue_num="$(_get_github_issue_number "$beads_id")"; then
169
+ _claim_error "No github_issue number found for $beads_id"
170
+ return 1
171
+ fi
172
+
173
+ if command -v flock &>/dev/null; then
174
+ # flock-based locking
175
+ (
176
+ flock -w 5 200 || {
177
+ _claim_error "Claim lock timeout after 5s"
178
+ exit 1
179
+ }
180
+ # Re-check assignee inside lock (skip if --force)
181
+ if [[ "$force" != "--force" ]]; then
182
+ local assignee
183
+ assignee="$(_get_github_assignee "$issue_num")" || true
184
+ if [[ -n "$assignee" ]] && [[ "$assignee" != "$current_user" ]]; then
185
+ _claim_error "$beads_id was claimed by $assignee while waiting for lock"
186
+ exit 1
187
+ fi
188
+ fi
189
+ # Claim on both Beads and GitHub
190
+ "$bd_cmd" update "$beads_id" --claim >/dev/null 2>&1 || true
191
+ "$gh_cmd" issue edit "$issue_num" --add-assignee "$current_user" >/dev/null 2>&1 || true
192
+ ) 200>"$lock_file"
193
+ local subshell_rc=$?
194
+ [[ $subshell_rc -ne 0 ]] && return $subshell_rc
195
+ else
196
+ # mkdir-based fallback
197
+ local lock_mkdir="$lock_dir/claim.lock.d"
198
+ local attempts=0
199
+ while ! mkdir "$lock_mkdir" 2>/dev/null; do
200
+ attempts=$((attempts + 1))
201
+ if [[ $attempts -ge 50 ]]; then
202
+ _claim_error "Claim lock timeout after 5s"
203
+ return 1
204
+ fi
205
+ sleep 0.1
206
+ done
207
+ trap 'rmdir "$lock_mkdir" 2>/dev/null; trap - RETURN' RETURN
208
+
209
+ # Re-check assignee inside lock (skip if --force)
210
+ if [[ "$force" != "--force" ]]; then
211
+ local assignee
212
+ assignee="$(_get_github_assignee "$issue_num")" || true
213
+ if [[ -n "$assignee" ]] && [[ "$assignee" != "$current_user" ]]; then
214
+ _claim_error "$beads_id was claimed by $assignee while waiting for lock"
215
+ return 1
216
+ fi
217
+ fi
218
+ # Claim on both Beads and GitHub
219
+ "$bd_cmd" update "$beads_id" --claim >/dev/null 2>&1 || true
220
+ "$gh_cmd" issue edit "$issue_num" --add-assignee "$current_user" >/dev/null 2>&1 || true
221
+ fi
222
+
223
+ return 0
224
+ }
225
+
226
+ # forge_team_claim <beads-id> [--force]
227
+ # Top-level function called by the dispatcher.
228
+ forge_team_claim() {
229
+ local beads_id="${1:-}"
230
+ local force_flag="${2:-}"
231
+
232
+ if [[ -z "$beads_id" ]]; then
233
+ _claim_error "Usage: forge team claim <beads-id> [--force]"
234
+ return 1
235
+ fi
236
+
237
+ local bd_cmd="${BD_CMD:-bd}"
238
+
239
+ if [[ "$force_flag" == "--force" ]]; then
240
+ # Skip pre-claim check, claim directly (pass --force to skip lock re-check too)
241
+ claim_with_lock "$beads_id" "--force" || return 1
242
+ # Log override via bd comments
243
+ "$bd_cmd" comments add "$beads_id" "Force-claimed by $(get_github_user 2>/dev/null || echo 'unknown')" >/dev/null 2>&1 || true
244
+ _claim_info "$beads_id force-claimed successfully"
245
+ return 0
246
+ fi
247
+
248
+ # Normal flow: pre-claim check then lock-claim
249
+ if ! pre_claim_check "$beads_id"; then
250
+ return 1
251
+ fi
252
+
253
+ claim_with_lock "$beads_id" || return 1
254
+ _claim_info "$beads_id claimed successfully"
255
+ return 0
256
+ }