claudius-core 0.9.6 → 0.10.1

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 (74) hide show
  1. package/.claude/agents/developer.md +4 -2
  2. package/.claude/agents/manager.md +5 -5
  3. package/.claude/agents/pulsar.md +4 -4
  4. package/.claude/agents/worker.md +21 -61
  5. package/.claude/hooks/guardian.ts +11 -0
  6. package/.claude/hooks/session-context.ts +29 -3
  7. package/.claude/scripts/project-status.sh +8 -3
  8. package/.claude/scripts/workpad-upsert.sh +25 -32
  9. package/.claude-plugin/marketplace.json +2 -2
  10. package/.claudius/jobs/qualify-ideas.md +1 -0
  11. package/dist/daemon/index.d.ts +1 -1
  12. package/dist/daemon/index.d.ts.map +1 -1
  13. package/dist/daemon/index.js +1 -1
  14. package/dist/daemon/index.js.map +1 -1
  15. package/dist/daemon/scheduler.d.ts +12 -0
  16. package/dist/daemon/scheduler.d.ts.map +1 -1
  17. package/dist/daemon/scheduler.js +63 -1
  18. package/dist/daemon/scheduler.js.map +1 -1
  19. package/dist/index.js +20 -3
  20. package/dist/index.js.map +1 -1
  21. package/dist/studio/assets/{_basePickBy-R-uis1nJ.js → _basePickBy-DFTr0P1q.js} +1 -1
  22. package/dist/studio/assets/{_baseUniq-DYva2fSG.js → _baseUniq-DZrBnw2L.js} +1 -1
  23. package/dist/studio/assets/{arc-BzeI-lxb.js → arc-C15jqhOi.js} +1 -1
  24. package/dist/studio/assets/{architectureDiagram-VXUJARFQ-DMa_o-aK.js → architectureDiagram-VXUJARFQ-CfBqrOzP.js} +1 -1
  25. package/dist/studio/assets/{blockDiagram-VD42YOAC-BFN1vazA.js → blockDiagram-VD42YOAC-vmfPvO8X.js} +1 -1
  26. package/dist/studio/assets/{c4Diagram-YG6GDRKO-BFDthHUP.js → c4Diagram-YG6GDRKO-DBQ_zJwo.js} +1 -1
  27. package/dist/studio/assets/channel-BNAWutZ3.js +1 -0
  28. package/dist/studio/assets/{chunk-4BX2VUAB-BcntFbLt.js → chunk-4BX2VUAB-DdUKHJpP.js} +1 -1
  29. package/dist/studio/assets/{chunk-55IACEB6-sNY6b1p-.js → chunk-55IACEB6-CwnLV00T.js} +1 -1
  30. package/dist/studio/assets/{chunk-B4BG7PRW-BLekgdES.js → chunk-B4BG7PRW-sLqc4EUj.js} +1 -1
  31. package/dist/studio/assets/{chunk-DI55MBZ5-CZw9ICRq.js → chunk-DI55MBZ5-DIMS_MIj.js} +1 -1
  32. package/dist/studio/assets/{chunk-FMBD7UC4-ntqmlvfQ.js → chunk-FMBD7UC4-ggnRtpJV.js} +1 -1
  33. package/dist/studio/assets/{chunk-QN33PNHL-CE-WX1bS.js → chunk-QN33PNHL-DaMEhziG.js} +1 -1
  34. package/dist/studio/assets/{chunk-QZHKN3VN-GyZ2p2Bl.js → chunk-QZHKN3VN-D4UgN63e.js} +1 -1
  35. package/dist/studio/assets/{chunk-TZMSLE5B--hsg0lEr.js → chunk-TZMSLE5B-a0lav95f.js} +1 -1
  36. package/dist/studio/assets/classDiagram-2ON5EDUG-DCggxQO8.js +1 -0
  37. package/dist/studio/assets/classDiagram-v2-WZHVMYZB-DCggxQO8.js +1 -0
  38. package/dist/studio/assets/clone-DK5ebX1Z.js +1 -0
  39. package/dist/studio/assets/{cose-bilkent-S5V4N54A-C4C-DPXZ.js → cose-bilkent-S5V4N54A-DLfcjX_v.js} +1 -1
  40. package/dist/studio/assets/{dagre-6UL2VRFP-1Bb2UhRi.js → dagre-6UL2VRFP-DPYF8SN1.js} +1 -1
  41. package/dist/studio/assets/{diagram-PSM6KHXK-C-FzwLf5.js → diagram-PSM6KHXK-gefKvZL9.js} +1 -1
  42. package/dist/studio/assets/{diagram-QEK2KX5R-Bctxbrmx.js → diagram-QEK2KX5R-CN7MUqrm.js} +1 -1
  43. package/dist/studio/assets/{diagram-S2PKOQOG-3uep1mdW.js → diagram-S2PKOQOG-D3UqjjFf.js} +1 -1
  44. package/dist/studio/assets/{erDiagram-Q2GNP2WA-B_Db2yYe.js → erDiagram-Q2GNP2WA-CF-8MoUF.js} +1 -1
  45. package/dist/studio/assets/{flowDiagram-NV44I4VS-Bzc-kwIX.js → flowDiagram-NV44I4VS-COkpkBNb.js} +1 -1
  46. package/dist/studio/assets/{ganttDiagram-JELNMOA3-BXibfYz-.js → ganttDiagram-JELNMOA3-DNJyrEK_.js} +1 -1
  47. package/dist/studio/assets/{gitGraphDiagram-V2S2FVAM-BLkjIgg1.js → gitGraphDiagram-V2S2FVAM-Dks7m-EG.js} +1 -1
  48. package/dist/studio/assets/{graph-B0Z_gmOm.js → graph-CpOxi7r6.js} +1 -1
  49. package/dist/studio/assets/{index-Ddl_PR-h.js → index-CUKxqiaW.js} +2 -2
  50. package/dist/studio/assets/{infoDiagram-HS3SLOUP-BeFdN5yb.js → infoDiagram-HS3SLOUP-BNap3Gvz.js} +1 -1
  51. package/dist/studio/assets/{journeyDiagram-XKPGCS4Q--icE_FdD.js → journeyDiagram-XKPGCS4Q-DcY1nvRz.js} +1 -1
  52. package/dist/studio/assets/{kanban-definition-3W4ZIXB7-BYGur8C7.js → kanban-definition-3W4ZIXB7-Ccyqqx5g.js} +1 -1
  53. package/dist/studio/assets/{layout-B4VDGqHe.js → layout-Bp-sWlva.js} +1 -1
  54. package/dist/studio/assets/{linear-Bm1oIJ5Y.js → linear-Cn9wajO0.js} +1 -1
  55. package/dist/studio/assets/{mermaid.core-Bl28GW7R.js → mermaid.core-Do-o_wmR.js} +4 -4
  56. package/dist/studio/assets/{mindmap-definition-VGOIOE7T-DC0Ch_L5.js → mindmap-definition-VGOIOE7T-Bshwlp_p.js} +1 -1
  57. package/dist/studio/assets/{pieDiagram-ADFJNKIX-Cfm6OLdr.js → pieDiagram-ADFJNKIX-BIyxi8K7.js} +1 -1
  58. package/dist/studio/assets/{quadrantDiagram-AYHSOK5B-C68ftZIq.js → quadrantDiagram-AYHSOK5B-CgiJ4dLs.js} +1 -1
  59. package/dist/studio/assets/{requirementDiagram-UZGBJVZJ-yH30jAOj.js → requirementDiagram-UZGBJVZJ-DiMA0f-Z.js} +1 -1
  60. package/dist/studio/assets/{sankeyDiagram-TZEHDZUN-BlEt27YS.js → sankeyDiagram-TZEHDZUN-DcwbypJY.js} +1 -1
  61. package/dist/studio/assets/{sequenceDiagram-WL72ISMW-CT2uLY3i.js → sequenceDiagram-WL72ISMW-C_f3iggK.js} +1 -1
  62. package/dist/studio/assets/{stateDiagram-FKZM4ZOC-DuMW3t9Q.js → stateDiagram-FKZM4ZOC-Cvk4ecgi.js} +1 -1
  63. package/dist/studio/assets/stateDiagram-v2-4FDKWEC3-BbCKSvPk.js +1 -0
  64. package/dist/studio/assets/{timeline-definition-IT6M3QCI-BGETR7lR.js → timeline-definition-IT6M3QCI-hLqQiB-V.js} +1 -1
  65. package/dist/studio/assets/{treemap-GDKQZRPO-BO8oqrvF.js → treemap-GDKQZRPO-BA5_-5Tc.js} +1 -1
  66. package/dist/studio/assets/{xychartDiagram-PRI3JC2R-DJ-F4Odf.js → xychartDiagram-PRI3JC2R-IqrVisKP.js} +1 -1
  67. package/dist/studio/index.html +1 -1
  68. package/global/scripts/project-sync.sh +5 -5
  69. package/package.json +7 -2
  70. package/dist/studio/assets/channel-huxXjZrf.js +0 -1
  71. package/dist/studio/assets/classDiagram-2ON5EDUG-ufBxprI3.js +0 -1
  72. package/dist/studio/assets/classDiagram-v2-WZHVMYZB-ufBxprI3.js +0 -1
  73. package/dist/studio/assets/clone-BRn04n6L.js +0 -1
  74. package/dist/studio/assets/stateDiagram-v2-4FDKWEC3-DjR-lF7e.js +0 -1
