@yemi33/minions 0.1.333 → 0.1.335
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/CHANGELOG.md +9 -1
- package/engine/ado.js +37 -12
- package/engine/github.js +41 -12
- package/engine/playbook.js +8 -0
- package/package.json +1 -1
- package/playbooks/build-and-test.md +1 -1
- package/playbooks/implement-shared.md +2 -2
- package/playbooks/implement.md +2 -2
- package/playbooks/plan-to-prd.md +2 -2
- package/playbooks/review.md +2 -2
- package/playbooks/test.md +1 -1
- package/playbooks/verify.md +1 -1
- package/playbooks/work-item.md +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## 0.1.
|
|
3
|
+
## 0.1.335 (2026-04-03)
|
|
4
|
+
|
|
5
|
+
### Fixes
|
|
6
|
+
- clear stale buildStatus when PRs are merged or abandoned
|
|
7
|
+
|
|
8
|
+
## 0.1.334 (2026-04-03)
|
|
9
|
+
|
|
10
|
+
### Features
|
|
11
|
+
- Fix PR write races in ado.js and github.js
|
|
4
12
|
|
|
5
13
|
### Fixes
|
|
6
14
|
- guarantee test cleanup via finally block in test harness
|
package/engine/ado.js
CHANGED
|
@@ -7,6 +7,7 @@ const path = require('path');
|
|
|
7
7
|
const shared = require('./shared');
|
|
8
8
|
const { exec, getAdoOrgBase, addPrLink, log, ts, dateStamp, PR_STATUS } = shared;
|
|
9
9
|
const { getPrs } = require('./queries');
|
|
10
|
+
const { mutateJsonFileLocked } = shared;
|
|
10
11
|
|
|
11
12
|
// Lazy require to avoid circular dependency — only needed for engine().handlePostMerge
|
|
12
13
|
let _engine = null;
|
|
@@ -99,7 +100,15 @@ async function forEachActivePr(config, token, callback) {
|
|
|
99
100
|
}
|
|
100
101
|
|
|
101
102
|
if (projectUpdated > 0) {
|
|
102
|
-
|
|
103
|
+
mutateJsonFileLocked(shared.projectPrPath(project), (currentPrs) => {
|
|
104
|
+
// Merge updated PRs into the locked copy by ID
|
|
105
|
+
for (const updatedPr of prs) {
|
|
106
|
+
const idx = currentPrs.findIndex(p => p.id === updatedPr.id);
|
|
107
|
+
if (idx >= 0) currentPrs[idx] = updatedPr;
|
|
108
|
+
else currentPrs.push(updatedPr);
|
|
109
|
+
}
|
|
110
|
+
return currentPrs;
|
|
111
|
+
}, { defaultValue: [] });
|
|
103
112
|
totalUpdated += projectUpdated;
|
|
104
113
|
}
|
|
105
114
|
}
|
|
@@ -137,6 +146,12 @@ async function pollPrStatus(config) {
|
|
|
137
146
|
pr.reviewStatus = newStatus === PR_STATUS.MERGED ? 'approved' : 'pending';
|
|
138
147
|
log('info', `PR ${pr.id} reviewStatus: waiting → ${pr.reviewStatus} (${newStatus})`);
|
|
139
148
|
}
|
|
149
|
+
// Clear stale build status — checks won't be polled after close
|
|
150
|
+
if (pr.buildStatus && pr.buildStatus !== 'none') {
|
|
151
|
+
delete pr.buildStatus;
|
|
152
|
+
delete pr.buildFailReason;
|
|
153
|
+
delete pr._buildFailNotified;
|
|
154
|
+
}
|
|
140
155
|
await engine().handlePostMerge(pr, project, config, newStatus);
|
|
141
156
|
}
|
|
142
157
|
}
|
|
@@ -174,11 +189,11 @@ async function pollPrStatus(config) {
|
|
|
174
189
|
if (authorId) {
|
|
175
190
|
try {
|
|
176
191
|
const metricsPath = path.join(__dirname, 'metrics.json');
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
192
|
+
mutateJsonFileLocked(metricsPath, (metrics) => {
|
|
193
|
+
if (!metrics[authorId]) metrics[authorId] = {};
|
|
194
|
+
if (newReviewStatus === 'approved') metrics[authorId].prsApproved = (metrics[authorId].prsApproved || 0) + 1;
|
|
195
|
+
else metrics[authorId].prsRejected = (metrics[authorId].prsRejected || 0) + 1;
|
|
196
|
+
});
|
|
182
197
|
} catch (err) { log('warn', `Metrics update: ${err.message}`); }
|
|
183
198
|
}
|
|
184
199
|
}
|
|
@@ -196,8 +211,8 @@ async function pollPrStatus(config) {
|
|
|
196
211
|
|
|
197
212
|
const buildStatuses = [...latest.values()].filter(s => {
|
|
198
213
|
const ctx = ((s.context?.genre || '') + '/' + (s.context?.name || '')).toLowerCase();
|
|
199
|
-
return ctx
|
|
200
|
-
ctx
|
|
214
|
+
return /\bcodecoverage\b/.test(ctx) || /\bbuild\b/.test(ctx) ||
|
|
215
|
+
/\bdeploy\b/.test(ctx) || /(?:^|\/)ci(?:\/|$)/.test(ctx);
|
|
201
216
|
});
|
|
202
217
|
|
|
203
218
|
let buildStatus = 'none';
|
|
@@ -248,7 +263,8 @@ async function pollPrHumanComments(config) {
|
|
|
248
263
|
const threadsData = await adoFetch(threadsUrl, token);
|
|
249
264
|
const threads = threadsData.value || [];
|
|
250
265
|
|
|
251
|
-
const
|
|
266
|
+
const cutoffStr = pr.humanFeedback?.lastProcessedCommentDate || pr.created || '1970-01-01';
|
|
267
|
+
const cutoffMs = new Date(cutoffStr).getTime() || 0;
|
|
252
268
|
|
|
253
269
|
// Collect ALL human comments on the PR for full context
|
|
254
270
|
const allHumanComments = [];
|
|
@@ -269,7 +285,8 @@ async function pollPrHumanComments(config) {
|
|
|
269
285
|
allHumanComments.push(entry);
|
|
270
286
|
|
|
271
287
|
// Track which comments are new (for triggering — any new comment triggers a fix)
|
|
272
|
-
|
|
288
|
+
const commentMs = comment.publishedDate ? new Date(comment.publishedDate).getTime() : 0;
|
|
289
|
+
if (commentMs && commentMs > cutoffMs) {
|
|
273
290
|
newHumanComments.push(entry);
|
|
274
291
|
}
|
|
275
292
|
}
|
|
@@ -285,7 +302,7 @@ async function pollPrHumanComments(config) {
|
|
|
285
302
|
// Provide ALL comments as context — the agent needs full thread context to fix properly
|
|
286
303
|
const feedbackContent = allHumanComments
|
|
287
304
|
.map(c => {
|
|
288
|
-
const isNew = c.date >
|
|
305
|
+
const isNew = (new Date(c.date).getTime() || 0) > cutoffMs;
|
|
289
306
|
return `${isNew ? '**[NEW]** ' : ''}**${c.author}** (${c.date}):\n${c.content.replace(/@minions\s*/gi, '').trim()}`;
|
|
290
307
|
})
|
|
291
308
|
.join('\n\n---\n\n');
|
|
@@ -412,7 +429,15 @@ async function reconcilePrs(config) {
|
|
|
412
429
|
}
|
|
413
430
|
|
|
414
431
|
if (projectAdded > 0 || projectUpdated > 0 || backfilled > 0) {
|
|
415
|
-
|
|
432
|
+
mutateJsonFileLocked(prPath, (currentPrs) => {
|
|
433
|
+
// Merge reconciled PRs into the locked copy by ID
|
|
434
|
+
for (const pr of existingPrs) {
|
|
435
|
+
const idx = currentPrs.findIndex(p => p.id === pr.id);
|
|
436
|
+
if (idx >= 0) currentPrs[idx] = pr;
|
|
437
|
+
else currentPrs.push(pr);
|
|
438
|
+
}
|
|
439
|
+
return currentPrs;
|
|
440
|
+
}, { defaultValue: [] });
|
|
416
441
|
totalAdded += projectAdded;
|
|
417
442
|
if (projectUpdated > 0) log('info', `PR reconciliation: linked ${projectUpdated} existing PR(s) to PRD items in ${project.name}`);
|
|
418
443
|
}
|
package/engine/github.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
const shared = require('./shared');
|
|
8
|
-
const { exec, getProjects, projectPrPath, projectWorkItemsPath, safeJson, safeWrite, MINIONS_DIR, addPrLink, getPrLinks, log, ts, dateStamp, PR_STATUS } = shared;
|
|
8
|
+
const { exec, getProjects, projectPrPath, projectWorkItemsPath, safeJson, safeWrite, mutateJsonFileLocked, MINIONS_DIR, addPrLink, getPrLinks, log, ts, dateStamp, PR_STATUS } = shared;
|
|
9
9
|
const { getPrs } = require('./queries');
|
|
10
10
|
const path = require('path');
|
|
11
11
|
|
|
@@ -71,7 +71,14 @@ async function forEachActiveGhPr(config, callback) {
|
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
if (projectUpdated > 0) {
|
|
74
|
-
|
|
74
|
+
mutateJsonFileLocked(projectPrPath(project), (currentPrs) => {
|
|
75
|
+
for (const updatedPr of prs) {
|
|
76
|
+
const idx = currentPrs.findIndex(p => p.id === updatedPr.id);
|
|
77
|
+
if (idx >= 0) currentPrs[idx] = updatedPr;
|
|
78
|
+
else currentPrs.push(updatedPr);
|
|
79
|
+
}
|
|
80
|
+
return currentPrs;
|
|
81
|
+
}, { defaultValue: [] });
|
|
75
82
|
totalUpdated += projectUpdated;
|
|
76
83
|
}
|
|
77
84
|
}
|
|
@@ -105,7 +112,14 @@ async function forEachActiveGhPr(config, callback) {
|
|
|
105
112
|
}
|
|
106
113
|
}
|
|
107
114
|
if (centralUpdated > 0) {
|
|
108
|
-
|
|
115
|
+
mutateJsonFileLocked(centralPath, (currentPrs) => {
|
|
116
|
+
for (const updatedPr of centralPrs) {
|
|
117
|
+
const idx = currentPrs.findIndex(p => p.id === updatedPr.id);
|
|
118
|
+
if (idx >= 0) currentPrs[idx] = updatedPr;
|
|
119
|
+
else currentPrs.push(updatedPr);
|
|
120
|
+
}
|
|
121
|
+
return currentPrs;
|
|
122
|
+
}, { defaultValue: [] });
|
|
109
123
|
totalUpdated += centralUpdated;
|
|
110
124
|
}
|
|
111
125
|
|
|
@@ -138,6 +152,12 @@ async function pollPrStatus(config) {
|
|
|
138
152
|
pr.reviewStatus = newStatus === PR_STATUS.MERGED ? 'approved' : 'pending';
|
|
139
153
|
log('info', `PR ${pr.id} reviewStatus: waiting → ${pr.reviewStatus} (${newStatus})`);
|
|
140
154
|
}
|
|
155
|
+
// Clear stale build status — checks won't be polled after close
|
|
156
|
+
if (pr.buildStatus && pr.buildStatus !== 'none') {
|
|
157
|
+
delete pr.buildStatus;
|
|
158
|
+
delete pr.buildFailReason;
|
|
159
|
+
delete pr._buildFailNotified;
|
|
160
|
+
}
|
|
141
161
|
await engine().handlePostMerge(pr, project, config, newStatus);
|
|
142
162
|
}
|
|
143
163
|
}
|
|
@@ -182,11 +202,11 @@ async function pollPrStatus(config) {
|
|
|
182
202
|
if (authorId) {
|
|
183
203
|
try {
|
|
184
204
|
const metricsPath = path.join(__dirname, 'metrics.json');
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
205
|
+
mutateJsonFileLocked(metricsPath, (metrics) => {
|
|
206
|
+
if (!metrics[authorId]) metrics[authorId] = {};
|
|
207
|
+
if (newReviewStatus === 'approved') metrics[authorId].prsApproved = (metrics[authorId].prsApproved || 0) + 1;
|
|
208
|
+
else metrics[authorId].prsRejected = (metrics[authorId].prsRejected || 0) + 1;
|
|
209
|
+
});
|
|
190
210
|
} catch (err) { log('warn', `Metrics update: ${err.message}`); }
|
|
191
211
|
}
|
|
192
212
|
}
|
|
@@ -258,7 +278,8 @@ async function pollPrHumanComments(config) {
|
|
|
258
278
|
return true;
|
|
259
279
|
});
|
|
260
280
|
|
|
261
|
-
const
|
|
281
|
+
const cutoffStr = pr.humanFeedback?.lastProcessedCommentDate || pr.created || '1970-01-01';
|
|
282
|
+
const cutoffMs = new Date(cutoffStr).getTime() || 0;
|
|
262
283
|
|
|
263
284
|
// Collect ALL human comments for full context, track new ones for triggering
|
|
264
285
|
const allCommentEntries = [];
|
|
@@ -275,7 +296,8 @@ async function pollPrHumanComments(config) {
|
|
|
275
296
|
allCommentEntries.push(entry);
|
|
276
297
|
|
|
277
298
|
// Any new comment triggers a fix — no @minions filter needed
|
|
278
|
-
|
|
299
|
+
const dateMs = date ? new Date(date).getTime() : 0;
|
|
300
|
+
if (dateMs && dateMs > cutoffMs) {
|
|
279
301
|
newComments.push(entry);
|
|
280
302
|
}
|
|
281
303
|
}
|
|
@@ -290,7 +312,7 @@ async function pollPrHumanComments(config) {
|
|
|
290
312
|
// Provide ALL comments as context — the agent needs full thread context to fix properly
|
|
291
313
|
const feedbackContent = allCommentEntries
|
|
292
314
|
.map(c => {
|
|
293
|
-
const isNew = c.date >
|
|
315
|
+
const isNew = (new Date(c.date).getTime() || 0) > cutoffMs;
|
|
294
316
|
return `${isNew ? '**[NEW]** ' : ''}**${c.author}** (${c.date}):\n${c.content.replace(/@minions\s*/gi, '').trim()}`;
|
|
295
317
|
})
|
|
296
318
|
.join('\n\n---\n\n');
|
|
@@ -397,7 +419,14 @@ async function reconcilePrs(config) {
|
|
|
397
419
|
}
|
|
398
420
|
|
|
399
421
|
if (projectAdded > 0 || backfilled > 0) {
|
|
400
|
-
|
|
422
|
+
mutateJsonFileLocked(prPath, (currentPrs) => {
|
|
423
|
+
for (const pr of existingPrs) {
|
|
424
|
+
const idx = currentPrs.findIndex(p => p.id === pr.id);
|
|
425
|
+
if (idx >= 0) currentPrs[idx] = pr;
|
|
426
|
+
else currentPrs.push(pr);
|
|
427
|
+
}
|
|
428
|
+
return currentPrs;
|
|
429
|
+
}, { defaultValue: [] });
|
|
401
430
|
totalAdded += projectAdded;
|
|
402
431
|
}
|
|
403
432
|
}
|
package/engine/playbook.js
CHANGED
|
@@ -293,6 +293,14 @@ function renderPlaybook(type, vars) {
|
|
|
293
293
|
log('warn', `Playbook "${type}": substituted values contain unresolved {{...}} patterns (potential self-reference): ${selfRefVars.join(', ')}`);
|
|
294
294
|
}
|
|
295
295
|
|
|
296
|
+
// Warn when a substituted value itself contains {{...}} patterns (potential self-reference)
|
|
297
|
+
const selfRefVars = Object.entries(allVars)
|
|
298
|
+
.filter(([, val]) => /\{\{\w+\}\}/.test(String(val)))
|
|
299
|
+
.map(([key]) => key);
|
|
300
|
+
if (selfRefVars.length > 0) {
|
|
301
|
+
log('warn', `Playbook "${type}": substituted values contain unresolved {{...}} patterns (potential self-reference): ${selfRefVars.join(', ')}`);
|
|
302
|
+
}
|
|
303
|
+
|
|
296
304
|
// Warn on variables that resolved to empty string
|
|
297
305
|
const emptyVars = Object.entries(allVars)
|
|
298
306
|
.filter(([, val]) => String(val) === '')
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.335",
|
|
4
4
|
"description": "Multi-agent AI dev team that runs from ~/.minions/ — five autonomous agents share a single engine, dashboard, and knowledge base",
|
|
5
5
|
"bin": {
|
|
6
6
|
"minions": "bin/minions.js"
|
|
@@ -102,7 +102,7 @@ Structure your report exactly like this:
|
|
|
102
102
|
|
|
103
103
|
## Auto-file Work Items on Failure
|
|
104
104
|
|
|
105
|
-
If the build
|
|
105
|
+
If the build or tests fail, create a work item so another agent can fix it. Write a JSON entry to the project's work queue:
|
|
106
106
|
|
|
107
107
|
```bash
|
|
108
108
|
# Read existing items, append new one, write back
|
|
@@ -17,7 +17,7 @@ Implement PRD item **{{item_id}}: {{item_name}}**
|
|
|
17
17
|
|
|
18
18
|
This is part of a **shared-branch plan**. Other agents may have already committed work to this branch before you. Your job is to build on top of their work.
|
|
19
19
|
|
|
20
|
-
## Git Workflow (
|
|
20
|
+
## Git Workflow (Shared Branch)
|
|
21
21
|
|
|
22
22
|
Your worktree is already set up. Pull latest before starting:
|
|
23
23
|
|
|
@@ -54,7 +54,7 @@ git push origin {{branch_name}}
|
|
|
54
54
|
|
|
55
55
|
## Build and Verify
|
|
56
56
|
|
|
57
|
-
After implementation
|
|
57
|
+
After implementation:
|
|
58
58
|
1. Build the project using the repo's build system (check CLAUDE.md, package.json, README)
|
|
59
59
|
2. Verify the build succeeds with your changes AND all prior commits on this branch
|
|
60
60
|
3. If the build fails:
|
package/playbooks/implement.md
CHANGED
|
@@ -58,7 +58,7 @@ Do NOT remove the worktree — the engine handles cleanup automatically.
|
|
|
58
58
|
|
|
59
59
|
## Build and Demo Rule
|
|
60
60
|
|
|
61
|
-
After implementation
|
|
61
|
+
After implementation:
|
|
62
62
|
1. Build the project using the repo's build system (check CLAUDE.md, package.json, README)
|
|
63
63
|
2. Start if applicable
|
|
64
64
|
3. Include the browser URL and run instructions in the PR description
|
|
@@ -71,7 +71,7 @@ After building, verify the build succeeded. If the build fails:
|
|
|
71
71
|
|
|
72
72
|
## Test Validation (MANDATORY before PR)
|
|
73
73
|
|
|
74
|
-
Before creating a PR,
|
|
74
|
+
Before creating a PR, run the project's test suite and ensure all existing tests pass:
|
|
75
75
|
|
|
76
76
|
1. Find the test command by reading the project's own documentation — check CLAUDE.md, agent.md, README, or package.json scripts in the project root. Every project defines its own conventions.
|
|
77
77
|
2. Run the full test suite using whatever command the project specifies
|
package/playbooks/plan-to-prd.md
CHANGED
|
@@ -18,7 +18,7 @@ A user has provided a plan. Analyze it against the codebase and produce a struct
|
|
|
18
18
|
3. **Break the plan into discrete, implementable items** — each should be a single PR's worth of work
|
|
19
19
|
4. **Estimate complexity** — `small` (< 1 file), `medium` (2-5 files), `large` (6+ files or cross-cutting)
|
|
20
20
|
5. **Order by dependency** — items that others depend on come first
|
|
21
|
-
6. **Use unique item IDs** — generate a short uuid for each item (e.g. `P-a3f9b2c1`).
|
|
21
|
+
6. **Use unique item IDs** — generate a short uuid for each item (e.g. `P-a3f9b2c1`). Do not use sequential `P001`/`P002` — IDs must be globally unique across all PRDs to avoid collisions
|
|
22
22
|
7. **Identify open questions** — flag anything ambiguous in the plan that needs user input
|
|
23
23
|
|
|
24
24
|
## Output
|
|
@@ -83,7 +83,7 @@ When using `parallel`:
|
|
|
83
83
|
|
|
84
84
|
Rules for items:
|
|
85
85
|
- IDs must be `P-<uuid>` format (e.g. `P-a3f9b2c1`) — globally unique, never sequential
|
|
86
|
-
- **`status`
|
|
86
|
+
- **`status` is always `"missing"`** — do not set `done`, `complete`, `implemented`, or any other value, even if you observe active PRs or completed work in the codebase. Status is exclusively engine-managed after the PRD is written. Pre-setting any other status causes items to be silently skipped by the engine and breaks dependency resolution for all downstream items.
|
|
87
87
|
- **`project` field is REQUIRED** — set it to the project name where the code changes go (e.g., `"OfficeAgent"`, `"office-bohemia"`). Cross-repo plans must route each item to the correct project. The engine materializes items into that project's work queue.
|
|
88
88
|
- `depends_on` lists IDs of items that must be done first
|
|
89
89
|
- Keep descriptions actionable — an implementing agent should know exactly what to build
|
package/playbooks/review.md
CHANGED
|
@@ -38,7 +38,7 @@ Branch: `{{pr_branch}}`
|
|
|
38
38
|
|
|
39
39
|
## Post Review — Comment AND Vote on PR
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
Do both of the following:
|
|
42
42
|
|
|
43
43
|
### Step 1: Leave a detailed review comment
|
|
44
44
|
|
|
@@ -61,7 +61,7 @@ This vote is visible to human reviewers in the PR UI and helps them understand t
|
|
|
61
61
|
If you encounter merge conflicts (e.g., the PR shows conflicts):
|
|
62
62
|
1. Note the conflict in your review comment. Do NOT attempt to resolve — flag it for the author.
|
|
63
63
|
|
|
64
|
-
##
|
|
64
|
+
## Do not run git checkout on the main working tree. Use `git diff` and `git show` only.
|
|
65
65
|
|
|
66
66
|
## Signal Completion
|
|
67
67
|
|
package/playbooks/test.md
CHANGED
|
@@ -37,7 +37,7 @@ This is a **test/build/run task**. Your goal is to build, run, test, or verify s
|
|
|
37
37
|
|
|
38
38
|
## Run Command (IMPORTANT)
|
|
39
39
|
|
|
40
|
-
When the build succeeds and the task involves running a server or app,
|
|
40
|
+
When the build succeeds and the task involves running a server or app, output a ready-to-paste run command using **absolute paths** so the user can launch it from any terminal. Format it exactly like this:
|
|
41
41
|
|
|
42
42
|
```
|
|
43
43
|
## Run Command
|
package/playbooks/verify.md
CHANGED
|
@@ -218,7 +218,7 @@ For each project worktree:
|
|
|
218
218
|
- If a project doesn't build, still document what SHOULD be testable once fixed
|
|
219
219
|
- Do NOT fix code — only report issues
|
|
220
220
|
- Leave all worktrees in place for the user to inspect
|
|
221
|
-
-
|
|
221
|
+
- Start the application **detached** so it keeps running after your process exits
|
|
222
222
|
- Use absolute paths everywhere so the user can copy-paste commands
|
|
223
223
|
- E2E PRs are for review only — do NOT auto-complete or merge them
|
|
224
224
|
|
package/playbooks/work-item.md
CHANGED
|
@@ -53,7 +53,7 @@ Write your findings to: `{{team_root}}/notes/inbox/{{agent_id}}-{{item_id}}-{{da
|
|
|
53
53
|
**Note:** Do NOT write to `agents/*/status.json` — the engine manages your status automatically.
|
|
54
54
|
|
|
55
55
|
## Rules
|
|
56
|
-
-
|
|
56
|
+
- Do not checkout branches in the main working tree — use worktrees
|
|
57
57
|
- Use the repo host's MCP tools for PR creation — check available MCP tools before starting
|
|
58
58
|
- Use PowerShell for build commands on Windows if applicable
|
|
59
59
|
- If you discover a repeatable workflow, output it as a ```skill block (the engine auto-extracts it to ~/.claude/skills/)
|