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
@@ -112,6 +112,28 @@ done
112
112
 
113
113
  # ── Fetch issues ────────────────────────────────────────────────────────
114
114
 
115
+ # Auto-recover beads database if Dolt server lost the database (e.g. branch
116
+ # switch, fresh clone, server restart). Checks for the specific "database
117
+ # not found" error, runs bd init --force + bd backup restore, then retries.
118
+ # Capture stderr into variable (2>&1 redirects stderr to stdout for capture,
119
+ # then >/dev/null discards original stdout so only errors remain).
120
+ _bd_list_err="$("$BD" list --json --limit 0 2>&1 >/dev/null || true)"
121
+ if printf '%s' "$_bd_list_err" | grep -qi "database.*not found"; then
122
+ # Derive prefix from repo root directory name (not pwd, which could be a subdirectory)
123
+ _repo_root="$("$GIT" rev-parse --show-toplevel 2>/dev/null || pwd)"
124
+ _bd_prefix="$(basename "$_repo_root" | tr '[:upper:]' '[:lower:]' | tr -cs '[:alnum:]-' '-' | sed 's/^-//;s/-$//')"
125
+ echo "Beads database not found — attempting auto-recovery..." >&2
126
+ if "$BD" init --force --prefix "$_bd_prefix" >/dev/null 2>&1; then
127
+ if [ -d "$_repo_root/.beads/backup" ] && ls "$_repo_root/.beads/backup"/*.jsonl >/dev/null 2>&1; then
128
+ "$BD" backup restore >/dev/null 2>&1 && echo "Beads: restored from backup." >&2 || echo "Beads: backup restore failed." >&2
129
+ else
130
+ echo "Beads: initialized fresh (no backup found)." >&2
131
+ fi
132
+ else
133
+ echo "Beads: auto-recovery failed — run 'bd doctor' manually." >&2
134
+ fi
135
+ fi
136
+
115
137
  ISSUES_JSON="$("$BD" list --json --limit 0 2>/dev/null || echo '[]')"
116
138
 
117
139
  # Bail early on empty
@@ -158,11 +180,15 @@ SCORED_JSON="$(printf '%s' "$ISSUES_JSON" | jq --argjson epic_stats "$EPIC_STATS
158
180
  [.[] | . as $issue |
159
181
 
160
182
  # Priority weight: P0=5, P1=4, P2=3, P3=2, P4=1, default=1
161
- (if .priority == "P0" then 5
162
- elif .priority == "P1" then 4
163
- elif .priority == "P2" then 3
164
- elif .priority == "P3" then 2
165
- elif .priority == "P4" then 1
183
+ # Normalize priority to number (beads 0.62+ sends numbers, older sends strings)
184
+ ((.priority // 2) | if type == "string" then
185
+ (if . == "P0" then 0 elif . == "P1" then 1 elif . == "P2" then 2 elif . == "P3" then 3 elif . == "P4" then 4 else 5 end)
186
+ else . end) as $pri_num |
187
+ (if $pri_num == 0 then 5
188
+ elif $pri_num == 1 then 4
189
+ elif $pri_num == 2 then 3
190
+ elif $pri_num == 3 then 2
191
+ elif $pri_num == 4 then 1
166
192
  else 1 end) as $priority_weight |
167
193
 
168
194
  # Unblock chain: dependent_count + 1 (min 1).
@@ -172,9 +198,10 @@ SCORED_JSON="$(printf '%s' "$ISSUES_JSON" | jq --argjson epic_stats "$EPIC_STATS
172
198
  (((.dependent_count // 0) + 1) | if . < 1 then 1 else . end) as $unblock_chain |
173
199
 
174
200
  # Type weight: bug=1.2, feature=1.0, task=0.8, default=1.0
175
- (if .type == "bug" then 1.2
176
- elif .type == "feature" then 1.0
177
- elif .type == "task" then 0.8
201
+ # Handle null type from beads 0.62+
202
+ (if (.type // "task") == "bug" then 1.2
203
+ elif (.type // "task") == "feature" then 1.0
204
+ elif (.type // "task") == "task" then 0.8
178
205
  else 1.0 end) as $type_weight |
179
206
 
180
207
  # Status boost: in_progress=1.5, open=1.0, default=1.0
@@ -690,7 +717,7 @@ else
690
717
  (if .status == "in_progress" then "RESUME"
691
718
  elif (.dependent_count // 0) >= 2 then "UNBLOCK_CHAINS"
692
719
  elif (.dependency_count // 0) > 0 and .status != "closed" then "BLOCKED"
693
- elif .priority == "P4" then "BACKLOG"
720
+ elif (.priority == "P4" or .priority == 4) then "BACKLOG"
694
721
  else "READY_WORK"
695
722
  end) as $group |
696
723
  # Flag in_progress issues that have active blockers
@@ -734,7 +761,7 @@ else
734
761
  (if $item.blocked_resume then " !! BLOCKED by dependencies" else "" end) as $blocked_warn |
735
762
  "ENTRY:" + $item.group + ":" +
736
763
  $rank + ". [" + ($item.score | tostring) + "] " +
737
- $item.id + " (" + ($item.priority // "-") + " " + ($item.type // "-") + ") -- " +
764
+ $item.id + " (" + (if ($item.priority | type) == "number" then "P" + ($item.priority | tostring) else ($item.priority // "-" | tostring) end) + " " + ($item.type // "-" | tostring) + ") -- " +
738
765
  ($item.title // "-") + " " + $status_tag + $stale + $blocked_warn + $unblocks
739
766
  ),
740
767
  ""
@@ -11,6 +11,10 @@ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
11
11
  set -euo pipefail
12
12
  fi
13
13
 
14
+ # Source shared sanitize library
15
+ _SU_SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
16
+ source "$_SU_SCRIPT_DIR/lib/sanitize.sh"
17
+
14
18
  # ── Session Identity ───────────────────────────────────────────────────
15
19
 
16
20
  # Validates a session identity string against the allowlist regex.
@@ -76,28 +80,6 @@ get_session_identity() {
76
80
  printf '%s' "$identity"
77
81
  }
78
82
 
79
- # ── Input Sanitization ─────────────────────────────────────────────────
80
-
81
- # Sanitize a config value: strip shell-injection patterns (OWASP A03).
82
- # Removes: backticks, $(...), semicolons, pipes, newlines.
83
- # Usage: sanitized="$(sanitize_config_value "$raw")"
84
- sanitize_config_value() {
85
- local val="$1"
86
- # Remove backtick command substitution
87
- val="${val//\`/}"
88
- # Remove $(...) command substitution patterns (loop handles nested)
89
- val="$(printf '%s' "$val" | sed -e ':loop' -e 's/\$([^()]*)//g' -e 't loop')"
90
- # Remove semicolons (command chaining)
91
- val="${val//;/}"
92
- # Remove pipes (command chaining)
93
- val="${val//|/}"
94
- # Collapse newlines to spaces
95
- val="$(printf '%s' "$val" | tr '\n' ' ')"
96
- # Trim leading/trailing whitespace
97
- val="$(printf '%s' "$val" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')"
98
- printf '%s' "$val"
99
- }
100
-
101
83
  # ── Config Reading ─────────────────────────────────────────────────────
102
84
 
103
85
  # Read a key from .beads/config.json (JSON format).
@@ -277,25 +259,37 @@ _SYNC_STALENESS_THRESHOLD=900
277
259
  # On success: records Unix epoch timestamp to .beads/.last-sync
278
260
  # and updates file index from all in-progress issues' task files.
279
261
  #
262
+ # _run_sync — execute beads sync (pull + push).
263
+ # If BD_SYNC_CMD is set, run it as a single command (for testing).
264
+ # Otherwise, run the default two-step pull+push sequence.
265
+ # shellcheck disable=SC2086
266
+ _run_sync() {
267
+ if [[ -n "${BD_SYNC_CMD:-}" ]]; then
268
+ $BD_SYNC_CMD
269
+ else
270
+ bd dolt pull && bd dolt push
271
+ fi
272
+ }
273
+
280
274
  # Environment overrides (for testing):
281
- # BD_SYNC_CMD — command to run instead of `bd sync` (default: "bd sync")
275
+ # BD_SYNC_CMD — single command to run instead of default pull+push (e.g. "echo mock-sync")
282
276
  # FILE_INDEX_ROOT — root directory for file-index.sh (default: repo_dir)
283
277
  auto_sync() {
284
278
  local repo_dir="${1:-.}"
285
- local sync_cmd="${BD_SYNC_CMD:-bd sync}"
286
279
  local last_sync_file="$repo_dir/.beads/.last-sync"
287
280
 
288
281
  # Ensure .beads directory exists
289
282
  mkdir -p "$repo_dir/.beads"
290
283
 
291
- # Run bd sync (or mock) use $sync_cmd without eval to prevent injection
292
- # shellcheck disable=SC2086
293
- if $sync_cmd >/dev/null 2>&1; then
284
+ # Run sync — helper function avoids eval while supporting compound default
285
+ if _run_sync >/dev/null 2>&1; then
294
286
  # Success: record timestamp
295
287
  date +%s > "$last_sync_file"
296
288
 
297
289
  # Update file index from in-progress issues' task files
298
- _auto_sync_update_file_index "$repo_dir"
290
+ _auto_sync_update_file_index "$repo_dir" || {
291
+ echo "Warning: file index update failed after sync" >&2
292
+ }
299
293
  else
300
294
  # Failure: warn but continue (non-blocking)
301
295
  local last_ts="unknown"
@@ -340,7 +334,8 @@ _auto_sync_update_file_index() {
340
334
 
341
335
  # Check jq is available
342
336
  if ! command -v jq &>/dev/null; then
343
- return 0
337
+ echo "Warning: jq not found — file index update skipped" >&2
338
+ return 1
344
339
  fi
345
340
 
346
341
  # Extract in-progress issue IDs (LWW resolution: group by id, take last, filter in_progress)
package/scripts/test.js CHANGED
@@ -22,7 +22,9 @@ function detectPackageManager() {
22
22
  const pkgManager = detectPackageManager();
23
23
  console.log(`🧪 Running test suite (${pkgManager} test)...`);
24
24
 
25
- const result = spawnSync(pkgManager, ['test'], { stdio: 'inherit', shell: isWindows });
25
+ // Use 'run test' to invoke the package.json script (which may include --timeout flags)
26
+ // 'bun test' is a built-in that ignores package.json scripts
27
+ const result = spawnSync(pkgManager, ['run', 'test'], { stdio: 'inherit', shell: isWindows });
26
28
 
27
29
  if (result.error) {
28
30
  console.error('');