@@ -29,10 +29,12 @@ Read, Write, Edit, Bash, Glob, Grep — full file system access for implementati
29
29
  ```
30
30
 
31
31
  **In main checkout** (git_dir is `.git`):
32
+ This is a fallback — `isolation: worktree` may have failed. Proceed carefully.
32
33
  ```bash
33
- git checkout main && git pull origin main
34
- git checkout -b <branch-name> # branch name provided in your prompt
34
+ git fetch origin main 2>/dev/null || true
35
+ git checkout -q -b <branch-name> origin/main # branch name provided in your prompt
35
36
  ```
37
+ **NEVER run `git checkout main`** — it switches the user's interactive session.
36
38
  1. Read the acceptance criteria from the issue
37
39
  2. Write a failing test for the first criterion
38
40
  3. Implement until the test passes
@@ -39,7 +39,7 @@ When uncertain, go larger.
39
39
  **Developer** (for M+ work):
40
40
  Use the Agent tool with `subagent_type: "developer"`. Include in the prompt:
41
41
  - The full issue body with acceptance criteria
42
- - The branch name to work on
42
+ - Instruction: use your current worktree branch do NOT create a new branch
43
43
  - The constraint: only implement what's asked, no scope creep
44
44
  - **Reminder: developer must run `bun test`, `bun run lint`, and `bun run build` before pushing**
45
45
 
@@ -73,8 +73,8 @@ if [ -n "$SLACK_CHANNEL" ]; then
73
73
  PR_URL=$(gh pr view $PR_NUMBER --json url --jq '.url')
74
74
  THREAD_REF=$(bun .claude/scripts/slack.ts send \
75
75
  --channel "$SLACK_CHANNEL" \
76
- --text "🔍 *PR #$PR_NUMBER ready for review* — $PR_TITLE $PR_URL") \
77
- && gh pr comment $PR_NUMBER --body "<!-- slack-thread: $THREAD_REF -->" \
76
+ --text "🔍 *PR #$PR_NUMBER ready for review* — $PR_TITLE $PR_URL" | tail -1) \
77
+ && printf '<!-- slack-thread: %s -->' "$THREAD_REF" | gh pr comment $PR_NUMBER --body-file - \
78
78
  || echo "[slack] notification skipped"
79
79
  fi
80
80
  ```
