clud-bug 0.7.0-rc.3 → 0.7.0-rc.4

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.
@@ -0,0 +1,37 @@
1
+ {
2
+ "$comment": "Canonical branch protection ruleset for SkDD-conformant repos. Applied by `logmind init --configure-github`, `clud-bug init --configure-github`, and `reporulez`. Implements thrillmade/protocol SPEC §7. Schema-locked at v1; major bumps require coordinated tool releases.",
3
+ "version": "v1",
4
+ "spec_version": "0.1.2",
5
+ "branch_protection": {
6
+ "required_status_checks": {
7
+ "strict": true,
8
+ "contexts": [
9
+ "clud-bug-review",
10
+ "check-decisions",
11
+ "check-derived-docs",
12
+ "check-links",
13
+ "test"
14
+ ]
15
+ },
16
+ "required_pull_request_reviews": {
17
+ "required_approving_review_count": 1,
18
+ "dismiss_stale_reviews": false,
19
+ "require_code_owner_reviews": false
20
+ },
21
+ "required_conversation_resolution": true,
22
+ "enforce_admins": false,
23
+ "allow_force_pushes": false,
24
+ "allow_deletions": false,
25
+ "required_linear_history": false,
26
+ "allow_auto_merge": true,
27
+ "delete_branch_on_merge": true,
28
+ "squash_merge_commit_title": "PR_TITLE",
29
+ "squash_merge_commit_message": "PR_BODY"
30
+ },
31
+ "$notes": {
32
+ "required_approving_review_count": "1 — auto-satisfied by clud-bug[bot]'s APPROVE vote on clean reviews for org-member PRs (per SPEC §7.2.1). External-contributor PRs still need a human reviewer because the bot's APPROVE on first-time contributors is policy-gated. Repo-owners MAY raise above 1; tools MUST NOT lower below 1.",
33
+ "enforce_admins": "false so maintainers can land hotfixes outside the gate; the dogfood discipline expects this only for genuine emergencies (clud-bug review still runs but doesn't block)",
34
+ "contexts": "the four logmind workflow check names + clud-bug-review + a generic 'tests' context. Repos may add more, but MUST keep these. If a context name is renamed in workflow YAML, update the ruleset.",
35
+ "required_conversation_resolution": "true is the dogfood-defining rule: clud-bug findings must be resolved before merge"
36
+ }
37
+ }
@@ -0,0 +1,39 @@
1
+ import { loadCanonicalV1, type OctokitLike } from '../core/configure-github.js';
2
+ export interface RunConfigureGithubOptions {
3
+ /** "owner/repo" target — required. */
4
+ target?: string | null;
5
+ /** Target branch (default `main`). */
6
+ branch?: string;
7
+ /** Render diff only; skip PATCH calls. */
8
+ dryRun?: boolean;
9
+ /** Suppress progress chatter; emit only the final `ok` summary. */
10
+ quiet?: boolean;
11
+ /** Resolver for the GitHub token (tests pass a stub). */
12
+ resolveToken?: () => Promise<string | null>;
13
+ /** Octokit factory (tests inject a fake; defaults to the gh-adapter). */
14
+ octokitFactory?: (token: string) => OctokitLike;
15
+ /** Stdout writer (tests capture). */
16
+ stdout?: (msg: string) => void;
17
+ /** Stderr writer (tests capture). */
18
+ stderr?: (msg: string) => void;
19
+ }
20
+ /**
21
+ * Entry point — wired into `clud-bug.js` dispatch. Returns a Node-style
22
+ * exit code so the dispatcher can `process.exit()` deterministically. We
23
+ * don't call `process.exit` ourselves so tests can drive the function
24
+ * without taking down the harness.
25
+ */
26
+ export declare function runConfigureGithub(options: RunConfigureGithubOptions): Promise<number>;
27
+ /**
28
+ * Builds an `OctokitLike` instance backed by `gh api` shell-outs. This
29
+ * lets us satisfy the structural interface without pulling in
30
+ * `@octokit/rest` as a runtime dep (~200KB). The App passes its real
31
+ * Octokit instance instead.
32
+ */
33
+ export declare function ghCliOctokit(token: string): OctokitLike;
34
+ /**
35
+ * Re-exported so callers can preload the ruleset (e.g. for a `--show-ruleset`
36
+ * flag in future). Currently exists for parity with the App's pattern.
37
+ */
38
+ export { loadCanonicalV1 };
39
+ //# sourceMappingURL=configure-github.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"configure-github.d.ts","sourceRoot":"","sources":["../../src/cli/configure-github.ts"],"names":[],"mappings":"AAwBA,OAAO,EAEL,eAAe,EACf,KAAK,WAAW,EAEjB,MAAM,6BAA6B,CAAC;AAErC,MAAM,WAAW,yBAAyB;IACxC,sCAAsC;IACtC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,sCAAsC;IACtC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,0CAA0C;IAC1C,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,mEAAmE;IACnE,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,yDAAyD;IACzD,YAAY,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC5C,yEAAyE;IACzE,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,WAAW,CAAC;IAChD,qCAAqC;IACrC,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,qCAAqC;IACrC,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAChC;AAuBD;;;;;GAKG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,yBAAyB,GACjC,OAAO,CAAC,MAAM,CAAC,CA2FjB;AAkBD;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,WAAW,CAyFvD;AAOD;;;GAGG;AACH,OAAO,EAAE,eAAe,EAAE,CAAC"}
@@ -0,0 +1,222 @@
1
+ // `clud-bug configure-github` CLI command — applies the SPEC §7 canonical
2
+ // branch protection ruleset to a target repo.
3
+ //
4
+ // External users installing the App expect "best practice branch protection"
5
+ // applied automatically. This command lets them opt in offline + dry-run
6
+ // safely BEFORE the App's auto-setup runs server-side. Same core logic
7
+ // powers both paths (the App imports `applyCanonicalRuleset` from
8
+ // clud-bug/core; this CLI passes a `gh api`-wrapping adapter so users
9
+ // don't need `@octokit/rest` on their machine).
10
+ //
11
+ // Usage:
12
+ //
13
+ // clud-bug configure-github <owner>/<repo>
14
+ // clud-bug configure-github <owner>/<repo> --dry-run
15
+ // clud-bug configure-github <owner>/<repo> --branch develop
16
+ //
17
+ // Auth ladder:
18
+ //
19
+ // 1. `GITHUB_TOKEN` env var (CI-friendly, no `gh` install required)
20
+ // 2. `gh auth token` (developer workstation default)
21
+ //
22
+ // If neither produces a token, exits 1 with a recovery message.
23
+ import { spawn, spawnSync } from 'node:child_process';
24
+ import { applyCanonicalRuleset, loadCanonicalV1, } from '../core/configure-github.js';
25
+ const HELP = `clud-bug configure-github — apply SPEC §7 canonical branch protection.
26
+
27
+ Usage:
28
+ clud-bug configure-github <owner>/<repo> [options]
29
+
30
+ Options:
31
+ --dry-run Compute diff and print it, but do NOT call PATCH endpoints.
32
+ --branch <name> Target branch (default: main).
33
+ --quiet,-q Suppress progress chatter; emit only the final summary.
34
+ --help,-h Show this help.
35
+
36
+ Auth (resolved in order):
37
+ 1. GITHUB_TOKEN env var (CI-friendly)
38
+ 2. \`gh auth token\` (developer workstation)
39
+
40
+ Exit codes:
41
+ 0 success or already-canonical
42
+ 1 auth missing, PATCH error, or unrecoverable transport failure
43
+ 2 CLI usage error (missing target, bad flag)
44
+ `;
45
+ /**
46
+ * Entry point — wired into `clud-bug.js` dispatch. Returns a Node-style
47
+ * exit code so the dispatcher can `process.exit()` deterministically. We
48
+ * don't call `process.exit` ourselves so tests can drive the function
49
+ * without taking down the harness.
50
+ */
51
+ export async function runConfigureGithub(options) {
52
+ const { target, branch = 'main', dryRun = false, quiet = false, resolveToken = defaultResolveToken, octokitFactory = ghCliOctokit, stdout = (msg) => process.stdout.write(msg), stderr = (msg) => process.stderr.write(msg), } = options;
53
+ if (!target) {
54
+ stderr(HELP);
55
+ return 2;
56
+ }
57
+ const match = /^([^/]+)\/([^/]+)$/.exec(target);
58
+ if (!match) {
59
+ stderr(`clud-bug configure-github: target must be in owner/repo form, got "${target}".\n`);
60
+ return 2;
61
+ }
62
+ const [, owner, repo] = match;
63
+ const token = await resolveToken();
64
+ if (!token) {
65
+ stderr('clud-bug configure-github: no GitHub token found.\n' +
66
+ ' Set GITHUB_TOKEN, or install + auth gh: brew install gh && gh auth login\n');
67
+ return 1;
68
+ }
69
+ if (!quiet) {
70
+ stdout(`\u{1F41B} configure-github: applying canonical-v1 ruleset to ${owner}/${repo} (branch=${branch})\n`);
71
+ }
72
+ // Single-call orchestration: the CLI previously called
73
+ // applyCanonicalRuleset twice in apply mode (dry-run first to display the
74
+ // planned changes, then a real call to apply). That two-read pattern
75
+ // opened a TOCTOU window — concurrent changes between the two reads
76
+ // could make the displayed list diverge from the actually-applied list,
77
+ // and the dry-run cost a redundant API round-trip every time. Drop the
78
+ // preview-first read: pass `dryRun` straight through. The function's
79
+ // idempotency contract (returns `alreadyCanonical: true` + empty changes
80
+ // on a no-op) means we don't need a separate "look before you leap"
81
+ // call. Users who want preview semantics run `--dry-run` first as a
82
+ // distinct invocation — idiomatic CLI behavior. Surfaced by PR #166
83
+ // reviewer (CTO follow-up 2026-06-17).
84
+ const octokit = octokitFactory(token);
85
+ let result;
86
+ try {
87
+ result = await applyCanonicalRuleset(octokit, {
88
+ owner,
89
+ repo,
90
+ branch,
91
+ dryRun,
92
+ });
93
+ }
94
+ catch (err) {
95
+ stderr(`clud-bug configure-github: ${dryRun ? 'failed to read current state' : 'PATCH failed'}: ${stringifyError(err)}\n`);
96
+ return 1;
97
+ }
98
+ if (result.alreadyCanonical) {
99
+ if (!quiet)
100
+ stdout(' No changes — repo already matches canonical-v1.\n');
101
+ stdout(`ok configure-github: ${owner}/${repo} already canonical-v1\n`);
102
+ return 0;
103
+ }
104
+ if (!quiet) {
105
+ const verb = dryRun ? 'Planned' : 'Applied';
106
+ stdout(` ${verb} changes (${result.changes.length}):\n`);
107
+ for (const c of result.changes)
108
+ stdout(` - ${c}\n`);
109
+ }
110
+ if (dryRun) {
111
+ stdout(`ok configure-github: dry-run on ${owner}/${repo} — ${result.changes.length} change${result.changes.length === 1 ? '' : 's'} pending\n`);
112
+ return 0;
113
+ }
114
+ stdout(`ok configure-github: ${owner}/${repo} converged to canonical-v1 (${result.changes.length} change${result.changes.length === 1 ? '' : 's'})\n`);
115
+ return 0;
116
+ }
117
+ /**
118
+ * Auth ladder: env var first, then `gh auth token`. Surfaces a token
119
+ * string on success or `null` if neither source has one (caller prints
120
+ * the recovery hint).
121
+ */
122
+ async function defaultResolveToken() {
123
+ const fromEnv = process.env.GITHUB_TOKEN?.trim();
124
+ if (fromEnv)
125
+ return fromEnv;
126
+ const result = spawnSync('gh', ['auth', 'token'], { encoding: 'utf8' });
127
+ if (result.status === 0) {
128
+ const tok = result.stdout.trim();
129
+ if (tok)
130
+ return tok;
131
+ }
132
+ return null;
133
+ }
134
+ /**
135
+ * Builds an `OctokitLike` instance backed by `gh api` shell-outs. This
136
+ * lets us satisfy the structural interface without pulling in
137
+ * `@octokit/rest` as a runtime dep (~200KB). The App passes its real
138
+ * Octokit instance instead.
139
+ */
140
+ export function ghCliOctokit(token) {
141
+ // We pass GITHUB_TOKEN through the spawn env so gh's API surface picks
142
+ // it up consistently whether the user supplied env or gh auth.
143
+ const env = { ...process.env, GITHUB_TOKEN: token };
144
+ function ghApi(method, path, body) {
145
+ return new Promise((resolve, reject) => {
146
+ const args = ['api', '-X', method];
147
+ if (body !== undefined) {
148
+ args.push('--input', '-');
149
+ }
150
+ args.push(path);
151
+ // Add an Accept header so 404s return the structured error, not a
152
+ // friendlier shell hint. The wrapping err.message detection in
153
+ // `isBranchNotProtected` keys on the structured form.
154
+ args.push('-H', 'Accept: application/vnd.github+json');
155
+ const child = spawn('gh', args, {
156
+ env,
157
+ stdio: ['pipe', 'pipe', 'pipe'],
158
+ });
159
+ let stdout = '';
160
+ let stderr = '';
161
+ child.stdout.on('data', (d) => {
162
+ stdout += d.toString();
163
+ });
164
+ child.stderr.on('data', (d) => {
165
+ stderr += d.toString();
166
+ });
167
+ child.on('error', reject);
168
+ child.on('close', (code) => {
169
+ if (code !== 0) {
170
+ const httpMatch = /HTTP (\d{3})/.exec(stderr);
171
+ const status = httpMatch ? Number(httpMatch[1]) : undefined;
172
+ const err = new Error((stderr.trim() || stdout.trim() || `gh api exited ${code}`).slice(0, 500));
173
+ if (status !== undefined)
174
+ err.status = status;
175
+ reject(err);
176
+ return;
177
+ }
178
+ try {
179
+ resolve(stdout ? JSON.parse(stdout) : {});
180
+ }
181
+ catch (parseErr) {
182
+ reject(parseErr);
183
+ }
184
+ });
185
+ if (body !== undefined) {
186
+ child.stdin.end(JSON.stringify(body));
187
+ }
188
+ else {
189
+ child.stdin.end();
190
+ }
191
+ });
192
+ }
193
+ return {
194
+ repos: {
195
+ async getBranchProtection({ owner, repo, branch }) {
196
+ const data = await ghApi('GET', `/repos/${owner}/${repo}/branches/${branch}/protection`);
197
+ return { data: data };
198
+ },
199
+ async updateBranchProtection({ owner, repo, branch, ...rest }) {
200
+ return ghApi('PUT', `/repos/${owner}/${repo}/branches/${branch}/protection`, rest);
201
+ },
202
+ async get({ owner, repo }) {
203
+ const data = await ghApi('GET', `/repos/${owner}/${repo}`);
204
+ return { data: data };
205
+ },
206
+ async update({ owner, repo, ...rest }) {
207
+ return ghApi('PATCH', `/repos/${owner}/${repo}`, rest);
208
+ },
209
+ },
210
+ };
211
+ }
212
+ function stringifyError(err) {
213
+ if (err instanceof Error)
214
+ return err.message;
215
+ return String(err);
216
+ }
217
+ /**
218
+ * Re-exported so callers can preload the ruleset (e.g. for a `--show-ruleset`
219
+ * flag in future). Currently exists for parity with the App's pattern.
220
+ */
221
+ export { loadCanonicalV1 };
222
+ //# sourceMappingURL=configure-github.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"configure-github.js","sourceRoot":"","sources":["../../src/cli/configure-github.ts"],"names":[],"mappings":"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"}
@@ -5,4 +5,5 @@ export { renderBlock, detectSkillRelPath, upsertBlock, hasAgentsMdImport, remove
5
5
  export { computeSkillUsageDelta, mergeSkillUsage, assessSkillHealth, formatHealthDashboard, DEFAULT_GH_RUNNER, fetchUsageArtifacts, aggregateUsageStream, type SkillDelta, type SkillUsageEntry, type SkillDeltaMap, type SkillUsageMap, type SkillHealthStatus, type SkillHealthRow, type GhRunResult, type GhRunner, type FetchUsageArtifactsOptions, type UsageArtifactRecord, } from './skill-usage.js';
6
6
  export { PRICING, computeReviewCost, costPerLOC, cacheHitRate, extractTokensFromLog, rollup, formatRollup, type ModelPricing, type TokenCounts, type CostParts, type ReviewCost, type ExtractedTokens, type ReviewRecord, type RollupGroupStats, type RollupTotal, type RollupTrend, type RollupOutlier, type UnknownModelReview, type Rollup, type FormatRollupOptions, } from './usage.js';
7
7
  export { runUpdate, type RunUpdateOptions, type UpdateChangeRecord, type UpdateSkippedRecord, type RunUpdateResult, } from './update.js';
8
+ export { runConfigureGithub, ghCliOctokit, type RunConfigureGithubOptions, } from './configure-github.js';
8
9
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EACL,UAAU,EACV,mBAAmB,EACnB,kBAAkB,EAClB,4BAA4B,EAC5B,KAAK,QAAQ,EACb,KAAK,SAAS,EACd,KAAK,SAAS,EACd,KAAK,iBAAiB,EACtB,KAAK,YAAY,EACjB,KAAK,0BAA0B,EAC/B,KAAK,eAAe,EACpB,KAAK,yBAAyB,EAC9B,KAAK,mCAAmC,EACxC,KAAK,YAAY,GAClB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EACL,uBAAuB,EACvB,cAAc,EACd,cAAc,EACd,GAAG,EACH,KAAK,oBAAoB,EACzB,KAAK,UAAU,EACf,KAAK,SAAS,GACf,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,WAAW,EACX,kBAAkB,EAClB,WAAW,EACX,iBAAiB,EACjB,WAAW,EACX,WAAW,EACX,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,GACvB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EACL,sBAAsB,EACtB,eAAe,EACf,iBAAiB,EACjB,qBAAqB,EACrB,iBAAiB,EACjB,mBAAmB,EACnB,oBAAoB,EACpB,KAAK,UAAU,EACf,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACnB,KAAK,WAAW,EAChB,KAAK,QAAQ,EACb,KAAK,0BAA0B,EAC/B,KAAK,mBAAmB,GACzB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACL,OAAO,EACP,iBAAiB,EACjB,UAAU,EACV,YAAY,EACZ,oBAAoB,EACpB,MAAM,EACN,YAAY,EACZ,KAAK,YAAY,EACjB,KAAK,WAAW,EAChB,KAAK,SAAS,EACd,KAAK,UAAU,EACf,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,KAAK,gBAAgB,EACrB,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,KAAK,kBAAkB,EACvB,KAAK,MAAM,EACX,KAAK,mBAAmB,GACzB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,SAAS,EACT,KAAK,gBAAgB,EACrB,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,EACxB,KAAK,eAAe,GACrB,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EACL,UAAU,EACV,mBAAmB,EACnB,kBAAkB,EAClB,4BAA4B,EAC5B,KAAK,QAAQ,EACb,KAAK,SAAS,EACd,KAAK,SAAS,EACd,KAAK,iBAAiB,EACtB,KAAK,YAAY,EACjB,KAAK,0BAA0B,EAC/B,KAAK,eAAe,EACpB,KAAK,yBAAyB,EAC9B,KAAK,mCAAmC,EACxC,KAAK,YAAY,GAClB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EACL,uBAAuB,EACvB,cAAc,EACd,cAAc,EACd,GAAG,EACH,KAAK,oBAAoB,EACzB,KAAK,UAAU,EACf,KAAK,SAAS,GACf,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,WAAW,EACX,kBAAkB,EAClB,WAAW,EACX,iBAAiB,EACjB,WAAW,EACX,WAAW,EACX,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,GACvB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EACL,sBAAsB,EACtB,eAAe,EACf,iBAAiB,EACjB,qBAAqB,EACrB,iBAAiB,EACjB,mBAAmB,EACnB,oBAAoB,EACpB,KAAK,UAAU,EACf,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACnB,KAAK,WAAW,EAChB,KAAK,QAAQ,EACb,KAAK,0BAA0B,EAC/B,KAAK,mBAAmB,GACzB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACL,OAAO,EACP,iBAAiB,EACjB,UAAU,EACV,YAAY,EACZ,oBAAoB,EACpB,MAAM,EACN,YAAY,EACZ,KAAK,YAAY,EACjB,KAAK,WAAW,EAChB,KAAK,SAAS,EACd,KAAK,UAAU,EACf,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,KAAK,gBAAgB,EACrB,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,KAAK,kBAAkB,EACvB,KAAK,MAAM,EACX,KAAK,mBAAmB,GACzB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,SAAS,EACT,KAAK,gBAAgB,EACrB,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,EACxB,KAAK,eAAe,GACrB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,kBAAkB,EAClB,YAAY,EACZ,KAAK,yBAAyB,GAC/B,MAAM,uBAAuB,CAAC"}
package/dist/cli/index.js CHANGED
@@ -15,4 +15,5 @@ export { renderBlock, detectSkillRelPath, upsertBlock, hasAgentsMdImport, remove
15
15
  export { computeSkillUsageDelta, mergeSkillUsage, assessSkillHealth, formatHealthDashboard, DEFAULT_GH_RUNNER, fetchUsageArtifacts, aggregateUsageStream, } from './skill-usage.js';
16
16
  export { PRICING, computeReviewCost, costPerLOC, cacheHitRate, extractTokensFromLog, rollup, formatRollup, } from './usage.js';
17
17
  export { runUpdate, } from './update.js';
18
+ export { runConfigureGithub, ghCliOctokit, } from './configure-github.js';
18
19
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,0CAA0C;AAC1C,EAAE;AACF,oEAAoE;AACpE,uEAAuE;AACvE,iBAAiB;AAEjB,oEAAoE;AACpE,oEAAoE;AACpE,0DAA0D;AAC1D,oCAAoC;AACpC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EACL,UAAU,EACV,mBAAmB,EACnB,kBAAkB,EAClB,4BAA4B,GAW7B,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EACL,uBAAuB,EACvB,cAAc,EACd,cAAc,EACd,GAAG,GAIJ,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,WAAW,EACX,kBAAkB,EAClB,WAAW,EACX,iBAAiB,EACjB,WAAW,EACX,WAAW,GAGZ,MAAM,gBAAgB,CAAC;AAExB,OAAO,EACL,sBAAsB,EACtB,eAAe,EACf,iBAAiB,EACjB,qBAAqB,EACrB,iBAAiB,EACjB,mBAAmB,EACnB,oBAAoB,GAWrB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACL,OAAO,EACP,iBAAiB,EACjB,UAAU,EACV,YAAY,EACZ,oBAAoB,EACpB,MAAM,EACN,YAAY,GAcb,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,SAAS,GAKV,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,0CAA0C;AAC1C,EAAE;AACF,oEAAoE;AACpE,uEAAuE;AACvE,iBAAiB;AAEjB,oEAAoE;AACpE,oEAAoE;AACpE,0DAA0D;AAC1D,oCAAoC;AACpC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EACL,UAAU,EACV,mBAAmB,EACnB,kBAAkB,EAClB,4BAA4B,GAW7B,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EACL,uBAAuB,EACvB,cAAc,EACd,cAAc,EACd,GAAG,GAIJ,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,WAAW,EACX,kBAAkB,EAClB,WAAW,EACX,iBAAiB,EACjB,WAAW,EACX,WAAW,GAGZ,MAAM,gBAAgB,CAAC;AAExB,OAAO,EACL,sBAAsB,EACtB,eAAe,EACf,iBAAiB,EACjB,qBAAqB,EACrB,iBAAiB,EACjB,mBAAmB,EACnB,oBAAoB,GAWrB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACL,OAAO,EACP,iBAAiB,EACjB,UAAU,EACV,YAAY,EACZ,oBAAoB,EACpB,MAAM,EACN,YAAY,GAcb,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,SAAS,GAKV,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,kBAAkB,EAClB,YAAY,GAEb,MAAM,uBAAuB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../src/cli/main.ts"],"names":[],"mappings":"AAwLA,iBAAe,IAAI,kBAyBlB;AAsyCD,OAAO,EAAE,IAAI,EAAE,CAAC"}
1
+ {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../src/cli/main.ts"],"names":[],"mappings":"AAuMA,iBAAe,IAAI,kBA0BlB;AAwzCD,OAAO,EAAE,IAAI,EAAE,CAAC"}
package/dist/cli/main.js CHANGED
@@ -55,6 +55,10 @@ function parseArgs(argv) {
55
55
  // users get the same one-command bootstrap as Python-first users
56
56
  // (logmind v0.6.8's --with-skdd is the symmetric counterpart).
57
57
  withSkdd: false,
58
+ // v0.7.0-rc.4: `clud-bug configure-github` flags.
59
+ // --dry-run prints the diff but skips PATCH; --branch overrides "main".
60
+ dryRun: false,
61
+ branch: null,
58
62
  };
59
63
  for (let i = 0; i < argv.length; i++) {
60
64
  const a = argv[i];
@@ -96,6 +100,10 @@ function parseArgs(argv) {
96
100
  args.artifacts = false;
97
101
  else if (a === '--with-skdd')
98
102
  args.withSkdd = true;
103
+ else if (a === '--dry-run')
104
+ args.dryRun = true;
105
+ else if (a === '--branch')
106
+ args.branch = argv[++i];
99
107
  else
100
108
  args._.push(a);
101
109
  }
@@ -117,6 +125,12 @@ Commands:
117
125
  Use --since / --changed-in / --scope to narrow.
118
126
  update Re-render workflows + refresh baseline specimens to the latest shipped
119
127
  templates. Custom and skills.sh-installed specimens left alone.
128
+ configure-github <owner>/<repo>
129
+ Apply the SPEC §7 canonical branch protection ruleset to a repo.
130
+ Auth: GITHUB_TOKEN env first, then \`gh auth token\`. Use
131
+ --dry-run to print the diff without PATCH-ing; --branch to
132
+ target a non-main branch. Idempotent — already-canonical
133
+ repos exit 0 with no changes.
120
134
  edit-workflow Helper for editing .github/workflows/clud-bug-*.yml in an isolated
121
135
  PR (the action refuses to review PRs that modify its own workflow).
122
136
  usage Read recent clud-bug-review run JSON + normalize cost per LOC.
@@ -175,6 +189,9 @@ Options:
175
189
  required_conversation_resolution on the default
176
190
  branch (init only). Use for repos that manage
177
191
  branch protection via ruleset or org policy.
192
+ --dry-run Print the canonical-v1 diff without PATCH-ing
193
+ (configure-github only).
194
+ --branch <name> Target branch for configure-github (default: main).
178
195
  --repo <owner/name> Restrict \`usage\` to a single repo. Default: all repos
179
196
  with clud-bug-review.yml in the gh user's auth scope.
180
197
  --pr <N> Restrict \`usage\` to a single PR.
@@ -214,6 +231,7 @@ async function main() {
214
231
  case 'audit': return runAudit(args);
215
232
  case 'update': return runUpdateCmd(args);
216
233
  case 'edit-workflow': return runEditWorkflow(args);
234
+ case 'configure-github': return runConfigureGithubCmd(args);
217
235
  case 'usage': return runUsage(args);
218
236
  case 'eval': return runEval();
219
237
  case 'render': return runRender(args);
@@ -1092,6 +1110,24 @@ async function runUpdateCmd(_args) {
1092
1110
  log('Commit + push to apply the refreshed kit on the next PR.');
1093
1111
  ok(`updated: @v${ourVersion}, ${result.changed.length} changed, ${result.unchanged.length} unchanged${skipped.length ? `, ${skipped.length} skipped` : ''}`);
1094
1112
  }
1113
+ // v0.7.0-rc.4 — `clud-bug configure-github <owner>/<repo>`. Pulls in the
1114
+ // SPEC §7 canonical ruleset applier from src/cli/configure-github.ts;
1115
+ // thin wrapper here just maps CLI args into the typed entry point. The
1116
+ // command is idempotent — re-runs on a converged repo exit 0 with no
1117
+ // PATCH calls. See `src/core/configure-github.ts` for the diff + rule
1118
+ // table.
1119
+ async function runConfigureGithubCmd(args) {
1120
+ const { runConfigureGithub } = await import('./configure-github.js');
1121
+ const target = args._[1] ?? null;
1122
+ const code = await runConfigureGithub({
1123
+ target,
1124
+ branch: args.branch || 'main',
1125
+ dryRun: Boolean(args.dryRun),
1126
+ quiet: QUIET,
1127
+ });
1128
+ if (code !== 0)
1129
+ process.exit(code);
1130
+ }
1095
1131
  async function runAudit(args) {
1096
1132
  const cwd = process.cwd();
1097
1133
  const date = new Date().toISOString().slice(0, 10);