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.
- package/.claude/agents/developer.md +4 -2
- package/.claude/agents/manager.md +5 -5
- package/.claude/agents/pulsar.md +4 -4
- package/.claude/agents/worker.md +21 -61
- package/.claude/hooks/guardian.ts +11 -0
- package/.claude/hooks/session-context.ts +29 -3
- package/.claude/scripts/project-status.sh +8 -3
- package/.claude/scripts/workpad-upsert.sh +25 -32
- package/.claude-plugin/marketplace.json +2 -2
- package/.claudius/jobs/qualify-ideas.md +1 -0
- package/dist/daemon/index.d.ts +1 -1
- package/dist/daemon/index.d.ts.map +1 -1
- package/dist/daemon/index.js +1 -1
- package/dist/daemon/index.js.map +1 -1
- package/dist/daemon/scheduler.d.ts +12 -0
- package/dist/daemon/scheduler.d.ts.map +1 -1
- package/dist/daemon/scheduler.js +63 -1
- package/dist/daemon/scheduler.js.map +1 -1
- package/dist/index.js +20 -3
- package/dist/index.js.map +1 -1
- package/dist/studio/assets/{_basePickBy-R-uis1nJ.js → _basePickBy-DFTr0P1q.js} +1 -1
- package/dist/studio/assets/{_baseUniq-DYva2fSG.js → _baseUniq-DZrBnw2L.js} +1 -1
- package/dist/studio/assets/{arc-BzeI-lxb.js → arc-C15jqhOi.js} +1 -1
- package/dist/studio/assets/{architectureDiagram-VXUJARFQ-DMa_o-aK.js → architectureDiagram-VXUJARFQ-CfBqrOzP.js} +1 -1
- package/dist/studio/assets/{blockDiagram-VD42YOAC-BFN1vazA.js → blockDiagram-VD42YOAC-vmfPvO8X.js} +1 -1
- package/dist/studio/assets/{c4Diagram-YG6GDRKO-BFDthHUP.js → c4Diagram-YG6GDRKO-DBQ_zJwo.js} +1 -1
- package/dist/studio/assets/channel-BNAWutZ3.js +1 -0
- package/dist/studio/assets/{chunk-4BX2VUAB-BcntFbLt.js → chunk-4BX2VUAB-DdUKHJpP.js} +1 -1
- package/dist/studio/assets/{chunk-55IACEB6-sNY6b1p-.js → chunk-55IACEB6-CwnLV00T.js} +1 -1
- package/dist/studio/assets/{chunk-B4BG7PRW-BLekgdES.js → chunk-B4BG7PRW-sLqc4EUj.js} +1 -1
- package/dist/studio/assets/{chunk-DI55MBZ5-CZw9ICRq.js → chunk-DI55MBZ5-DIMS_MIj.js} +1 -1
- package/dist/studio/assets/{chunk-FMBD7UC4-ntqmlvfQ.js → chunk-FMBD7UC4-ggnRtpJV.js} +1 -1
- package/dist/studio/assets/{chunk-QN33PNHL-CE-WX1bS.js → chunk-QN33PNHL-DaMEhziG.js} +1 -1
- package/dist/studio/assets/{chunk-QZHKN3VN-GyZ2p2Bl.js → chunk-QZHKN3VN-D4UgN63e.js} +1 -1
- package/dist/studio/assets/{chunk-TZMSLE5B--hsg0lEr.js → chunk-TZMSLE5B-a0lav95f.js} +1 -1
- package/dist/studio/assets/classDiagram-2ON5EDUG-DCggxQO8.js +1 -0
- package/dist/studio/assets/classDiagram-v2-WZHVMYZB-DCggxQO8.js +1 -0
- package/dist/studio/assets/clone-DK5ebX1Z.js +1 -0
- package/dist/studio/assets/{cose-bilkent-S5V4N54A-C4C-DPXZ.js → cose-bilkent-S5V4N54A-DLfcjX_v.js} +1 -1
- package/dist/studio/assets/{dagre-6UL2VRFP-1Bb2UhRi.js → dagre-6UL2VRFP-DPYF8SN1.js} +1 -1
- package/dist/studio/assets/{diagram-PSM6KHXK-C-FzwLf5.js → diagram-PSM6KHXK-gefKvZL9.js} +1 -1
- package/dist/studio/assets/{diagram-QEK2KX5R-Bctxbrmx.js → diagram-QEK2KX5R-CN7MUqrm.js} +1 -1
- package/dist/studio/assets/{diagram-S2PKOQOG-3uep1mdW.js → diagram-S2PKOQOG-D3UqjjFf.js} +1 -1
- package/dist/studio/assets/{erDiagram-Q2GNP2WA-B_Db2yYe.js → erDiagram-Q2GNP2WA-CF-8MoUF.js} +1 -1
- package/dist/studio/assets/{flowDiagram-NV44I4VS-Bzc-kwIX.js → flowDiagram-NV44I4VS-COkpkBNb.js} +1 -1
- package/dist/studio/assets/{ganttDiagram-JELNMOA3-BXibfYz-.js → ganttDiagram-JELNMOA3-DNJyrEK_.js} +1 -1
- package/dist/studio/assets/{gitGraphDiagram-V2S2FVAM-BLkjIgg1.js → gitGraphDiagram-V2S2FVAM-Dks7m-EG.js} +1 -1
- package/dist/studio/assets/{graph-B0Z_gmOm.js → graph-CpOxi7r6.js} +1 -1
- package/dist/studio/assets/{index-Ddl_PR-h.js → index-CUKxqiaW.js} +2 -2
- package/dist/studio/assets/{infoDiagram-HS3SLOUP-BeFdN5yb.js → infoDiagram-HS3SLOUP-BNap3Gvz.js} +1 -1
- package/dist/studio/assets/{journeyDiagram-XKPGCS4Q--icE_FdD.js → journeyDiagram-XKPGCS4Q-DcY1nvRz.js} +1 -1
- package/dist/studio/assets/{kanban-definition-3W4ZIXB7-BYGur8C7.js → kanban-definition-3W4ZIXB7-Ccyqqx5g.js} +1 -1
- package/dist/studio/assets/{layout-B4VDGqHe.js → layout-Bp-sWlva.js} +1 -1
- package/dist/studio/assets/{linear-Bm1oIJ5Y.js → linear-Cn9wajO0.js} +1 -1
- package/dist/studio/assets/{mermaid.core-Bl28GW7R.js → mermaid.core-Do-o_wmR.js} +4 -4
- package/dist/studio/assets/{mindmap-definition-VGOIOE7T-DC0Ch_L5.js → mindmap-definition-VGOIOE7T-Bshwlp_p.js} +1 -1
- package/dist/studio/assets/{pieDiagram-ADFJNKIX-Cfm6OLdr.js → pieDiagram-ADFJNKIX-BIyxi8K7.js} +1 -1
- package/dist/studio/assets/{quadrantDiagram-AYHSOK5B-C68ftZIq.js → quadrantDiagram-AYHSOK5B-CgiJ4dLs.js} +1 -1
- package/dist/studio/assets/{requirementDiagram-UZGBJVZJ-yH30jAOj.js → requirementDiagram-UZGBJVZJ-DiMA0f-Z.js} +1 -1
- package/dist/studio/assets/{sankeyDiagram-TZEHDZUN-BlEt27YS.js → sankeyDiagram-TZEHDZUN-DcwbypJY.js} +1 -1
- package/dist/studio/assets/{sequenceDiagram-WL72ISMW-CT2uLY3i.js → sequenceDiagram-WL72ISMW-C_f3iggK.js} +1 -1
- package/dist/studio/assets/{stateDiagram-FKZM4ZOC-DuMW3t9Q.js → stateDiagram-FKZM4ZOC-Cvk4ecgi.js} +1 -1
- package/dist/studio/assets/stateDiagram-v2-4FDKWEC3-BbCKSvPk.js +1 -0
- package/dist/studio/assets/{timeline-definition-IT6M3QCI-BGETR7lR.js → timeline-definition-IT6M3QCI-hLqQiB-V.js} +1 -1
- package/dist/studio/assets/{treemap-GDKQZRPO-BO8oqrvF.js → treemap-GDKQZRPO-BA5_-5Tc.js} +1 -1
- package/dist/studio/assets/{xychartDiagram-PRI3JC2R-DJ-F4Odf.js → xychartDiagram-PRI3JC2R-IqrVisKP.js} +1 -1
- package/dist/studio/index.html +1 -1
- package/global/scripts/project-sync.sh +5 -5
- package/package.json +7 -2
- package/dist/studio/assets/channel-huxXjZrf.js +0 -1
- package/dist/studio/assets/classDiagram-2ON5EDUG-ufBxprI3.js +0 -1
- package/dist/studio/assets/classDiagram-v2-WZHVMYZB-ufBxprI3.js +0 -1
- package/dist/studio/assets/clone-BRn04n6L.js +0 -1
- 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
|
|
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
|
-
-
|
|
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
|
|
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(
|
|
86
|
-
|
|
|
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" \
|
package/.claude/agents/pulsar.md
CHANGED
|
@@ -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
|
|
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(
|
|
183
|
-
|
|
|
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" \
|
package/.claude/agents/worker.md
CHANGED
|
@@ -102,23 +102,16 @@ for CANDIDATE in <issue-numbers>; do
|
|
|
102
102
|
done
|
|
103
103
|
```
|
|
104
104
|
|
|
105
|
-
**Check attempt count before 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
|
|
110
|
-
--jq '[.
|
|
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
|
-
|
|
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** —
|
|
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
|
-
|
|
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
|
|
142
|
-
|
|
130
|
+
**Update Project #6 board → In Progress / Running:**
|
|
143
131
|
```bash
|
|
144
|
-
|
|
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
|
-
-
|
|
189
|
-
|
|
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
|
|
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
|
|
244
|
+
**Update workpad + board → Under Review / Review:**
|
|
268
245
|
```bash
|
|
269
|
-
|
|
270
|
-
|
|
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
|
|
273
|
+
**Update board → Blocked:**
|
|
302
274
|
```bash
|
|
303
|
-
|
|
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
|
|
286
|
+
**Update workpad + close issue** (closing removes it from the board via project-sync):
|
|
321
287
|
```bash
|
|
322
|
-
|
|
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(
|
|
335
|
-
|
|
|
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
|
|
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
|
-
#
|
|
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> [
|
|
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
|
|
11
|
-
#
|
|
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
|
-
#
|
|
17
|
-
# bash .claude/scripts/workpad-upsert.sh 42 "🔄 Claimed —
|
|
18
|
-
# bash .claude/scripts/workpad-upsert.sh 42 "✅
|
|
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
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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
|
|
42
|
-
--jq '[.
|
|
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="$
|
|
47
|
-
echo "Workpad updated on issue #${ISSUE_NUMBER}
|
|
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 "$
|
|
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.
|
|
24
|
+
"version": "0.10.1",
|
|
25
25
|
"strict": true,
|
|
26
26
|
"category": "development"
|
|
27
27
|
}
|
|
28
28
|
],
|
|
29
|
-
"version": "0.
|
|
29
|
+
"version": "0.10.1"
|
|
30
30
|
}
|
package/dist/daemon/index.d.ts
CHANGED
|
@@ -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,
|
|
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"}
|
package/dist/daemon/index.js
CHANGED
|
@@ -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
|
package/dist/daemon/index.js.map
CHANGED
|
@@ -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,
|
|
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":"AAAA;;;;;;;;;;;GAWG;AAKH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"scheduler.d.ts","sourceRoot":"","sources":["../../src/daemon/scheduler.ts"],"names":[],"mappings":"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"}
|
package/dist/daemon/scheduler.js
CHANGED
|
@@ -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
|
-
|
|
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).
|