@@ -82,8 +82,8 @@ fi
82
82
  **After every `bash .claude/scripts/merge-pr.sh $PR_NUMBER`** — reply in the PR's thread:
83
83
  ```bash
84
84
  THREAD_REF=$(gh pr view $PR_NUMBER --json comments \
85
- --jq '[.comments[].body | select(startswith("<!-- slack-thread:"))] | first' \
86
- | sed 's/<!-- slack-thread: //;s/ -->//')
85
+ --jq '[.comments[].body | select(contains("slack-thread:"))] | first' \
86
+ | grep -oE '[A-Z][A-Z0-9]{8,}:[0-9]+\.[0-9]+')
87
87
  if [ -n "$THREAD_REF" ] && [ "$THREAD_REF" != "null" ]; then
88
88
  bun .claude/scripts/slack.ts reply \
89
89
  --thread "$THREAD_REF" \
@@ -140,8 +140,8 @@ if [ -n "$SLACK_CHANNEL" ]; then
140
140
  PR_URL=$(gh pr view $PR_NUMBER --json url --jq '.url')
141
141
  THREAD_REF=$(bun .claude/scripts/slack.ts send \
142
142
  --channel "$SLACK_CHANNEL" \
143
- --text "🔍 *PR #$PR_NUMBER ready for review* — $PR_TITLE $PR_URL") \
144
- && gh pr comment $PR_NUMBER --body "<!-- slack-thread: $THREAD_REF -->" \
143
+ --text "🔍 *PR #$PR_NUMBER ready for review* — $PR_TITLE $PR_URL" | tail -1) \
144
+ && printf '<!-- slack-thread: %s -->' "$THREAD_REF" | gh pr comment $PR_NUMBER --body-file - \
145
145
  || true
146
146
  fi
147
147
  ```
@@ -179,8 +179,8 @@ gh issue close <N> --comment "Closed by PR #<M>."
179
179
  After each merge, reply in the PR's Slack thread (best-effort):
180
180
  ```bash
181
181
  THREAD_REF=$(gh pr view $PR_NUMBER --json comments \
182
- --jq '[.comments[].body | select(startswith("<!-- slack-thread:"))] | first' \
183
- | sed 's/<!-- slack-thread: //;s/ -->//')
182
+ --jq '[.comments[].body | select(contains("slack-thread:"))] | first' \
183
+ | grep -oE '[A-Z][A-Z0-9]{8,}:[0-9]+\.[0-9]+')
184
184
  if [ -n "$THREAD_REF" ] && [ "$THREAD_REF" != "null" ]; then
185
185
  bun .claude/scripts/slack.ts reply \
186
186
  --thread "$THREAD_REF" \
@@ -102,23 +102,16 @@ for CANDIDATE in <issue-numbers>; do
102
102
  done
103
103
  ```
104
104
 
105
- **Check attempt count before claiming** — read the existing workpad (if any) to get prior attempt count. If at max, move to Blocked instead of claiming:
105
+ **Check attempt count before claiming** — count existing workpad comments to determine prior attempts:
106
106
 
107
107
  ```bash
108
108
  MAX_ATTEMPTS=3
109
- PRIOR_ATTEMPTS=$(gh issue view <N> --json comments \
110
- --jq '[.comments[].body | select(contains("claudius:workpad"))] | last // ""' 2>/dev/null \
111
- | grep -o 'Attempts: [0-9]*' | grep -o '[0-9]*' || echo "0")
109
+ PRIOR_ATTEMPTS=$(gh api "repos/{owner}/{repo}/issues/<N>/comments" \
110
+ --jq '[.[] | select(.body | contains("claudius:workpad"))] | length' 2>/dev/null || echo "0")
112
111
  if [ "${PRIOR_ATTEMPTS:-0}" -ge "$MAX_ATTEMPTS" ]; then
113
112
  gh issue edit <N> --remove-label "ready" --add-label "blocked"
114
113
  gh issue comment <N> --body "Claudius: exceeded ${MAX_ATTEMPTS} build attempts. Needs human review before retrying."
115
- REPO_NAME=$(gh repo view --json nameWithOwner --jq '.nameWithOwner')
116
- ITEM_ID=$(gh api graphql -f query='{user(login:"SharadKumar"){projectV2(number:6){items(first:100){nodes{id content{...on Issue{number repository{nameWithOwner}}}}}}}}' \
117
- --jq ".data.user.projectV2.items.nodes[] | select(.content.number == <N> and .content.repository.nameWithOwner == \"$REPO_NAME\") | .id" 2>/dev/null | head -1)
118
- [ -n "$ITEM_ID" ] && gh project item-edit --id "$ITEM_ID" \
119
- --project-id "PVT_kwHOAFO-EM4BRTW5" \
120
- --field-id "PVTSSF_lAHOAFO-EM4BRTW5zg_K3tE" \
121
- --single-select-option-id "d239ddb3" 2>/dev/null || true
114
+ bash .claude/scripts/project-status.sh <N> "Blocked" "Blocked"
122
115
  # skip this issue — continue to next candidate
123
116
  fi
124
117
  ```
@@ -129,27 +122,14 @@ gh issue edit <N> --add-label "in-progress,claude" --remove-label "ready"
129
122
  gh issue view <N> # read full body + acceptance criteria
130
123
  ```
131
124
 
132
- **Post workpad comment** — after claiming each issue, create (or update) a persistent workpad
133
- comment using the `<!-- claudius:workpad -->` marker. This gives reviewers a live trace of
134
- what's happening without reading logs. Pass the incremented attempt count:
135
-
125
+ **Post workpad comment** — one-liner status at claim:
136
126
  ```bash
