@cobusgreyling/loop-init 1.2.3 → 1.3.2
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/README.md +2 -0
- package/dist/cli.js +133 -31
- package/package.json +6 -2
- package/starters/README.md +8 -1
- package/starters/changelog-drafter-opencode/AGENTS.md +15 -0
- package/starters/changelog-drafter-opencode/LOOP.md +26 -0
- package/starters/changelog-drafter-opencode/README.md +44 -0
- package/starters/changelog-drafter-opencode/changelog-drafter-state.md.example +10 -0
- package/starters/changelog-drafter-opencode/opencode.json.example +35 -0
- package/starters/changelog-drafter-opencode/skills/changelog-scan/SKILL.md +38 -0
- package/starters/ci-sweeper-opencode/AGENTS.md +17 -0
- package/starters/ci-sweeper-opencode/LOOP.md +26 -0
- package/starters/ci-sweeper-opencode/README.md +43 -0
- package/starters/ci-sweeper-opencode/ci-sweeper-state.md.example +10 -0
- package/starters/ci-sweeper-opencode/opencode.json.example +35 -0
- package/starters/ci-sweeper-opencode/skills/ci-triage/SKILL.md +32 -0
- package/starters/dependency-sweeper-opencode/AGENTS.md +15 -0
- package/starters/dependency-sweeper-opencode/LOOP.md +27 -0
- package/starters/dependency-sweeper-opencode/README.md +44 -0
- package/starters/dependency-sweeper-opencode/dependency-sweeper-state.md.example +11 -0
- package/starters/dependency-sweeper-opencode/opencode.json.example +35 -0
- package/starters/dependency-sweeper-opencode/skills/dependency-triage/SKILL.md +35 -0
- package/starters/issue-triage-opencode/AGENTS.md +16 -0
- package/starters/issue-triage-opencode/LOOP.md +21 -0
- package/starters/issue-triage-opencode/README.md +43 -0
- package/starters/issue-triage-opencode/issue-triage-state.md.example +11 -0
- package/starters/issue-triage-opencode/opencode.json.example +35 -0
- package/starters/issue-triage-opencode/skills/issue-triage/SKILL.md +33 -0
- package/starters/minimal-loop-opencode/AGENTS.md +23 -0
- package/starters/minimal-loop-opencode/LOOP.md +33 -0
- package/starters/minimal-loop-opencode/README.md +50 -0
- package/starters/minimal-loop-opencode/STATE.md.example +12 -0
- package/starters/minimal-loop-opencode/opencode.json.example +35 -0
- package/starters/minimal-loop-opencode/skills/loop-triage/SKILL.md +59 -0
- package/starters/post-merge-cleanup-opencode/AGENTS.md +15 -0
- package/starters/post-merge-cleanup-opencode/LOOP.md +26 -0
- package/starters/post-merge-cleanup-opencode/README.md +44 -0
- package/starters/post-merge-cleanup-opencode/opencode.json.example +35 -0
- package/starters/post-merge-cleanup-opencode/post-merge-state.md.example +10 -0
- package/starters/post-merge-cleanup-opencode/skills/post-merge-scan/SKILL.md +34 -0
- package/starters/pr-babysitter-opencode/AGENTS.md +18 -0
- package/starters/pr-babysitter-opencode/LOOP.md +27 -0
- package/starters/pr-babysitter-opencode/README.md +44 -0
- package/starters/pr-babysitter-opencode/opencode.json.example +35 -0
- package/starters/pr-babysitter-opencode/pr-babysitter-state.md.example +10 -0
- package/starters/pr-babysitter-opencode/skills/pr-review-triage/SKILL.md +33 -0
package/README.md
CHANGED
|
@@ -8,6 +8,7 @@ Scaffold loop engineering starters into your project by pattern and tool.
|
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
10
|
npx @cobusgreyling/loop-init . --pattern daily-triage --tool grok
|
|
11
|
+
npx @cobusgreyling/loop-init . --pattern daily-triage --tool opencode
|
|
11
12
|
npx @cobusgreyling/loop-init . -p pr-babysitter -t claude
|
|
12
13
|
npx @cobusgreyling/loop-init . -p dependency-sweeper --dry-run
|
|
13
14
|
```
|
|
@@ -41,6 +42,7 @@ Every scaffold also creates:
|
|
|
41
42
|
- `grok` (default)
|
|
42
43
|
- `claude`
|
|
43
44
|
- `codex`
|
|
45
|
+
- `opencode` — daily-triage ships `minimal-loop-opencode` (`skills/`, `AGENTS.md`, `opencode.json`)
|
|
44
46
|
|
|
45
47
|
Falls back to Grok starter paths when a per-tool variant is not yet available.
|
|
46
48
|
|
package/dist/cli.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { spawn } from 'node:child_process';
|
|
2
3
|
import { cp, mkdir, readFile, writeFile, access } from 'node:fs/promises';
|
|
3
4
|
import path from 'node:path';
|
|
4
5
|
import { fileURLToPath } from 'node:url';
|
|
@@ -19,6 +20,7 @@ const TOOL_SUFFIX = {
|
|
|
19
20
|
grok: '',
|
|
20
21
|
claude: '-claude',
|
|
21
22
|
codex: '-codex',
|
|
23
|
+
opencode: '-opencode',
|
|
22
24
|
};
|
|
23
25
|
const L2_PATTERNS = new Set(['ci-sweeper', 'dependency-sweeper']);
|
|
24
26
|
const PATTERNS_NEEDING_FIX = new Set([
|
|
@@ -99,6 +101,7 @@ async function copyTemplateSkill(templatesRoot, templateFile, targetDir, tool, s
|
|
|
99
101
|
grok: path.join(targetDir, '.grok', 'skills', skillName, 'SKILL.md'),
|
|
100
102
|
claude: path.join(targetDir, '.claude', 'skills', skillName, 'SKILL.md'),
|
|
101
103
|
codex: path.join(targetDir, '.codex', 'skills', skillName, 'SKILL.md'),
|
|
104
|
+
opencode: path.join(targetDir, 'skills', skillName, 'SKILL.md'),
|
|
102
105
|
};
|
|
103
106
|
const dest = destByTool[tool];
|
|
104
107
|
if (await exists(dest))
|
|
@@ -110,6 +113,7 @@ async function copyTemplateVerifier(templatesRoot, targetDir, tool, dryRun) {
|
|
|
110
113
|
grok: path.join(targetDir, '.grok', 'skills', 'loop-verifier', 'SKILL.md'),
|
|
111
114
|
claude: path.join(targetDir, '.claude', 'agents', 'loop-verifier.md'),
|
|
112
115
|
codex: path.join(targetDir, '.codex', 'agents', 'verifier.toml'),
|
|
116
|
+
opencode: path.join(targetDir, 'skills', 'loop-verifier', 'SKILL.md'),
|
|
113
117
|
};
|
|
114
118
|
const dest = verifierPaths[tool];
|
|
115
119
|
if (await exists(dest))
|
|
@@ -214,53 +218,112 @@ async function copyFile(src, dest, dryRun) {
|
|
|
214
218
|
console.log(` copied: ${src} → ${dest}`);
|
|
215
219
|
return true;
|
|
216
220
|
}
|
|
221
|
+
const OPENCODE_RUN = 'opencode run';
|
|
217
222
|
function firstLoopCommand(pattern, tool) {
|
|
218
223
|
const cmds = {
|
|
219
224
|
'daily-triage': {
|
|
220
225
|
grok: '/loop 1d Run loop-triage. Update STATE.md. No auto-fix in week one.',
|
|
221
226
|
claude: '/loop 1d $loop-triage — update STATE.md. Report-only week one.',
|
|
222
227
|
codex: 'Automation daily: $loop-triage → update STATE.md. Report-only.',
|
|
228
|
+
opencode: `${OPENCODE_RUN} "Run loop-triage. Read STATE.md first. Update High Priority and Watch List. No auto-fix in week one." --agent loop-triage`,
|
|
223
229
|
},
|
|
224
230
|
'pr-babysitter': {
|
|
225
231
|
grok: '/loop 10m Run pr-review-triage. Update pr-babysitter-state.md. Worktree + minimal-fix + verifier for allowlisted PRs only. Escalate after 3 attempts.',
|
|
226
232
|
claude: '/loop 10m $pr-review-triage — update pr-babysitter-state.md. No auto-merge.',
|
|
227
233
|
codex: 'Automation 10m: pr-review-triage → pr-babysitter-state.md. No auto-merge.',
|
|
234
|
+
opencode: `${OPENCODE_RUN} "Run PR babysitter triage. Read pr-babysitter-state.md first. Report only — no code edits." --title "PR babysitter"`,
|
|
228
235
|
},
|
|
229
236
|
'ci-sweeper': {
|
|
230
237
|
grok: '/loop 15m Run ci-triage on failing CI. Update ci-sweeper-state.md. Fix only regressions in worktree. Max 3 attempts.',
|
|
231
238
|
claude: '/loop 15m $ci-triage — update ci-sweeper-state.md. Max 3 fix attempts.',
|
|
232
239
|
codex: 'Automation 15m: ci-triage on CI failures. Max 3 attempts.',
|
|
240
|
+
opencode: `${OPENCODE_RUN} "Run ci-triage on failing CI. Update ci-sweeper-state.md. Report only in week one."`,
|
|
233
241
|
},
|
|
234
242
|
'dependency-sweeper': {
|
|
235
243
|
grok: '/loop 6h Run dependency-triage. Patch-only auto-fix in worktree + verifier. Escalate majors and denylist.',
|
|
236
244
|
claude: '/loop 6h $dependency-triage — patch-only with verifier. Escalate risky bumps.',
|
|
237
245
|
codex: 'Automation 6h: dependency-triage. Patch-only with verifier.',
|
|
246
|
+
opencode: `${OPENCODE_RUN} "Run dependency-triage. Update dependency-sweeper-state.md. Report only — escalate majors."`,
|
|
238
247
|
},
|
|
239
248
|
'post-merge-cleanup': {
|
|
240
249
|
grok: '/loop 1d Run post-merge-scan on recent merges. Update post-merge-state.md. Small fixes only in worktree.',
|
|
241
250
|
claude: '/loop 1d $post-merge-scan — update post-merge-state.md. Small fixes only.',
|
|
242
251
|
codex: 'Automation daily: post-merge-scan → post-merge-state.md.',
|
|
252
|
+
opencode: `${OPENCODE_RUN} "Run post-merge-scan. Update post-merge-state.md. Report only in week one."`,
|
|
243
253
|
},
|
|
244
254
|
'changelog-drafter': {
|
|
245
255
|
grok: '/loop 1d Run changelog-scan on merges since last tag. Produce categorized draft in RELEASE_NOTES_DRAFT.md using draft-release-notes. Update changelog-drafter-state.md. Human review only.',
|
|
246
256
|
claude: '/loop 1d $changelog-scan + draft-release-notes — write RELEASE_NOTES_DRAFT.md and update state. Human approves before publish.',
|
|
247
257
|
codex: 'Automation daily: changelog-scan + draft-release-notes → RELEASE_NOTES_DRAFT.md. Human review.',
|
|
258
|
+
opencode: `${OPENCODE_RUN} "Run changelog-scan. Draft RELEASE_NOTES_DRAFT.md. Human review only — no publish."`,
|
|
248
259
|
},
|
|
249
260
|
'issue-triage': {
|
|
250
261
|
grok: '/loop 2h Run issue-triage. Update issue-triage-state.md. Propose labels and priority only. No auto-apply. Human reviews the needs-human slice.',
|
|
251
262
|
claude: '/loop 2h $issue-triage — update issue-triage-state.md. Suggest labels on allowlisted areas only. Report mode week one.',
|
|
252
263
|
codex: 'Automation 2h: issue-triage → issue-triage-state.md. Propose only.',
|
|
264
|
+
opencode: `${OPENCODE_RUN} "Run issue-triage. Update issue-triage-state.md. Propose labels only — no auto-apply."`,
|
|
253
265
|
},
|
|
254
266
|
};
|
|
255
267
|
return cmds[pattern][tool];
|
|
256
268
|
}
|
|
269
|
+
async function resolveAuditCli() {
|
|
270
|
+
const monorepo = path.resolve(PACKAGE_ROOT, '../loop-audit/dist/cli.js');
|
|
271
|
+
if (await exists(monorepo))
|
|
272
|
+
return monorepo;
|
|
273
|
+
try {
|
|
274
|
+
const { createRequire } = await import('node:module');
|
|
275
|
+
const require = createRequire(import.meta.url);
|
|
276
|
+
const pkg = require.resolve('@cobusgreyling/loop-audit/package.json');
|
|
277
|
+
return path.join(path.dirname(pkg), 'dist/cli.js');
|
|
278
|
+
}
|
|
279
|
+
catch {
|
|
280
|
+
return null;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
async function runAuditJson(cli, targetDir) {
|
|
284
|
+
return new Promise((resolve, reject) => {
|
|
285
|
+
const child = spawn('node', [cli, targetDir, '--json'], {
|
|
286
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
287
|
+
});
|
|
288
|
+
let stdout = '';
|
|
289
|
+
child.stdout.on('data', (chunk) => {
|
|
290
|
+
stdout += chunk.toString();
|
|
291
|
+
});
|
|
292
|
+
child.on('error', reject);
|
|
293
|
+
child.on('close', () => {
|
|
294
|
+
if (stdout.trim())
|
|
295
|
+
resolve(stdout);
|
|
296
|
+
else
|
|
297
|
+
reject(new Error('loop-audit produced no output'));
|
|
298
|
+
});
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
async function runAuditSummary(targetDir) {
|
|
302
|
+
const cli = await resolveAuditCli();
|
|
303
|
+
if (!cli)
|
|
304
|
+
return null;
|
|
305
|
+
try {
|
|
306
|
+
const stdout = await runAuditJson(cli, targetDir);
|
|
307
|
+
return JSON.parse(stdout);
|
|
308
|
+
}
|
|
309
|
+
catch {
|
|
310
|
+
return null;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
function formatScoreBar(score, width = 20) {
|
|
314
|
+
const filled = Math.max(0, Math.min(width, Math.round((score / 100) * width)));
|
|
315
|
+
return `${'█'.repeat(filled)}${'░'.repeat(width - filled)} ${score}/100`;
|
|
316
|
+
}
|
|
317
|
+
function auditTargetArg(target, targetDir) {
|
|
318
|
+
return target === '.' ? '.' : targetDir;
|
|
319
|
+
}
|
|
257
320
|
async function main() {
|
|
258
321
|
const args = parseArgs(process.argv.slice(2));
|
|
259
322
|
if (args.help) {
|
|
260
323
|
console.log(`loop-init — scaffold loop engineering starters
|
|
261
324
|
|
|
262
325
|
Usage:
|
|
263
|
-
loop-init [target-dir] --pattern <name> --tool <grok|claude|codex>
|
|
326
|
+
loop-init [target-dir] --pattern <name> --tool <grok|claude|codex|opencode>
|
|
264
327
|
|
|
265
328
|
Patterns:
|
|
266
329
|
daily-triage (default)
|
|
@@ -280,6 +343,7 @@ Options:
|
|
|
280
343
|
Examples:
|
|
281
344
|
npx @cobusgreyling/loop-init . --pattern daily-triage --tool grok
|
|
282
345
|
npx @cobusgreyling/loop-init . -p pr-babysitter -t claude
|
|
346
|
+
npx @cobusgreyling/loop-init . -p daily-triage -t opencode
|
|
283
347
|
`);
|
|
284
348
|
process.exit(0);
|
|
285
349
|
}
|
|
@@ -297,7 +361,7 @@ Examples:
|
|
|
297
361
|
const targetDir = path.resolve(target);
|
|
298
362
|
const baseStarter = PATTERN_STARTERS[pattern];
|
|
299
363
|
const suffix = TOOL_SUFFIX[tool];
|
|
300
|
-
const starterName =
|
|
364
|
+
const starterName = `${baseStarter}${suffix}`;
|
|
301
365
|
const startersRoot = await resolveBundledOrMonorepo('starters');
|
|
302
366
|
const templatesRoot = await resolveBundledOrMonorepo('templates');
|
|
303
367
|
const starterRoot = path.join(startersRoot, starterName);
|
|
@@ -313,33 +377,52 @@ Examples:
|
|
|
313
377
|
? starterRoot
|
|
314
378
|
: path.join(startersRoot, baseStarter);
|
|
315
379
|
console.log(`\nloop-init: ${pattern} → ${targetDir} (${tool})${dryRun ? ' [dry-run]' : ''}\n`);
|
|
316
|
-
|
|
317
|
-
path.join(effectiveStarter, '
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
const
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
await copyDir(path.join(skillsDir, entry), path.join(targetDir, toolPrefix, entry), dryRun);
|
|
380
|
+
if (tool === 'opencode') {
|
|
381
|
+
const skillsDir = path.join(effectiveStarter, 'skills');
|
|
382
|
+
if (await exists(skillsDir)) {
|
|
383
|
+
const entries = await readDirNames(skillsDir);
|
|
384
|
+
for (const entry of entries) {
|
|
385
|
+
await copyDir(path.join(skillsDir, entry), path.join(targetDir, 'skills', entry), dryRun);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
const agentsMd = path.join(effectiveStarter, 'AGENTS.md');
|
|
389
|
+
if (await exists(agentsMd)) {
|
|
390
|
+
await copyFile(agentsMd, path.join(targetDir, 'AGENTS.md'), dryRun);
|
|
391
|
+
}
|
|
392
|
+
const opencodeJson = path.join(effectiveStarter, 'opencode.json.example');
|
|
393
|
+
if (await exists(opencodeJson)) {
|
|
394
|
+
await copyFile(opencodeJson, path.join(targetDir, 'opencode.json'), dryRun);
|
|
332
395
|
}
|
|
333
396
|
}
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
397
|
+
else {
|
|
398
|
+
const skillRoots = [
|
|
399
|
+
path.join(effectiveStarter, '.grok', 'skills'),
|
|
400
|
+
path.join(effectiveStarter, '.claude', 'skills'),
|
|
401
|
+
path.join(effectiveStarter, '.codex', 'skills'),
|
|
402
|
+
];
|
|
403
|
+
for (const skillsDir of skillRoots) {
|
|
404
|
+
if (!(await exists(skillsDir)))
|
|
405
|
+
continue;
|
|
406
|
+
const toolPrefix = skillsDir.includes('.grok')
|
|
407
|
+
? '.grok/skills'
|
|
408
|
+
: skillsDir.includes('.claude')
|
|
409
|
+
? '.claude/skills'
|
|
410
|
+
: '.codex/skills';
|
|
411
|
+
const entries = await readDirNames(skillsDir);
|
|
341
412
|
for (const entry of entries) {
|
|
342
|
-
await
|
|
413
|
+
await copyDir(path.join(skillsDir, entry), path.join(targetDir, toolPrefix, entry), dryRun);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
const agentFiles = [
|
|
417
|
+
{ src: path.join(effectiveStarter, '.claude', 'agents'), dest: path.join(targetDir, '.claude', 'agents') },
|
|
418
|
+
{ src: path.join(effectiveStarter, '.codex', 'agents'), dest: path.join(targetDir, '.codex', 'agents') },
|
|
419
|
+
];
|
|
420
|
+
for (const { src, dest } of agentFiles) {
|
|
421
|
+
if (await exists(src)) {
|
|
422
|
+
const entries = await readDirNames(src);
|
|
423
|
+
for (const entry of entries) {
|
|
424
|
+
await copyFile(path.join(src, entry), path.join(dest, entry), dryRun);
|
|
425
|
+
}
|
|
343
426
|
}
|
|
344
427
|
}
|
|
345
428
|
}
|
|
@@ -361,7 +444,7 @@ Examples:
|
|
|
361
444
|
await copyL2Templates(pattern, tool, targetDir, templatesRoot, dryRun);
|
|
362
445
|
await scaffoldObservability(pattern, tool, targetDir, templatesRoot, dryRun);
|
|
363
446
|
await scaffoldConstraints(targetDir, templatesRoot, tool, dryRun);
|
|
364
|
-
if (!dryRun && !(await exists(path.join(targetDir, 'AGENTS.md')))) {
|
|
447
|
+
if (tool !== 'opencode' && !dryRun && !(await exists(path.join(targetDir, 'AGENTS.md')))) {
|
|
365
448
|
const agentsTemplate = `# AGENTS.md
|
|
366
449
|
|
|
367
450
|
## Test commands
|
|
@@ -375,10 +458,29 @@ npm run lint
|
|
|
375
458
|
await writeFile(path.join(targetDir, 'AGENTS.md'), agentsTemplate);
|
|
376
459
|
console.log(' created: AGENTS.md (template)');
|
|
377
460
|
}
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
461
|
+
const auditArg = auditTargetArg(target, targetDir);
|
|
462
|
+
if (!dryRun) {
|
|
463
|
+
const audit = await runAuditSummary(targetDir);
|
|
464
|
+
if (audit) {
|
|
465
|
+
console.log('');
|
|
466
|
+
console.log(`✓ Loop Ready: ${audit.score}/100 (${audit.level})`);
|
|
467
|
+
console.log(` ${formatScoreBar(audit.score)}`);
|
|
468
|
+
console.log(` ${audit.assessment}`);
|
|
469
|
+
console.log('');
|
|
470
|
+
console.log('Paste badge in README:');
|
|
471
|
+
console.log(` npx @cobusgreyling/loop-audit ${auditArg} --badge`);
|
|
472
|
+
}
|
|
473
|
+
else {
|
|
474
|
+
console.log('\n=== Loop Ready score ===');
|
|
475
|
+
console.log(` npx @cobusgreyling/loop-audit ${auditArg} --suggest`);
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
console.log('');
|
|
479
|
+
console.log(`First loop (${tool}):`);
|
|
480
|
+
console.log(` ${firstLoopCommand(pattern, tool)}`);
|
|
481
|
+
console.log('');
|
|
482
|
+
console.log(`Estimate cost: npx @cobusgreyling/loop-cost --pattern ${pattern} --level L1`);
|
|
483
|
+
console.log('');
|
|
382
484
|
}
|
|
383
485
|
async function readDirNames(dir) {
|
|
384
486
|
const { readdir } = await import('node:fs/promises');
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cobusgreyling/loop-init",
|
|
3
|
-
"version": "1.2
|
|
4
|
-
"description": "Scaffold loop engineering patterns and starters into any project. Supports Grok, Claude Code, Codex and more. npx @cobusgreyling/loop-init . --pattern daily-triage --tool
|
|
3
|
+
"version": "1.3.2",
|
|
4
|
+
"description": "Scaffold loop engineering patterns and starters into any project. Supports Grok, Claude Code, Codex, Opencode and more. npx @cobusgreyling/loop-init . --pattern daily-triage --tool opencode",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"loop-init": "./dist/cli.js"
|
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
"claude-code",
|
|
32
32
|
"grok",
|
|
33
33
|
"codex",
|
|
34
|
+
"opencode",
|
|
34
35
|
"github-actions",
|
|
35
36
|
"patterns"
|
|
36
37
|
],
|
|
@@ -48,6 +49,9 @@
|
|
|
48
49
|
"publishConfig": {
|
|
49
50
|
"access": "public"
|
|
50
51
|
},
|
|
52
|
+
"dependencies": {
|
|
53
|
+
"@cobusgreyling/loop-audit": "^1.5.0"
|
|
54
|
+
},
|
|
51
55
|
"devDependencies": {
|
|
52
56
|
"@types/node": "^26.0.0",
|
|
53
57
|
"typescript": "^6.0.3"
|
package/starters/README.md
CHANGED
|
@@ -14,21 +14,28 @@ npx @cobusgreyling/loop-init . -p pr-babysitter -t claude
|
|
|
14
14
|
| [minimal-loop](./minimal-loop/) | Grok | `.grok/skills/` |
|
|
15
15
|
| [minimal-loop-claude](./minimal-loop-claude/) | Claude Code | `.claude/skills/` + `.claude/agents/` |
|
|
16
16
|
| [minimal-loop-codex](./minimal-loop-codex/) | Codex | `.codex/skills/` + `.codex/agents/` |
|
|
17
|
+
| [minimal-loop-opencode](./minimal-loop-opencode/) | Opencode | `skills/` + `AGENTS.md` |
|
|
17
18
|
|
|
18
19
|
## L2 assisted patterns
|
|
19
20
|
|
|
20
21
|
| Starter | Pattern | Tools | Readiness |
|
|
21
22
|
|---------|---------|-------|-----------|
|
|
22
23
|
| [pr-babysitter](./pr-babysitter/) | PR Babysitter | Grok, Claude, Codex | L2 assisted |
|
|
24
|
+
| [pr-babysitter-opencode](./pr-babysitter-opencode/) | PR Babysitter | Opencode | L1 → L2 |
|
|
23
25
|
| [ci-sweeper](./ci-sweeper/) | CI Sweeper | Grok, Claude, Codex | L2 assisted |
|
|
26
|
+
| [ci-sweeper-opencode](./ci-sweeper-opencode/) | CI Sweeper | Opencode | L2 cautious |
|
|
24
27
|
| [dependency-sweeper](./dependency-sweeper/) | Dependency Sweeper | Grok, Claude, Codex | L2 patch-only |
|
|
28
|
+
| [dependency-sweeper-opencode](./dependency-sweeper-opencode/) | Dependency Sweeper | Opencode | L2 patch-only |
|
|
25
29
|
| [post-merge-cleanup](./post-merge-cleanup/) | Post-Merge Cleanup | Grok, Claude, Codex | L1 → L2 |
|
|
30
|
+
| [post-merge-cleanup-opencode](./post-merge-cleanup-opencode/) | Post-Merge Cleanup | Opencode | L1 → L2 |
|
|
26
31
|
| [changelog-drafter](./changelog-drafter/) | Changelog Drafter | Grok, Claude, Codex | L1 draft → L2 |
|
|
32
|
+
| [changelog-drafter-opencode](./changelog-drafter-opencode/) | Changelog Drafter | Opencode | L1 draft → L2 |
|
|
27
33
|
| [issue-triage](./issue-triage/) | Issue Triage | Grok, Claude, Codex | L1 propose-only |
|
|
34
|
+
| [issue-triage-opencode](./issue-triage-opencode/) | Issue Triage | Opencode | L1 propose-only |
|
|
28
35
|
|
|
29
36
|
After copying:
|
|
30
37
|
|
|
31
38
|
```bash
|
|
32
39
|
npx @cobusgreyling/loop-audit .
|
|
33
40
|
npx @cobusgreyling/loop-audit . --suggest
|
|
34
|
-
```
|
|
41
|
+
```
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# AGENTS.md — Changelog Drafter (Opencode)
|
|
2
|
+
|
|
3
|
+
## Loop Mode
|
|
4
|
+
- L1 report + draft for the first 1-2 weeks.
|
|
5
|
+
- Read `changelog-drafter-state.md` before every run.
|
|
6
|
+
- Human approves every draft before any publish or PR.
|
|
7
|
+
|
|
8
|
+
## Safety
|
|
9
|
+
- Never create tags, GitHub releases, or update CHANGELOG.md without explicit human approval.
|
|
10
|
+
- Breaking changes and security items must be surfaced explicitly.
|
|
11
|
+
- Draft PRs only when L2 is enabled — never auto-merge.
|
|
12
|
+
|
|
13
|
+
## Verification
|
|
14
|
+
- Verifier checks draft completeness: categories, date range, links.
|
|
15
|
+
- Record draft status and human feedback in state.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Loop Configuration — Changelog Drafter (Opencode)
|
|
2
|
+
|
|
3
|
+
## Active Loops
|
|
4
|
+
|
|
5
|
+
| Pattern | Cadence | Status | Command |
|
|
6
|
+
|---------|---------|--------|---------|
|
|
7
|
+
| Changelog Drafter | 1d or on release prep | L1 draft-only | `opencode run "Run changelog-scan"` via cron (after merges settle) |
|
|
8
|
+
|
|
9
|
+
## Human Gates
|
|
10
|
+
- Breaking changes always human review.
|
|
11
|
+
- Security notes / CVEs human must approve wording.
|
|
12
|
+
- No tags, no PRs, no publishes without explicit approval.
|
|
13
|
+
- First 3 releases: human approves full draft.
|
|
14
|
+
|
|
15
|
+
## Budget
|
|
16
|
+
- Max sub-agent spawns per run: 2 (scanner + drafter).
|
|
17
|
+
- Prefer running after merges settle (evening).
|
|
18
|
+
|
|
19
|
+
## Output Convention
|
|
20
|
+
- Draft: `RELEASE_NOTES_DRAFT.md` at repo root.
|
|
21
|
+
- State: `changelog-drafter-state.md`.
|
|
22
|
+
|
|
23
|
+
## Links
|
|
24
|
+
- Pattern: [changelog-drafter](../../patterns/changelog-drafter.md)
|
|
25
|
+
- Example: [examples/opencode/changelog-drafter.md](../../examples/opencode/changelog-drafter.md)
|
|
26
|
+
- Safety: [docs/safety.md](../../docs/safety.md)
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Changelog Drafter — Opencode Starter
|
|
2
|
+
|
|
3
|
+
Clone-and-run scaffold for the **Changelog Drafter** pattern with opencode. Scan merged PRs and commits, produce categorized release notes.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npx @cobusgreyling/loop-init . --pattern changelog-drafter --tool opencode
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Or copy manually:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
cp -r starters/changelog-drafter-opencode/skills .
|
|
15
|
+
cp starters/changelog-drafter-opencode/AGENTS.md .
|
|
16
|
+
cp starters/changelog-drafter-opencode/LOOP.md .
|
|
17
|
+
cp starters/changelog-drafter-opencode/changelog-drafter-state.md.example changelog-drafter-state.md
|
|
18
|
+
cp starters/changelog-drafter-opencode/opencode.json.example opencode.json
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Start (L1 report + draft, week 1):
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
opencode run \
|
|
25
|
+
"Run skills/changelog-scan/SKILL.md. Scan merges since last tag (or last 7 days). Produce categorized draft in RELEASE_NOTES_DRAFT.md. Update state. Do not publish or create PRs." \
|
|
26
|
+
--title "Changelog drafter"
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## What's Included
|
|
30
|
+
|
|
31
|
+
| File | Purpose |
|
|
32
|
+
|------|---------|
|
|
33
|
+
| `changelog-drafter-state.md.example` | State spine (last release, draft status) |
|
|
34
|
+
| `skills/changelog-scan/SKILL.md` | Merge scan and categorization skill |
|
|
35
|
+
| `AGENTS.md` | Always-on safety rules |
|
|
36
|
+
| `LOOP.md` | Cadence, gates, budget |
|
|
37
|
+
| `opencode.json.example` | Named agent definitions |
|
|
38
|
+
|
|
39
|
+
## Next Steps
|
|
40
|
+
|
|
41
|
+
- [patterns/changelog-drafter.md](../../patterns/changelog-drafter.md)
|
|
42
|
+
- [examples/opencode/changelog-drafter.md](../../examples/opencode/changelog-drafter.md)
|
|
43
|
+
- [docs/loop-design-checklist.md](../../docs/loop-design-checklist.md)
|
|
44
|
+
- [stories/changelog-drafter-week-one.md](../../stories/changelog-drafter-week-one.md)
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://opencode.ai/config.json",
|
|
3
|
+
"agent": {
|
|
4
|
+
"changelog-scan": {
|
|
5
|
+
"name": "changelog-scan",
|
|
6
|
+
"description": "Changelog drafting loop. Reads changelog-drafter-state.md, scans merges, and produces categorized release notes.",
|
|
7
|
+
"mode": "primary",
|
|
8
|
+
"prompt": "Read AGENTS.md, LOOP.md, changelog-drafter-state.md, and skills/changelog-scan/SKILL.md. Scan merges since last tag. Produce categorized draft in RELEASE_NOTES_DRAFT.md. Never publish or tag without human approval.",
|
|
9
|
+
"permission": {
|
|
10
|
+
"bash": "ask",
|
|
11
|
+
"edit": "ask"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"implementer": {
|
|
15
|
+
"name": "implementer",
|
|
16
|
+
"description": "L2 implementer for creating changelog PRs.",
|
|
17
|
+
"mode": "subagent",
|
|
18
|
+
"prompt": "Commit the approved release notes to a new branch and open a draft PR. Flag breaking changes and security items explicitly in the PR body. Do not merge.",
|
|
19
|
+
"permission": {
|
|
20
|
+
"bash": "ask",
|
|
21
|
+
"edit": "ask"
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"verifier": {
|
|
25
|
+
"name": "verifier",
|
|
26
|
+
"description": "Checker agent for draft release notes. APPROVE or REJECT only.",
|
|
27
|
+
"mode": "subagent",
|
|
28
|
+
"prompt": "Review the draft notes for completeness, categorization, and correct date range. Flag missing sections. Do not edit. APPROVE or REJECT.",
|
|
29
|
+
"permission": {
|
|
30
|
+
"bash": "ask",
|
|
31
|
+
"edit": "deny"
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: changelog-scan
|
|
3
|
+
description: >
|
|
4
|
+
Scan merged PRs and commits since a given reference, extract
|
|
5
|
+
titles, labels, types, and signals. Produces structured input
|
|
6
|
+
for release notes drafting.
|
|
7
|
+
user_invocable: true
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Changelog Scan Skill
|
|
11
|
+
|
|
12
|
+
You are a changelog drafting agent. Scan merges and produce release notes.
|
|
13
|
+
|
|
14
|
+
## Scan Sources
|
|
15
|
+
|
|
16
|
+
- `git log --merges --oneline <last-tag>..HEAD`
|
|
17
|
+
- PR labels and milestones
|
|
18
|
+
- Commit messages since last tag
|
|
19
|
+
|
|
20
|
+
## Categories
|
|
21
|
+
|
|
22
|
+
- Features
|
|
23
|
+
- Bug fixes
|
|
24
|
+
- Documentation
|
|
25
|
+
- Dependencies
|
|
26
|
+
- Breaking changes
|
|
27
|
+
- Security
|
|
28
|
+
|
|
29
|
+
## Output
|
|
30
|
+
|
|
31
|
+
- Categorized draft in `RELEASE_NOTES_DRAFT.md`
|
|
32
|
+
- Update `changelog-drafter-state.md` with scan window
|
|
33
|
+
|
|
34
|
+
## Rules
|
|
35
|
+
|
|
36
|
+
- Never publish or tag without explicit human approval.
|
|
37
|
+
- Surface breaking changes and security items explicitly.
|
|
38
|
+
- L1: draft only — no PRs, no tags.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# AGENTS.md — CI Sweeper (Opencode)
|
|
2
|
+
|
|
3
|
+
## Loop Mode
|
|
4
|
+
- CI sweeper is L2 from week one (action-oriented — CIs are urgent).
|
|
5
|
+
- Read `ci-sweeper-state.md` before every run.
|
|
6
|
+
- Classify failures before attempting any fix.
|
|
7
|
+
|
|
8
|
+
## Safety
|
|
9
|
+
- Always dispatch into a git worktree per attempt.
|
|
10
|
+
- Infra / security / payments test failures: do not auto-fix; flag and stop.
|
|
11
|
+
- Max 3 fix attempts per item; escalate after.
|
|
12
|
+
- Enforce budget cap from `LOOP.md`.
|
|
13
|
+
|
|
14
|
+
## Verification
|
|
15
|
+
- Verifier must run tests after implementer.
|
|
16
|
+
- Record attempt count in `ci-sweeper-state.md`.
|
|
17
|
+
- No auto-merge without verifier approval.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Loop Configuration — CI Sweeper (Opencode)
|
|
2
|
+
|
|
3
|
+
## Active Loops
|
|
4
|
+
|
|
5
|
+
| Pattern | Cadence | Status | Command |
|
|
6
|
+
|---------|---------|--------|---------|
|
|
7
|
+
| CI Sweeper | 5-15m | L2 cautious | `opencode run "Run ci-triage" --agent loop-triage` via cron/systemd |
|
|
8
|
+
|
|
9
|
+
## Human Gates
|
|
10
|
+
- Infra / security / payments test failures: do not auto-fix.
|
|
11
|
+
- Max 3 fix attempts per item; escalate after.
|
|
12
|
+
- No auto-merge without verifier pass.
|
|
13
|
+
|
|
14
|
+
## Worktrees
|
|
15
|
+
- Always dispatch into a git worktree per attempt.
|
|
16
|
+
- One worktree per fix; discard after REJECT.
|
|
17
|
+
|
|
18
|
+
## Budget
|
|
19
|
+
- Max sub-agent spawns per run: 3.
|
|
20
|
+
- Max auto-PRs per day: 5.
|
|
21
|
+
- Early exit if no failures found.
|
|
22
|
+
|
|
23
|
+
## Links
|
|
24
|
+
- Pattern: [ci-sweeper](../../patterns/ci-sweeper.md)
|
|
25
|
+
- Example: [examples/opencode/ci-sweeper.md](../../examples/opencode/ci-sweeper.md)
|
|
26
|
+
- Safety: [docs/safety.md](../../docs/safety.md)
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# CI Sweeper — Opencode Starter
|
|
2
|
+
|
|
3
|
+
Clone-and-run scaffold for the **CI Sweeper** pattern with opencode. React to failing CI with minimal fixes at short cadences.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npx @cobusgreyling/loop-init . --pattern ci-sweeper --tool opencode
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Or copy manually:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
cp -r starters/ci-sweeper-opencode/skills .
|
|
15
|
+
cp starters/ci-sweeper-opencode/AGENTS.md .
|
|
16
|
+
cp starters/ci-sweeper-opencode/LOOP.md .
|
|
17
|
+
cp starters/ci-sweeper-opencode/ci-sweeper-state.md.example ci-sweeper-state.md
|
|
18
|
+
cp starters/ci-sweeper-opencode/opencode.json.example opencode.json
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Start (L2 cautious — CI sweeper is action-oriented from week one):
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
opencode run \
|
|
25
|
+
"Run skills/ci-triage/SKILL.md. Classify each failing check. For clear single-file regressions, create a worktree and apply a minimal fix with verifier. Update ci-sweeper-state.md. Infra and security failures: escalate." \
|
|
26
|
+
--title "CI sweeper"
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## What's Included
|
|
30
|
+
|
|
31
|
+
| File | Purpose |
|
|
32
|
+
|------|---------|
|
|
33
|
+
| `ci-sweeper-state.md.example` | State spine (in-flight CI failures) |
|
|
34
|
+
| `skills/ci-triage/SKILL.md` | CI-focused triage skill |
|
|
35
|
+
| `AGENTS.md` | Always-on safety rules |
|
|
36
|
+
| `LOOP.md` | Cadence, gates, budget |
|
|
37
|
+
| `opencode.json.example` | Named agent definitions |
|
|
38
|
+
|
|
39
|
+
## Next Steps
|
|
40
|
+
|
|
41
|
+
- [patterns/ci-sweeper.md](../../patterns/ci-sweeper.md)
|
|
42
|
+
- [examples/opencode/ci-sweeper.md](../../examples/opencode/ci-sweeper.md)
|
|
43
|
+
- [docs/loop-design-checklist.md](../../docs/loop-design-checklist.md)
|