clud-bug 0.5.0 → 0.5.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 CHANGED
@@ -28,12 +28,14 @@ The naturalist arrives at your repo, surveys the habitat, and assembles a field
28
28
 
29
29
  1. **Surveys habitat.** Reads `package.json`, `pyproject.toml`, `go.mod`, `Cargo.toml`, etc., to learn what your stack is.
30
30
  2. **Consults [skills.sh](https://skills.sh).** Pulls review skills relevant to your dependencies (e.g. a Next.js project gets Next.js review specimens).
31
- 3. **Pins three baseline specimens** that enforce review discipline regardless of stack:
31
+ 3. **Pins baseline specimens** that enforce review discipline regardless of stack:
32
32
  - `critical-issues-only` — flag bugs, security, perf only. Skip nits.
33
33
  - `evidence-based-review` — every claim must quote the line being criticized.
34
34
  - `respect-existing-conventions` — don't suggest fights with the codebase's patterns.
35
+ - `clud-bug-collaboration` — guidance for any other Claude Code agents working in your repo: how to coexist with bot review threads, how to read the gate, why workflow self-mods break the action, etc.
35
36
  4. **Writes** the chosen specimens to `.claude/skills/<name>/SKILL.md` (Claude Code auto-loads them in the GitHub Action).
36
37
  5. **Drafts the field kit** at `.github/workflows/clud-bug-review.yml` with your project description filled in and the right permissions/tool allowlist for `gh pr comment` to actually post.
38
+ 6. **Briefs other agents** by adding a `<!-- clud-bug-start -->` block to `AGENTS.md` (creating it if missing — it's the cross-tool canonical), and idempotently to `CLAUDE.md`, `GEMINI.md`, `.github/copilot-instructions.md`, `.cursorrules`, `.windsurfrules`, `.clinerules`, `.continuerules`, and `.cursor/rules/*.md` where they already exist. Re-runs replace the prior block in place. Files you didn't already have are left uncreated — no proliferating stubs.
37
39
 
38
40
  ## CLI options
39
41
 
package/bin/clud-bug.js CHANGED
@@ -15,6 +15,7 @@ import {
15
15
  import { computeAuditFileSet, renderAuditHeader } from '../lib/audit.js';
16
16
  import { runUpdate } from '../lib/update.js';
17
17
  import { getPendingWorkflowEdits, makeBranchName, git as gitCmd } from '../lib/edit-workflow.js';
18
+ import { applyToRepo as applyAgentDocs } from '../lib/agents-md.js';
18
19
 
19
20
  const PKG_ROOT = dirname(dirname(fileURLToPath(import.meta.url)));
20
21
  const TEMPLATES = join(PKG_ROOT, 'templates');
@@ -204,9 +205,34 @@ async function runInit(args) {
204
205
  }
205
206
  await writeManifest(skillsDirPath, manifest);
206
207
 
208
+ // Tell other agents what's installed and how to coexist with the bot.
209
+ // Idempotent — re-runs replace the prior block in place. AGENTS.md is the
210
+ // canonical home (cross-tool); CLAUDE.md / GEMINI.md / Cursor / Windsurf
211
+ // / Cline / Continue rules files get the same block appended IF they
212
+ // already exist (we don't proliferate stubs the user didn't ask for).
213
+ log(' briefing other agents (AGENTS.md / CLAUDE.md)...');
214
+ // Pass `=== true` (not `!== false`) so the rendered block matches the
215
+ // workflow's gate predicate exactly. A v0.3 advisory upgrade where
216
+ // strictMode is undefined renders "off" — which is what the workflow
217
+ // actually does on that manifest.
218
+ const agentDocs = await applyAgentDocs(cwd, {
219
+ version: manifest.lastUpdateVersion,
220
+ strictMode: manifest.strictMode === true,
221
+ });
222
+ for (const p of agentDocs.created) log(` created ${p}`);
223
+ for (const p of agentDocs.touched) log(` updated ${p}`);
224
+
207
225
  if (args.commit) {
208
226
  log(' committing...');
209
- spawnSync('git', ['add', '.claude', '.github/workflows/clud-bug-review.yml', '.github/workflows/clud-bug-audit.yml', '.github/workflows/clud-bug-self-update.yml'], { cwd, stdio: 'inherit' });
227
+ const toAdd = [
228
+ '.claude',
229
+ '.github/workflows/clud-bug-review.yml',
230
+ '.github/workflows/clud-bug-audit.yml',
231
+ '.github/workflows/clud-bug-self-update.yml',
232
+ ...agentDocs.created,
233
+ ...agentDocs.touched,
234
+ ];
235
+ spawnSync('git', ['add', ...toAdd], { cwd, stdio: 'inherit' });
210
236
  spawnSync('git', ['commit', '-m', 'Add clud-bug 🐛 — a field guide to specimens crawling your code'], { cwd, stdio: 'inherit' });
211
237
  }
212
238
 
@@ -0,0 +1,183 @@
1
+ import { readFile, writeFile, stat, readdir } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+
4
+ // Manages a clud-bug-owned section inside AGENTS.md / CLAUDE.md and adjacent
5
+ // agent-instruction files. Mirrors the well-established `<!-- logmind-start -->`
6
+ // pattern: a marked block that other agents can read for collaboration rules,
7
+ // idempotently rewritten on each `clud-bug init` so the content stays current.
8
+
9
+ const START_MARKER = '<!-- clud-bug-start -->';
10
+ const END_MARKER = '<!-- clud-bug-end -->';
11
+ const BLOCK_VERSION = 'v1';
12
+
13
+ // Files we'll touch when present, plus files we'll create if missing.
14
+ // AGENTS.md is the cross-tool canonical (logmind made it canonical too).
15
+ // CLAUDE.md, GEMINI.md, .cursorrules, .windsurfrules etc. are tool-specific
16
+ // stubs/instructions; we append the same block where they exist but don't
17
+ // create them (logmind already creates the ones it knows about).
18
+ const ALWAYS_TOUCH = ['AGENTS.md']; // create if missing
19
+ const TOUCH_IF_PRESENT = [
20
+ 'CLAUDE.md',
21
+ 'GEMINI.md',
22
+ '.github/copilot-instructions.md',
23
+ '.cursorrules',
24
+ '.windsurfrules',
25
+ '.clinerules',
26
+ '.continuerules',
27
+ ];
28
+
29
+ // Render the clud-bug block. Bundled here rather than in a template file so
30
+ // updates ship with the CLI itself.
31
+ //
32
+ // `strictMode` MUST match the workflow's gate predicate exactly so the block
33
+ // can't lie about repo state. The workflow at `templates/workflow*.yml.tmpl`
34
+ // reads the manifest with `JSON.parse(s).strictMode === true` — meaning the
35
+ // gate fires ONLY on an explicit `true`, and anything else (missing field,
36
+ // `false`, `null`) is advisory. Mirror that here: render "on" only when the
37
+ // caller explicitly passes `true`. Anything else is "off".
38
+ //
39
+ // Why this matters: a v0.3-era install (no `strictMode` field, `lastUpdate`
40
+ // set) is a documented advisory upgrade path — `clud-bug init` deliberately
41
+ // preserves that state. If the block rendered "on" for that case, other
42
+ // agents reading AGENTS.md would get a wrong model of the gate.
43
+ export function renderBlock({ version, strictMode } = {}) {
44
+ const versionLine = version ? `_Installed at clud-bug v${version}._` : '';
45
+ const strictNote = strictMode === true
46
+ ? 'Strict mode is **on** in this repo (workflow check fails on critical findings).'
47
+ : 'Strict mode is **off** in this repo (advisory only).';
48
+ return `${START_MARKER}
49
+ <!-- clud-bug-block-version: ${BLOCK_VERSION} -->
50
+ ## clud-bug — Claude PR review
51
+
52
+ This repository uses [clud-bug](https://cludbug.dev) to review pull requests
53
+ automatically. Three things matter when other agents (or future-you) work
54
+ in this repo:
55
+
56
+ ### When you push fixes addressing prior Clud Bug review threads
57
+
58
+ The bot resolves its own prior review threads on the next pass when it can
59
+ verify the fix in the diff. You don't need to manually resolve threads it
60
+ opened — push the fix, wait ~2 minutes, and check the PR. If a thread it
61
+ left isn't auto-resolved after a fix, the bot judged the issue still open;
62
+ read its latest review comment for what it's still flagging.
63
+
64
+ ### Strict mode
65
+
66
+ ${strictNote} Toggle by editing \`.claude/skills/.clud-bug.json\`:
67
+
68
+ \`\`\`json
69
+ { "strictMode": true | false, ... }
70
+ \`\`\`
71
+
72
+ The setting is read from the **base ref** of any open PR, so PRs cannot
73
+ disable strict mode on themselves. Changes take effect on PRs opened after
74
+ they merge to the base branch.
75
+
76
+ ### Where the skills live
77
+
78
+ Project-aware review rules live in \`.claude/skills/<name>/SKILL.md\`. A
79
+ small baseline kit ships with every install — see
80
+ \`.claude/skills/.clud-bug.json\` for the current set. Add more via
81
+ \`clud-bug add <source/name>\` (from skills.sh) or by dropping your own
82
+ \`.md\` files there. They auto-load into the reviewer.
83
+
84
+ ### Editing the workflow
85
+
86
+ Anthropic's \`claude-code-action\` refuses to run on PRs that modify its own
87
+ workflow file. Use \`clud-bug edit-workflow\` to bundle workflow tweaks into
88
+ their own isolated PR — see [README](https://github.com/thrillmot/clud-bug#when-you-edit-the-workflow).
89
+
90
+ ${versionLine}
91
+ ${END_MARKER}`;
92
+ }
93
+
94
+ // Replace an existing clud-bug block in `content`, OR append if absent.
95
+ // Idempotent: running multiple times leaves a single block.
96
+ export function upsertBlock(content, block) {
97
+ const startRe = new RegExp(escapeRe(START_MARKER));
98
+ const endRe = new RegExp(escapeRe(END_MARKER));
99
+ if (startRe.test(content) && endRe.test(content)) {
100
+ // Replace from START_MARKER through END_MARKER (greedy multi-line).
101
+ const re = new RegExp(`${escapeRe(START_MARKER)}[\\s\\S]*?${escapeRe(END_MARKER)}`);
102
+ return content.replace(re, block);
103
+ }
104
+ // Append with a separating blank line, no trailing newline duplication.
105
+ const sep = content.endsWith('\n') ? '\n' : '\n\n';
106
+ return `${content}${sep}${block}\n`;
107
+ }
108
+
109
+ function escapeRe(s) {
110
+ return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
111
+ }
112
+
113
+ // Touches all relevant agent-instruction files in `cwd`.
114
+ // Creates AGENTS.md if it doesn't exist (it's the canonical home).
115
+ // Updates other files only if they already exist (don't proliferate stubs;
116
+ // logmind or the user owns those creation decisions).
117
+ //
118
+ // Returns { touched: string[], created: string[] } for the caller to log.
119
+ export async function applyToRepo(cwd, blockOpts = {}) {
120
+ const block = renderBlock(blockOpts);
121
+ const touched = [];
122
+ const created = [];
123
+
124
+ for (const path of ALWAYS_TOUCH) {
125
+ const full = join(cwd, path);
126
+ const existed = await fileExists(full);
127
+ const prior = existed ? await readFile(full, 'utf8') : seedFile(path);
128
+ const next = upsertBlock(prior, block);
129
+ if (next !== prior) {
130
+ await writeFile(full, next);
131
+ (existed ? touched : created).push(path);
132
+ }
133
+ }
134
+
135
+ for (const path of TOUCH_IF_PRESENT) {
136
+ const full = join(cwd, path);
137
+ if (!(await fileExists(full))) continue;
138
+ const prior = await readFile(full, 'utf8');
139
+ const next = upsertBlock(prior, block);
140
+ if (next !== prior) {
141
+ await writeFile(full, next);
142
+ touched.push(path);
143
+ }
144
+ }
145
+
146
+ // .cursor/rules/*.md — append to every file that exists.
147
+ const cursorRulesDir = join(cwd, '.cursor', 'rules');
148
+ if (await fileExists(cursorRulesDir)) {
149
+ let entries = [];
150
+ try { entries = await readdir(cursorRulesDir); } catch {}
151
+ for (const name of entries) {
152
+ if (!name.endsWith('.md')) continue;
153
+ const full = join(cursorRulesDir, name);
154
+ const prior = await readFile(full, 'utf8');
155
+ const next = upsertBlock(prior, block);
156
+ if (next !== prior) {
157
+ await writeFile(full, next);
158
+ touched.push(`.cursor/rules/${name}`);
159
+ }
160
+ }
161
+ }
162
+
163
+ return { touched, created };
164
+ }
165
+
166
+ function seedFile(name) {
167
+ // When AGENTS.md doesn't exist (no logmind, no prior tooling), seed with a
168
+ // minimal canonical header so the clud-bug block has context.
169
+ if (name === 'AGENTS.md') {
170
+ return `# AGENTS.md
171
+
172
+ This file is the canonical instruction file for AI coding agents working in
173
+ this repository. Tools that understand AGENTS.md (Cursor, Codex, Windsurf,
174
+ Claude Code, Cline, Continue, Aider, ...) read it directly.
175
+
176
+ `;
177
+ }
178
+ return '';
179
+ }
180
+
181
+ async function fileExists(path) {
182
+ try { await stat(path); return true; } catch { return false; }
183
+ }
package/lib/skills.js CHANGED
@@ -15,7 +15,7 @@ const MANIFEST_VERSION = 1;
15
15
  // skill content, bump BASELINE_SKILLS_REF below in the same clud-bug PR
16
16
  // that ships the corresponding bundled fallback update.
17
17
  // See thrillmot/agent-skills — skills.sh `skills/<name>/SKILL.md` layout.
18
- const BASELINE_SKILLS_REF = '977e439ec861860351239ed89dd56edcd48cbf6b';
18
+ const BASELINE_SKILLS_REF = 'a44559770686e6c51d08ba5bb842d78f85876fb2';
19
19
  const AGENT_SKILLS_BASE = process.env.CLUD_BUG_AGENT_SKILLS_BASE
20
20
  ?? `https://raw.githubusercontent.com/thrillmot/agent-skills/${BASELINE_SKILLS_REF}/skills`;
21
21
  const SKILL_FETCH_TIMEOUT_MS = 5000;
package/lib/update.js CHANGED
@@ -3,6 +3,7 @@ import { join, dirname } from 'node:path';
3
3
  import { renderFile, pickTemplate } from './render.js';
4
4
  import { detect, buildDescriptionLine } from './detect.js';
5
5
  import { loadBaseline, readManifest, writeManifest } from './skills.js';
6
+ import { applyToRepo as applyAgentDocs } from './agents-md.js';
6
7
 
7
8
  // Re-render the user's workflow + refresh baseline skills using the
8
9
  // templates / baseline shipped with the currently-installed clud-bug.
@@ -67,7 +68,22 @@ export async function runUpdate({
67
68
  // `clud-bug refresh`. We just emit an advisory.
68
69
  }
69
70
 
70
- // 5. Stamp the manifest with the version that ran the update.
71
+ // 5. Refresh the AGENTS.md / CLAUDE.md clud-bug block. The block embeds
72
+ // the version + strict-mode state, so an update with a new version
73
+ // rewrites it. Files that don't already exist (other than AGENTS.md)
74
+ // are left alone, so this never silently creates instruction stubs.
75
+ // `=== true` mirrors the workflow's gate predicate at
76
+ // templates/workflow*.yml.tmpl. A v0.3 advisory manifest (strictMode
77
+ // undefined, lastUpdate set) renders "off" — matching the gate, not the
78
+ // default-on behavior of fresh v0.4+ installs.
79
+ const agentDocs = await applyAgentDocs(cwd, {
80
+ version: ourVersion,
81
+ strictMode: manifest.strictMode === true,
82
+ });
83
+ for (const p of agentDocs.created) changed.push({ path: join(cwd, p), label: `agent docs: created ${p}` });
84
+ for (const p of agentDocs.touched) changed.push({ path: join(cwd, p), label: `agent docs: ${p}` });
85
+
86
+ // 6. Stamp the manifest with the version that ran the update.
71
87
  manifest.lastUpdate = new Date().toISOString();
72
88
  manifest.lastUpdateVersion = ourVersion;
73
89
  await writeManifest(skillsDir, manifest);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clud-bug",
3
- "version": "0.5.0",
3
+ "version": "0.5.2",
4
4
  "description": "Claude PR review with project-aware skills. CLI installs a working GitHub Actions workflow and curates skills from skills.sh.",
5
5
  "homepage": "https://cludbug.dev",
6
6
  "bugs": "https://github.com/thrillmot/clud-bug/issues",
@@ -0,0 +1,124 @@
1
+ ---
2
+ name: clud-bug-collaboration
3
+ description: How Claude Code agents working in a clud-bug-installed repo should interact with the bot's review threads, strict-mode gate, and skill set. Use this skill whenever you're about to push a commit, address a clud-bug PR review comment, edit anything under .claude/skills/, modify .github/workflows/clud-bug-*.yml, or wonder why a PR check is red. Also use when planning work in a repo that has a `clud-bug-review` workflow installed — even if the user didn't mention clud-bug by name.
4
+ ---
5
+
6
+ # Working in a clud-bug-installed repo
7
+
8
+ Clud Bug reviews every PR via `anthropics/claude-code-action`. As another
9
+ Claude Code agent working alongside it, here's what to know — these aren't
10
+ arbitrary rules, they're the consequences of how the gate is wired.
11
+
12
+ ## When you push fixes to a clud-bug-reviewed PR
13
+
14
+ The bot reviews on `pull_request: synchronize` (every push). When you push
15
+ a commit that addresses an issue Clud Bug flagged in a prior review, the bot
16
+ will re-review the PR within ~2 minutes and **resolve its own prior
17
+ unresolved inline review threads** where the flagged issue is verifiably
18
+ fixed in the current diff. You don't have to resolve threads manually.
19
+
20
+ If a thread it left isn't auto-resolved after your fix:
21
+ - The bot judged the issue still open (read its latest comment for why).
22
+ - Or the resolution call hit a transient API issue (re-push to retry).
23
+
24
+ Don't manually resolve clud-bug threads on its behalf. The check
25
+ (`required_conversation_resolution` branch protection) will block merge if
26
+ unresolved threads remain — and the right fix is usually "fix the actual
27
+ issue and re-push," not "mark the conversation resolved."
28
+
29
+ ## When you read the PR's `clud-bug-review` check status
30
+
31
+ - **Green** = Clud Bug ran and either found no critical issues OR strict
32
+ mode is off (advisory).
33
+ - **Red** = either the action errored OR strict mode is on AND Clud Bug
34
+ flagged a critical issue. Read the latest `## 🐛 Clud Bug review` comment
35
+ on the PR — the body indicates "critical findings" or "clean."
36
+ - **Skipped/green-with-comment** = bot- or fork-authored PR (Dependabot,
37
+ Renovate, fork contributor). GitHub deliberately doesn't pass repo
38
+ secrets to those workflows. The bot posts a one-line "Clud Bug skipped"
39
+ comment and exits 0. Review the diff manually.
40
+
41
+ ## Strict mode
42
+
43
+ Read `.claude/skills/.clud-bug.json` to check this repo's setting.
44
+ `strictMode: true` means the workflow check fails on critical findings.
45
+ The setting is read from the **base ref**, so a PR cannot disable strict
46
+ mode on itself by editing the manifest. Changes take effect for PRs opened
47
+ after the change merges to the base branch.
48
+
49
+ To disable strict mode for a repo, edit the manifest on the base branch:
50
+
51
+ ```json
52
+ { "strictMode": false, ... }
53
+ ```
54
+
55
+ ## When you modify a clud-bug skill
56
+
57
+ Skills live in `.claude/skills/<slug>/SKILL.md`. Three groups:
58
+
59
+ - **Baseline** (`critical-issues-only`, `evidence-based-review`,
60
+ `respect-existing-conventions`) — managed by clud-bug; if you edit them
61
+ in-place they'll be overwritten on the next `clud-bug update`. To
62
+ customize behavior for this repo, write a NEW skill rather than mutating
63
+ a baseline.
64
+ - **From skills.sh** — installed via `clud-bug add <source/name>`. Tracked
65
+ in `.claude/skills/.clud-bug.json`. Use `clud-bug refresh` to sync.
66
+ - **Custom** (anything not in the manifest) — yours, never touched by any
67
+ clud-bug command. Drop a new `.md` here and it auto-loads on the next PR.
68
+
69
+ When you write a custom skill, follow the SKILL.md frontmatter format
70
+ (`name`, `description`) and write specific, evidence-anchored guidance.
71
+ Generic advice gets ignored; rules with examples and quoted-line evidence
72
+ move the bot's behavior.
73
+
74
+ ## When you edit `.github/workflows/clud-bug-*.yml`
75
+
76
+ `anthropics/claude-code-action` **refuses to run on PRs that modify its
77
+ own workflow file** (App token exchange fails with 401, "Workflow
78
+ validation failed"). This is a security guard against PRs that try to
79
+ neuter the reviewer or exfiltrate secrets.
80
+
81
+ Consequence: if you bundle a workflow tweak with other work, the
82
+ `clud-bug-review` check on that PR will fail and not actually review your
83
+ other changes either.
84
+
85
+ The fix: split workflow edits into their own PR. The CLI helps:
86
+
87
+ ```bash
88
+ # After editing .github/workflows/clud-bug-*.yml locally:
89
+ clud-bug edit-workflow
90
+ ```
91
+
92
+ It refuses to run if your working tree has non-workflow changes, so the
93
+ isolation guarantee is enforced. Branches from `origin/main`, not HEAD —
94
+ unrelated commits on a feature branch can't leak in.
95
+
96
+ ## When the secret is missing
97
+
98
+ `ANTHROPIC_API_KEY` must be set in the repo's Actions secrets. Without it,
99
+ the workflow's guard step fails loudly with an `::error::` annotation
100
+ explaining how to set it. (For bot/fork PRs where the secret legitimately
101
+ isn't passed, the guard posts a one-line advisory comment and exits 0
102
+ instead of failing red.)
103
+
104
+ ## Updating clud-bug itself
105
+
106
+ `clud-bug-self-update.yml` runs weekly (Mondays 12:00 UTC) and opens a PR
107
+ when a newer clud-bug version is published to npm. Pin to a specific
108
+ version by adding `pinVersion: "x.y.z"` to `.claude/skills/.clud-bug.json`.
109
+
110
+ To trigger an update on demand:
111
+
112
+ ```bash
113
+ clud-bug update
114
+ ```
115
+
116
+ Re-renders workflow templates and refreshes baseline skills from the
117
+ currently-installed clud-bug version. Custom and skills.sh-installed
118
+ skills are left untouched.
119
+
120
+ ## Where to find more
121
+
122
+ - Site: https://cludbug.dev
123
+ - Repo: https://github.com/thrillmot/clud-bug
124
+ - Skill catalog: https://github.com/thrillmot/agent-skills