137
- NEW_ATTEMPTS=$(( ${PRIOR_ATTEMPTS:-0} + 1 ))
138
- bash .claude/scripts/workpad-upsert.sh <N> "🔄 Claimed — reading issue" "TBD" "- [ ] (loading)" "pending" "$NEW_ATTEMPTS"
127
+ bash .claude/scripts/workpad-upsert.sh <N> "🔄 Claimed — attempt $(( ${PRIOR_ATTEMPTS:-0} + 1 ))"
139
128
  ```
140
129
 
141
- **Update Project #6 Status → In Progress** add to project if needed, then set status:
142
-
130
+ **Update Project #6 board → In Progress / Running:**
143
131
  ```bash
144
- ISSUE_URL=$(gh issue view <N> --json url --jq '.url')
145
- REPO_NAME=$(gh repo view --json nameWithOwner --jq '.nameWithOwner')
146
- gh project item-add 6 --owner SharadKumar --url "$ISSUE_URL" 2>/dev/null || true
147
- ITEM_ID=$(gh api graphql -f query='{user(login:"SharadKumar"){projectV2(number:6){items(first:100){nodes{id content{...on Issue{number repository{nameWithOwner}}}}}}}}' \
148
- --jq ".data.user.projectV2.items.nodes[] | select(.content.number == <N> and .content.repository.nameWithOwner == \"$REPO_NAME\") | .id" 2>/dev/null | head -1)
149
- [ -n "$ITEM_ID" ] && gh project item-edit --id "$ITEM_ID" \
150
- --project-id "PVT_kwHOAFO-EM4BRTW5" \
151
- --field-id "PVTSSF_lAHOAFO-EM4BRTW5zg_K3tE" \
152
- --single-select-option-id "0d583361" 2>/dev/null || true
132
+ bash .claude/scripts/project-status.sh <N> "In Progress" "Running"
153
133
  ```
154
134
 
155
135
  ## Step 3: Develop in Parallel
@@ -185,11 +165,8 @@ For each issue, each developer prompt must include:
185
165
  - Instruction: use your current branch (the worktree branch), do NOT create a new branch.
186
166
  Implement, commit, push with `git push -u origin HEAD`, then report the branch name you
187
167
  pushed. Do not create the PR (worker creates PRs).
188
- - Workpad: update the `<!-- claudius:workpad -->` comment on the issue at meaningful steps
189
- after forming a plan, after each AC is checked off, after validation runs. Use
190
- `bash .claude/scripts/workpad-upsert.sh <N> "<status>"` to update it. In your final output,
191
- include a structured **handoff summary** (what was built, how to test, AC coverage) so the
192
- worker can post it to the PR.
168
+ - In your final output, include a structured **handoff summary** (what was built, how to test,
169
+ AC coverage) so the worker can post it to the PR and update the workpad.
193
170
 
194
171
  Wait for all developer agents to complete before proceeding.
195
172
 
@@ -258,21 +235,16 @@ if [ -n "$SLACK_CHANNEL" ]; then
258
235
  PR_URL=$(gh pr view $PR_NUMBER --json url --jq '.url')
259
236
  THREAD_REF=$(bun .claude/scripts/slack.ts send \
260
237
  --channel "$SLACK_CHANNEL" \
261
- --text "🔍 *PR #$PR_NUMBER ready for review* — $PR_TITLE $PR_URL") \
262
- && gh pr comment $PR_NUMBER --body "<!-- slack-thread: $THREAD_REF -->" \
238
+ --text "🔍 *PR #$PR_NUMBER ready for review* — $PR_TITLE $PR_URL" | tail -1) \
239
+ && printf '<!-- slack-thread: %s -->' "$THREAD_REF" | gh pr comment $PR_NUMBER --body-file - \
263
240
  || true
264
241
  fi
265
242
  ```
266
243
 
267
- **Update Project #6 Status → Under Review** (best-effort):
244
+ **Update workpad + board → Under Review / Review:**
268
245
  ```bash
269
- REPO_NAME=$(gh repo view --json nameWithOwner --jq '.nameWithOwner')
270
- ITEM_ID=$(gh api graphql -f query='{user(login:"SharadKumar"){projectV2(number:6){items(first:100){nodes{id content{...on Issue{number repository{nameWithOwner}}}}}}}}' \
271
- --jq ".data.user.projectV2.items.nodes[] | select(.content.number == <N> and .content.repository.nameWithOwner == \"$REPO_NAME\") | .id" 2>/dev/null | head -1)
272
- [ -n "$ITEM_ID" ] && gh project item-edit --id "$ITEM_ID" \
273
- --project-id "PVT_kwHOAFO-EM4BRTW5" \
274
- --field-id "PVTSSF_lAHOAFO-EM4BRTW5zg_K3tE" \
275
- --single-select-option-id "173633ca" 2>/dev/null || true
246
+ bash .claude/scripts/workpad-upsert.sh <N> "🔍 Under review — PR #<M>"
247
+ bash .claude/scripts/project-status.sh <N> "Under Review" "Review"
276
248
  ```
277
249
 
278
250
  ## Step 5: Review in Parallel
@@ -298,15 +270,9 @@ gh issue edit <N> --remove-label "in-progress,claude" --add-label "ready"
298
270
  gh issue comment <N> --body "Worker: reviewer blocked. Reason: <reason>."
299
271
  ```
300
272
 
301
- **Update Project #6 Status → Blocked** (best-effort):
273
+ **Update board → Blocked:**
302
274
  ```bash
303
- REPO_NAME=$(gh repo view --json nameWithOwner --jq '.nameWithOwner')
304
- ITEM_ID=$(gh api graphql -f query='{user(login:"SharadKumar"){projectV2(number:6){items(first:100){nodes{id content{...on Issue{number repository{nameWithOwner}}}}}}}}' \
305
- --jq ".data.user.projectV2.items.nodes[] | select(.content.number == <N> and .content.repository.nameWithOwner == \"$REPO_NAME\") | .id" 2>/dev/null | head -1)
306
- [ -n "$ITEM_ID" ] && gh project item-edit --id "$ITEM_ID" \
307
- --project-id "PVT_kwHOAFO-EM4BRTW5" \
308
- --field-id "PVTSSF_lAHOAFO-EM4BRTW5zg_K3tE" \
309
- --single-select-option-id "d239ddb3" 2>/dev/null || true
275
+ bash .claude/scripts/project-status.sh <N> "Blocked" "Blocked"
310
276
  ```
311
277
 
312
278
  ## Step 6: Merge & Record
@@ -317,22 +283,16 @@ bash .claude/scripts/merge-pr.sh <PR-number>
317
283
  gh issue close <N> --comment "Closed by PR #<M>."
318
284
  ```
319
285
 
320
- **Update Project #6 Status → Done** (best-effort):
286
+ **Update workpad + close issue** (closing removes it from the board via project-sync):
321
287
  ```bash
322
- REPO_NAME=$(gh repo view --json nameWithOwner --jq '.nameWithOwner')
323
- ITEM_ID=$(gh api graphql -f query='{user(login:"SharadKumar"){projectV2(number:6){items(first:100){nodes{id content{...on Issue{number repository{nameWithOwner}}}}}}}}' \
324
- --jq ".data.user.projectV2.items.nodes[] | select(.content.number == <N> and .content.repository.nameWithOwner == \"$REPO_NAME\") | .id" 2>/dev/null | head -1)
325
- [ -n "$ITEM_ID" ] && gh project item-edit --id "$ITEM_ID" \
326
- --project-id "PVT_kwHOAFO-EM4BRTW5" \
327
- --field-id "PVTSSF_lAHOAFO-EM4BRTW5zg_K3tE" \
328
- --single-select-option-id "b60a81d0" 2>/dev/null || true
288
+ bash .claude/scripts/workpad-upsert.sh <N> "✅ Merged PR #<M>"
329
289
  ```
330
290
 
331
291
  After each merge, reply in the PR's Slack thread (best-effort):
332
292
  ```bash
333
293
  THREAD_REF=$(gh pr view $PR_NUMBER --json comments \
334
- --jq '[.comments[].body | select(startswith("<!-- slack-thread:"))] | first' \
335
- | sed 's/<!-- slack-thread: //;s/ -->//')
294
+ --jq '[.comments[].body | select(contains("slack-thread:"))] | first' \
295
+ | grep -oE '[A-Z][A-Z0-9]{8,}:[0-9]+\.[0-9]+')
336
296
  if [ -n "$THREAD_REF" ] && [ "$THREAD_REF" != "null" ]; then
337
297
  bun .claude/scripts/slack.ts reply \
338
298
  --thread "$THREAD_REF" \
@@ -134,6 +134,17 @@ export function evaluate(
134
134
  }
135
135
  }
136
136
 
137
+ // Branch creation inside a worktree — block (defense-in-depth)
138
+ // Agents in worktrees must use the worktree branch, never create new ones.
139
+ if (onWorktree && inWorktree) {
140
+ if (cmd.match(/git\s+(checkout\s+-b|switch\s+-c)/)) {
141
+ return {
142
+ decision: "block",
143
+ reason: `Branch creation blocked inside worktree on ${branch}. Use the worktree branch directly — do not create feature branches inside worktrees.`,
144
+ };
145
+ }
146
+ }
147
+
137
148
  // Worktree branch from inside the worktree — allow commits/pushes
138
149
  if (onWorktree && inWorktree) {
139
150
  return { decision: "approve" };
@@ -13,7 +13,7 @@
13
13
  import { readFileSync, existsSync } from "node:fs";
14
14
  import { execSync } from "node:child_process";
15
15
  import { resolve } from "node:path";
16
- import { isProtectedBranch } from "../lib/workflow-config.ts";
16
+ import { isProtectedBranch, isInWorktreePath } from "../lib/workflow-config.ts";
17
17
 
18
18
  export function getCurrentBranch(): string {
19
19
  try {
@@ -31,13 +31,38 @@ function isHomeBranch(branch: string): boolean {
31
31
  return isProtectedBranch(branch) || branch.startsWith("worktree-");
32
32
  }
33
33
 
34
+ function detectInWorktree(): boolean {
35
+ const cwd = process.env.CLAUDE_PROJECT_DIR || process.cwd();
36
+ try {
37
+ const toplevel = execSync("git rev-parse --show-toplevel", {
38
+ cwd, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"],
39
+ }).trim();
40
+ const gitCommonDir = execSync("git rev-parse --git-common-dir", {
41
+ cwd, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"],
42
+ }).trim();
43
+ const projectDir = resolve(toplevel, gitCommonDir, "..");
44
+ return isInWorktreePath(toplevel, projectDir);
45
+ } catch {
46
+ return false;
47
+ }
48
+ }
49
+
34
50
  /**
35
51
  * Returns a branch-state message to inject at session start.
36
52
  * Warning if on a protected/home branch, confirmation if on a feature branch.
53
+ *
54
+ * @param branch - The current git branch name
55
+ * @param inWorktree - Whether the session is running inside the owning worktree.
56
+ * When true and branch is a worktree-* branch, emits a positive confirmation
57
+ * instead of a warning (agent is in the right place, no feature branch needed).
37
58
  */
38
- export function getBranchWarning(branch: string): string {
59
+ export function getBranchWarning(branch: string, inWorktree?: boolean): string {
39
60
  if (branch === "unknown") return "";
40
61
 
62
+ if (branch.startsWith("worktree-") && inWorktree === true) {
63
+ return `✓ Current branch: \`${branch}\` (worktree) — commit and push from this branch directly. Do NOT create a feature branch.`;
64
+ }
65
+
41
66
  if (isHomeBranch(branch)) {
42
67
  return `⚠ BRANCH WARNING: You are on \`${branch}\` (a protected branch). You MUST create a feature branch before writing any files.\n\nRun this exact command first:\n\`\`\`\nrm -f .git/index.lock 2>/dev/null; git checkout -q -b feat/N-slug main\n\`\`\`\n\nDo NOT attempt Write, Edit, or Bash commits until you have created a feature branch.`;
43
68
  }
@@ -59,7 +84,8 @@ function main() {
59
84
  }
60
85
 
61
86
  const branch = getCurrentBranch();
62
- const branchWarning = getBranchWarning(branch);
87
+ const inWorktree = branch.startsWith("worktree-") ? detectInWorktree() : undefined;
88
+ const branchWarning = getBranchWarning(branch, inWorktree);
63
89
 
64
90
  const parts = [personality, branchWarning].filter(Boolean);
65
91
  if (parts.length === 0) return;
@@ -1,19 +1,24 @@
1
1
  #!/usr/bin/env bash
2
2
  # project-status.sh — Update Project #6 Status and Execution fields for an issue.
3
3
  #
4
- # Usage: bash .claude/scripts/project-status.sh <issue-number> <status> <execution>
4
+ # Usage: bash .claude/scripts/project-status.sh <issue-number> <status> <execution> [repo]
5
5
  #
6
6
  # Status values: Todo | "In Progress" | "Under Review" | Blocked
7
7
  # Execution values: Queued | Running | Review | Blocked
8
+ # repo (optional): owner/repo — required when running outside a git repo (e.g. global dir)
8
9
  #
9
10
  # Both fields are updated atomically. Missing or invalid values are skipped silently.
10
11
  # All operations are best-effort — failures are logged but never fatal.
11
12
 
12
13
  set -euo pipefail
13
14
 
14
- ISSUE_NUMBER="${1:?Usage: project-status.sh <issue-number> <status> <execution>}"
15
+ ISSUE_NUMBER="${1:?Usage: project-status.sh <issue-number> <status> <execution> [repo]}"
15
16
  STATUS="${2:?Missing status value}"
16
17
  EXECUTION="${3:?Missing execution value}"
18
+ REPO_FLAG=""
19
+ if [ -n "${4:-}" ]; then
20
+ REPO_FLAG="--repo $4"
21
+ fi
17
22
 
18
23
  # --- Project #6 constants ---
19
24
  PROJECT_ID="PVT_kwHOAFO-EM4BRTW5"
@@ -39,7 +44,7 @@ case "$EXECUTION" in
39
44
  esac
40
45
 
41
46
  # Get issue URL
42
- ISSUE_URL=$(gh issue view "$ISSUE_NUMBER" --json url --jq '.url' 2>/dev/null || echo "")
47
+ ISSUE_URL=$(gh issue view "$ISSUE_NUMBER" $REPO_FLAG --json url --jq '.url' 2>/dev/null || echo "")
43
48
  if [ -z "$ISSUE_URL" ]; then
44
49
  echo "[project-status] Could not find issue #$ISSUE_NUMBER" >&2
45
50
  exit 0
@@ -1,51 +1,44 @@
1
1
  #!/usr/bin/env bash
2
- # workpad-upsert.sh — posts or updates the <!-- claudius:workpad --> sticky comment
3
- # on a GitHub issue. Idempotent: finds existing workpad comment and patches it in-place.
2
+ # workpad-upsert.sh — posts or updates the <!-- claudius:workpad --> sticky comment.
3
+ # Idempotent: finds existing workpad comment and patches it in-place.
4
4
  #
5
5
  # Usage:
6
- # bash .claude/scripts/workpad-upsert.sh <issue-number> <status> [plan] [acs] [validation] [attempts]
6
+ # bash .claude/scripts/workpad-upsert.sh <issue-number> "<status>" ["<body>"]
7
7
  #
8
8
  # Arguments:
9
9
  # issue-number GitHub issue number (required)
10
- # status Status line, e.g. "🔄 Claimed — reading issue" (required)
11
- # plan Plan text (optional, default: "TBD")
12
- # acs ACs markdown (optional, default: "- [ ] (loading from issue)")
13
- # validation Validation status (optional, default: "pending")
14
- # attempts Build attempt count (optional, default: 1)
10
+ # status One-line status, e.g. "🔄 Claimed — attempt 1" (required)
11
+ # body Optional markdown body (ACs, validation, notes)
15
12
  #
16
- # Example:
17
- # bash .claude/scripts/workpad-upsert.sh 42 "🔄 Claimed — reading issue"
18
- # bash .claude/scripts/workpad-upsert.sh 42 "✅ Implementation complete" "Added auth middleware" "- [x] Token validated" "bun test: 45/45 pass" 2
19
- #
20
- # Run from the repo root (where gh is authenticated).
13
+ # Examples:
14
+ # bash .claude/scripts/workpad-upsert.sh 42 "🔄 Claimed — attempt 1"
15
+ # bash .claude/scripts/workpad-upsert.sh 42 "✅ Complete PR #55" "- [x] AC1\n- [x] AC2"
21
16
  set -euo pipefail
22
17
 
23
18
  ISSUE_NUMBER="${1:?'issue-number required'}"
24
19
  STATUS="${2:?'status required'}"
25
- PLAN="${3:-TBD}"
26
- ACS="${4:-- [ ] (loading from issue)}"
27
- VALIDATION="${5:-pending}"
28
- ATTEMPTS="${6:-1}"
29
- TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
20
+ BODY="${3:-}"
21
+ TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%MZ")
22
+
23
+ if [ -n "$BODY" ]; then
24
+ COMMENT="<!-- claudius:workpad -->
25
+ > ${STATUS} · ${TIMESTAMP}
30
26
 
31
- BODY="<!-- claudius:workpad -->
32
- **Workpad** · Updated: ${TIMESTAMP}
33
- **Status:** ${STATUS}
34
- **Attempts:** ${ATTEMPTS}
35
- **Plan:** ${PLAN}
36
- **ACs:**
37
- ${ACS}
38
- **Validation:** ${VALIDATION}"
27
+ ${BODY}"
28
+ else
29
+ COMMENT="<!-- claudius:workpad -->
30
+ > ${STATUS} · ${TIMESTAMP}"
31
+ fi
39
32
 
40
- # Find existing workpad comment ID
41
- EXISTING=$(gh issue view "$ISSUE_NUMBER" --json comments \
42
- --jq '[.comments[] | select(.body | contains("claudius:workpad"))] | last | .databaseId // empty' \
33
+ # Find existing workpad comment ID — search ALL comments, take the last match
34
+ EXISTING=$(gh api "repos/{owner}/{repo}/issues/${ISSUE_NUMBER}/comments" \
35
+ --paginate --jq '[.[] | select(.body | contains("claudius:workpad")) | .id] | last // empty' \
43
36
  2>/dev/null || echo "")
44
37
 
45
38
  if [ -n "$EXISTING" ]; then
46
- gh api "repos/{owner}/{repo}/issues/comments/${EXISTING}" -X PATCH -f body="$BODY" >/dev/null
47
- echo "Workpad updated on issue #${ISSUE_NUMBER} (comment ${EXISTING})"
39
+ gh api "repos/{owner}/{repo}/issues/comments/${EXISTING}" -X PATCH -f body="$COMMENT" >/dev/null
40
+ echo "Workpad updated on issue #${ISSUE_NUMBER}"
48
41
  else
49
- gh issue comment "$ISSUE_NUMBER" --body "$BODY" >/dev/null
42
+ gh issue comment "$ISSUE_NUMBER" --body "$COMMENT" >/dev/null
50
43
  echo "Workpad created on issue #${ISSUE_NUMBER}"
51
44
  fi
@@ -21,10 +21,10 @@
21
21
  "name": "claudius",
22
22
  "source": "./",
23
23
  "description": "Autonomous orchestration layer for Claude Code — agents, skills, hooks, and heartbeat daemon for round-the-clock development",
24
- "version": "0.9.6",
24
+ "version": "0.10.1",
25
25
  "strict": true,
26
26
  "category": "development"
27
27
  }
28
28
  ],
29
- "version": "0.9.6"
29
+ "version": "0.10.1"
30
30
  }
@@ -4,6 +4,7 @@ cron: "0 */4 * * *"
4
4
  enabled: true
5
5
  description: Convert approved Discussions to ready Issues
6
6
  timeout: 300
7
+ guardrails: true
7
8
  allowedTools: Bash,Read,Glob,Grep
8
9
  maxTurns: 20
9
10
  ---
@@ -10,7 +10,7 @@ export type { ScoredIssue } from "./priority.js";
10
10
  export { installDaemon, uninstallDaemon } from "./install.js";
11
11
  export { acquireLock, releaseLock, getLockInfo, stopRunningDaemon } from "./lock.js";
12
12
  export type { LockInfo } from "./lock.js";
13
- export { loadJobs, loadLastRuns, isDue, executeJob, logRun, getDueJobs, nextRunTime, parseFrontmatter, parseCron, fieldMatches, checkGuardrails, } from "./scheduler.js";
13
+ export { loadJobs, loadLastRuns, isDue, executeJob, logRun, getDueJobs, nextRunTime, parseFrontmatter, parseCron, fieldMatches, checkGuardrails, isGlobalSleeplessActive, } from "./scheduler.js";
14
14
  export type { JobDefinition, JobRun } from "./scheduler.js";
15
15
  export { isSlimInstalled, startSlimDomain, stopSlimDomain } from "./slim.js";
16
16
  export type { SlimHandle } from "./slim.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/daemon/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,YAAY,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACtE,OAAO,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAC5F,YAAY,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACzD,OAAO,EACN,UAAU,EACV,aAAa,EACb,eAAe,EACf,oBAAoB,EACpB,cAAc,GACd,MAAM,eAAe,CAAC;AACvB,YAAY,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC9D,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AACrF,YAAY,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EACN,QAAQ,EACR,YAAY,EACZ,KAAK,EACL,UAAU,EACV,MAAM,EACN,UAAU,EACV,WAAW,EACX,gBAAgB,EAChB,SAAS,EACT,YAAY,EACZ,eAAe,GACf,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAC5D,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC7E,YAAY,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/daemon/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,YAAY,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACtE,OAAO,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAC5F,YAAY,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACzD,OAAO,EACN,UAAU,EACV,aAAa,EACb,eAAe,EACf,oBAAoB,EACpB,cAAc,GACd,MAAM,eAAe,CAAC;AACvB,YAAY,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC9D,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AACrF,YAAY,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EACN,QAAQ,EACR,YAAY,EACZ,KAAK,EACL,UAAU,EACV,MAAM,EACN,UAAU,EACV,WAAW,EACX,gBAAgB,EAChB,SAAS,EACT,YAAY,EACZ,eAAe,EACf,uBAAuB,GACvB,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAC5D,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC7E,YAAY,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC"}
@@ -6,6 +6,6 @@ export { checkConstraints, isWithinActiveHours, getTimeInTimezone } from "./cons
6
6
  export { scoreIssue, pickBestIssue, filterUnblocked, clusterMomentumBonus, pickBestIssues, } from "./priority.js";
7
7
  export { installDaemon, uninstallDaemon } from "./install.js";
8
8
  export { acquireLock, releaseLock, getLockInfo, stopRunningDaemon } from "./lock.js";
9
- export { loadJobs, loadLastRuns, isDue, executeJob, logRun, getDueJobs, nextRunTime, parseFrontmatter, parseCron, fieldMatches, checkGuardrails, } from "./scheduler.js";
9
+ export { loadJobs, loadLastRuns, isDue, executeJob, logRun, getDueJobs, nextRunTime, parseFrontmatter, parseCron, fieldMatches, checkGuardrails, isGlobalSleeplessActive, } from "./scheduler.js";
10
10
  export { isSlimInstalled, startSlimDomain, stopSlimDomain } from "./slim.js";
11
11
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/daemon/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEzC,OAAO,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAE5F,OAAO,EACN,UAAU,EACV,aAAa,EACb,eAAe,EACf,oBAAoB,EACpB,cAAc,GACd,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC9D,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAErF,OAAO,EACN,QAAQ,EACR,YAAY,EACZ,KAAK,EACL,UAAU,EACV,MAAM,EACN,UAAU,EACV,WAAW,EACX,gBAAgB,EAChB,SAAS,EACT,YAAY,EACZ,eAAe,GACf,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/daemon/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEzC,OAAO,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAE5F,OAAO,EACN,UAAU,EACV,aAAa,EACb,eAAe,EACf,oBAAoB,EACpB,cAAc,GACd,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC9D,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAErF,OAAO,EACN,QAAQ,EACR,YAAY,EACZ,KAAK,EACL,UAAU,EACV,MAAM,EACN,UAAU,EACV,WAAW,EACX,gBAAgB,EAChB,SAAS,EACT,YAAY,EACZ,eAAe,EACf,uBAAuB,GACvB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC"}
@@ -11,6 +11,18 @@
11
11
  * - worktree → run in isolated git worktree (--worktree flag)
12
12
  */
13
13
  import type { ClaudiusConfig } from "../config.js";
14
+ /**
15
+ * Check whether a global sleepless orchestrator is active for this project.
16
+ *
17
+ * Detection: look for `../.claudius/config.yaml` with `mode: global`
18
+ * (presence of the global config indicates sleepless may be running),
19
+ * combined with an active entry for this repo in `../.claudius/active-builds.jsonl`.
20
+ *
21
+ * Returns true only when both signals are present — the global config marks
22
+ * the directory as a global orchestrator, and the active-builds entry confirms
23
+ * a worker for this specific repo is currently running.
24
+ */
25
+ export declare function isGlobalSleeplessActive(projectDir: string): boolean;
14
26
  export interface JobDefinition {
15
27
  name: string;
16
28
  cron: string;
@@ -1 +1 @@
1
- {"version":3,"file":"scheduler.d.ts","sourceRoot":"","sources":["../../src/daemon/scheduler.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"scheduler.d.ts","sourceRoot":"","sources":["../../src/daemon/scheduler.ts"],"names":[],"mappings":"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"}
@@ -14,6 +14,59 @@ import { execSync, spawnSync } from "node:child_process";
14
14
  import { appendFileSync, existsSync, mkdirSync, readFileSync, readdirSync } from "node:fs";
15
15
  import { dirname, resolve } from "node:path";
16
16
  import { checkConstraints } from "./constraints.js";
17
+ /**
18
+ * Build job names that spawn autonomous workers — these must be skipped when
19
+ * a global sleepless orchestrator is already managing this repo to prevent
20
+ * duplicate builds, race conditions, and conflicting worktree claims.
21
+ */
22
+ const BUILD_JOB_NAMES = new Set(["pulse", "pulsar"]);
23
+ /**
24
+ * Check whether a global sleepless orchestrator is active for this project.
25
+ *
26
+ * Detection: look for `../.claudius/config.yaml` with `mode: global`
27
+ * (presence of the global config indicates sleepless may be running),
28
+ * combined with an active entry for this repo in `../.claudius/active-builds.jsonl`.
29
+ *
30
+ * Returns true only when both signals are present — the global config marks
31
+ * the directory as a global orchestrator, and the active-builds entry confirms
32
+ * a worker for this specific repo is currently running.
33
+ */
34
+ export function isGlobalSleeplessActive(projectDir) {
35
+ const parentDir = resolve(projectDir, "..");
36
+ const globalConfigPath = resolve(parentDir, ".claudius/config.yaml");
37
+ const activeBuildsPath = resolve(parentDir, ".claudius/active-builds.jsonl");
38
+ // Fast exit: no global config means this is a standalone repo
39
+ if (!existsSync(globalConfigPath))
40
+ return false;
41
+ // Check global config has mode: global
42
+ try {
43
+ const configContent = readFileSync(globalConfigPath, "utf-8");
44
+ if (!configContent.includes("mode: global"))
45
+ return false;
46
+ }
47
+ catch {
48
+ return false;
49
+ }
50
+ // Check for an active-builds entry for this repo
51
+ if (!existsSync(activeBuildsPath))
52
+ return false;
53
+ try {
54
+ const repoName = resolve(projectDir).split("/").pop() ?? "";
55
+ const lines = readFileSync(activeBuildsPath, "utf-8").split("\n").filter(Boolean);
56
+ return lines.some((line) => {
57
+ try {
58
+ const entry = JSON.parse(line);
59
+ return entry.repo === repoName && entry.status === "running";
60
+ }
61
+ catch {
62
+ return false;
63
+ }
64
+ });
65
+ }
66
+ catch {
67
+ return false;
68
+ }
69
+ }
17
70
  /**
18
71
  * Parse simple YAML frontmatter from a markdown string.
19
72
  * Handles key: value pairs (strings, numbers, booleans). No nested objects.
@@ -419,7 +472,16 @@ export function logRun(projectDir, run) {
419
472
  export function getDueJobs(projectDir, jobsDir, now) {
420
473
  const jobs = loadJobs(projectDir, jobsDir);
421
474
  const lastRuns = loadLastRuns(projectDir);
422
- return jobs.filter((job) => isDue(job, lastRuns, now));
475
+ const sleeplessActive = isGlobalSleeplessActive(projectDir);
476
+ return jobs.filter((job) => {
477
+ if (!isDue(job, lastRuns, now))
478
+ return false;
479
+ // Skip build jobs when global sleepless is orchestrating this repo —
480
+ // running both would cause duplicate PRs and conflicting worktree claims.
481
+ if (sleeplessActive && BUILD_JOB_NAMES.has(job.name))
482
+ return false;
483
+ return true;
484
+ });
423
485
  }
424
486
  /**
425
487
  * Compute the next run time for a job (approximate — scans forward minute by minute).