clud-bug 0.7.0-rc.2 → 0.7.0-rc.21
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 +20 -0
- package/data/canonical-v1.json +37 -0
- package/dist/cli/configure-github.d.ts +58 -0
- package/dist/cli/configure-github.d.ts.map +1 -0
- package/dist/cli/configure-github.js +242 -0
- package/dist/cli/configure-github.js.map +1 -0
- package/dist/cli/hooks.d.ts +51 -0
- package/dist/cli/hooks.d.ts.map +1 -0
- package/dist/cli/hooks.js +138 -0
- package/dist/cli/hooks.js.map +1 -0
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +1 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/main.js +949 -35
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/post-check-run.d.ts +16 -0
- package/dist/cli/post-check-run.d.ts.map +1 -0
- package/dist/cli/post-check-run.js +93 -0
- package/dist/cli/post-check-run.js.map +1 -0
- package/dist/cli/review-prompt.d.ts +43 -0
- package/dist/cli/review-prompt.d.ts.map +1 -0
- package/dist/cli/review-prompt.js +287 -0
- package/dist/cli/review-prompt.js.map +1 -0
- package/dist/cli/skills.d.ts +7 -0
- package/dist/cli/skills.d.ts.map +1 -1
- package/dist/cli/skills.js +42 -2
- package/dist/cli/skills.js.map +1 -1
- package/dist/cli/update-notifier.d.ts +17 -0
- package/dist/cli/update-notifier.d.ts.map +1 -0
- package/dist/cli/update-notifier.js +88 -0
- package/dist/cli/update-notifier.js.map +1 -0
- package/dist/cli/update.d.ts.map +1 -1
- package/dist/cli/update.js +60 -11
- package/dist/cli/update.js.map +1 -1
- package/dist/core/auto-resolve.d.ts +235 -0
- package/dist/core/auto-resolve.d.ts.map +1 -0
- package/dist/core/auto-resolve.js +293 -0
- package/dist/core/auto-resolve.js.map +1 -0
- package/dist/core/budget-plan.d.ts +125 -0
- package/dist/core/budget-plan.d.ts.map +1 -0
- package/dist/core/budget-plan.js +211 -0
- package/dist/core/budget-plan.js.map +1 -0
- package/dist/core/check-verdict.d.ts +31 -0
- package/dist/core/check-verdict.d.ts.map +1 -0
- package/dist/core/check-verdict.js +66 -0
- package/dist/core/check-verdict.js.map +1 -0
- package/dist/core/configure-github.d.ts +179 -0
- package/dist/core/configure-github.d.ts.map +1 -0
- package/dist/core/configure-github.js +289 -0
- package/dist/core/configure-github.js.map +1 -0
- package/dist/core/design.d.ts +37 -0
- package/dist/core/design.d.ts.map +1 -0
- package/dist/core/design.js +47 -0
- package/dist/core/design.js.map +1 -0
- package/dist/core/diff-findings.d.ts +96 -0
- package/dist/core/diff-findings.d.ts.map +1 -0
- package/dist/core/diff-findings.js +261 -0
- package/dist/core/diff-findings.js.map +1 -0
- package/dist/core/formal-review.d.ts +79 -0
- package/dist/core/formal-review.d.ts.map +1 -0
- package/dist/core/formal-review.js +71 -0
- package/dist/core/formal-review.js.map +1 -0
- package/dist/core/index.d.ts +15 -2
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +53 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/inline-threads.d.ts +160 -0
- package/dist/core/inline-threads.d.ts.map +1 -0
- package/dist/core/inline-threads.js +369 -0
- package/dist/core/inline-threads.js.map +1 -0
- package/dist/core/multi-pass-aggregate.d.ts +161 -0
- package/dist/core/multi-pass-aggregate.d.ts.map +1 -0
- package/dist/core/multi-pass-aggregate.js +457 -0
- package/dist/core/multi-pass-aggregate.js.map +1 -0
- package/dist/core/plan-review.d.ts +57 -0
- package/dist/core/plan-review.d.ts.map +1 -0
- package/dist/core/plan-review.js +77 -0
- package/dist/core/plan-review.js.map +1 -0
- package/dist/core/prompt-builder.d.ts +18 -0
- package/dist/core/prompt-builder.d.ts.map +1 -1
- package/dist/core/prompt-builder.js +24 -2
- package/dist/core/prompt-builder.js.map +1 -1
- package/dist/core/render.d.ts.map +1 -1
- package/dist/core/render.js +1 -19
- package/dist/core/render.js.map +1 -1
- package/dist/core/resolve-verifier.d.ts +53 -0
- package/dist/core/resolve-verifier.d.ts.map +1 -0
- package/dist/core/resolve-verifier.js +204 -0
- package/dist/core/resolve-verifier.js.map +1 -0
- package/dist/core/review-context.d.ts +39 -0
- package/dist/core/review-context.d.ts.map +1 -0
- package/dist/core/review-context.js +99 -0
- package/dist/core/review-context.js.map +1 -0
- package/dist/core/review-plan.d.ts +219 -0
- package/dist/core/review-plan.d.ts.map +1 -0
- package/dist/core/review-plan.js +274 -0
- package/dist/core/review-plan.js.map +1 -0
- package/dist/core/review-writeback.d.ts +74 -0
- package/dist/core/review-writeback.d.ts.map +1 -1
- package/dist/core/review-writeback.js +71 -4
- package/dist/core/review-writeback.js.map +1 -1
- package/dist/core/skills.d.ts +25 -0
- package/dist/core/skills.d.ts.map +1 -1
- package/dist/core/skills.js +100 -4
- package/dist/core/skills.js.map +1 -1
- package/dist/core/version.d.ts +2 -0
- package/dist/core/version.d.ts.map +1 -0
- package/dist/core/version.js +4 -0
- package/dist/core/version.js.map +1 -0
- package/package.json +11 -5
- package/src/cli/configure-github.ts +329 -0
- package/src/cli/hooks.ts +155 -0
- package/src/cli/index.ts +6 -0
- package/src/cli/main.ts +1031 -37
- package/src/cli/post-check-run.ts +115 -0
- package/src/cli/review-prompt.ts +355 -0
- package/src/cli/skills.ts +42 -2
- package/src/cli/update-notifier.ts +100 -0
- package/src/cli/update.ts +60 -11
- package/src/core/auto-resolve.ts +488 -0
- package/src/core/budget-plan.ts +324 -0
- package/src/core/check-verdict.ts +93 -0
- package/src/core/configure-github.ts +497 -0
- package/src/core/design.ts +73 -0
- package/src/core/diff-findings.ts +323 -0
- package/src/core/formal-review.ts +168 -0
- package/src/core/index.ts +188 -0
- package/src/core/inline-threads.ts +471 -0
- package/src/core/multi-pass-aggregate.ts +591 -0
- package/src/core/plan-review.ts +136 -0
- package/src/core/prompt-builder.ts +44 -2
- package/src/core/render.ts +10 -16
- package/src/core/resolve-verifier.ts +254 -0
- package/src/core/review-context.ts +106 -0
- package/src/core/review-plan.ts +485 -0
- package/src/core/review-writeback.ts +161 -4
- package/src/core/skills.ts +128 -5
- package/src/core/version.ts +3 -0
- package/templates/clud-bug-review.md.tmpl +115 -0
- package/templates/skills/design/design-system-consistency.md +41 -0
- package/templates/skills/design/frontend-a11y.md +41 -0
- package/templates/skills/design/visual-polish.md +41 -0
- package/templates/workflow-py.yml.tmpl +92 -2
- package/templates/workflow-ts.yml.tmpl +92 -2
- package/templates/workflow.yml.tmpl +173 -2
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
// SPEC §7 canonical-ruleset applier — pure core half of `configure-github`.
|
|
2
|
+
//
|
|
3
|
+
// External users installing the App expect "best practice branch protection"
|
|
4
|
+
// applied automatically. This module diffs the current GitHub state against
|
|
5
|
+
// the canonical ruleset (bundled at `data/canonical-v1.json`) and emits the
|
|
6
|
+
// minimal set of PATCH calls to converge.
|
|
7
|
+
//
|
|
8
|
+
// Architectural shape mirrors `formal-review.ts` and `review-writeback.ts`:
|
|
9
|
+
// the pure rule-table + diff logic lives in core (no Octokit dep at compile
|
|
10
|
+
// time — clud-bug-app already has `@octokit/rest` and passes its instance;
|
|
11
|
+
// the CLI side wraps `gh api` in a tiny adapter that satisfies the same
|
|
12
|
+
// structural interface). Core stays npm-side single-source-of-truth per the
|
|
13
|
+
// Bug 9 / Phase 2-4 architectural lock.
|
|
14
|
+
//
|
|
15
|
+
// Idempotent contract (HARD GUARANTEE):
|
|
16
|
+
//
|
|
17
|
+
// const a = await applyCanonicalRuleset(octokit, params);
|
|
18
|
+
// const b = await applyCanonicalRuleset(octokit, params);
|
|
19
|
+
// // b.alreadyCanonical === true; b.changes.length === 0
|
|
20
|
+
//
|
|
21
|
+
// A second `apply()` call against a freshly-converged repo MUST produce
|
|
22
|
+
// `alreadyCanonical: true` with `changes: []` and zero PATCH calls. This
|
|
23
|
+
// lets external automation (CI, dispatch loops, dashboard probes) call
|
|
24
|
+
// `apply()` defensively without rate-limiting itself out of the API.
|
|
25
|
+
//
|
|
26
|
+
// SPEC pins honored here:
|
|
27
|
+
// - canonical-v1.json schema (frozen at v1; major bumps require coordinated
|
|
28
|
+
// tool releases per the `$comment` field).
|
|
29
|
+
// - `required_status_checks.contexts` are TREATED AS A SUPERSET: if the
|
|
30
|
+
// repo already requires MORE contexts than the canonical list, we leave
|
|
31
|
+
// them alone (a repo that runs additional CI gates legitimately needs
|
|
32
|
+
// them in the required set). We only ADD the canonical contexts that
|
|
33
|
+
// are missing.
|
|
34
|
+
// - `required_approving_review_count`: canonical floor is 1. If the repo
|
|
35
|
+
// already requires MORE than 1, we leave it alone (only raise to floor).
|
|
36
|
+
//
|
|
37
|
+
// Offline-resilient bundling: the canonical ruleset ships in `data/` at
|
|
38
|
+
// the package root (alongside `bin/`, `templates/`, etc., per package.json
|
|
39
|
+
// `files`). We read it at runtime via fs.readFile so the build stays out
|
|
40
|
+
// of TypeScript's `rootDir` constraint, and so callers that re-bundle
|
|
41
|
+
// clud-bug into a single file can override the ruleset by passing one
|
|
42
|
+
// explicitly to `applyCanonicalRuleset({ ruleset })`.
|
|
43
|
+
import { readFile } from 'node:fs/promises';
|
|
44
|
+
import { dirname, join } from 'node:path';
|
|
45
|
+
import { fileURLToPath } from 'node:url';
|
|
46
|
+
/**
|
|
47
|
+
* Resolves the package root from this file's URL. After tsc the file lives
|
|
48
|
+
* at `<pkg>/dist/core/configure-github.js`; three dirname() climbs land on
|
|
49
|
+
* `<pkg>`, parallel to the package.json `files` map entry for `data`.
|
|
50
|
+
*/
|
|
51
|
+
const PKG_ROOT = dirname(dirname(dirname(fileURLToPath(import.meta.url))));
|
|
52
|
+
/** Memoized canonical ruleset — loaded once per process. */
|
|
53
|
+
let CANONICAL_V1_CACHE = null;
|
|
54
|
+
/**
|
|
55
|
+
* Loads the bundled canonical-v1 ruleset from `data/canonical-v1.json`.
|
|
56
|
+
* Memoized so repeated calls don't re-hit the filesystem.
|
|
57
|
+
*
|
|
58
|
+
* Throws a wrapped error if the JSON file is missing — that's an
|
|
59
|
+
* install-time defect (package.json `files` excluded `data/`), not a
|
|
60
|
+
* runtime concern, and the loud failure surfaces it immediately.
|
|
61
|
+
*/
|
|
62
|
+
export async function loadCanonicalV1() {
|
|
63
|
+
if (CANONICAL_V1_CACHE)
|
|
64
|
+
return CANONICAL_V1_CACHE;
|
|
65
|
+
const path = join(PKG_ROOT, 'data', 'canonical-v1.json');
|
|
66
|
+
try {
|
|
67
|
+
const raw = await readFile(path, 'utf-8');
|
|
68
|
+
CANONICAL_V1_CACHE = JSON.parse(raw);
|
|
69
|
+
return CANONICAL_V1_CACHE;
|
|
70
|
+
}
|
|
71
|
+
catch (err) {
|
|
72
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
73
|
+
throw new Error(`configure-github: failed to load bundled canonical-v1.json at ${path}: ${msg}`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Applies the canonical ruleset to a GitHub repo. Reads current state via
|
|
78
|
+
* the Octokit-like instance, diffs against `ruleset` (default
|
|
79
|
+
* `CANONICAL_V1`), and PATCHes only what differs. Idempotent: second call
|
|
80
|
+
* returns `alreadyCanonical: true` with no PATCH side effects.
|
|
81
|
+
*
|
|
82
|
+
* Behavior on partial mismatch:
|
|
83
|
+
*
|
|
84
|
+
* - `required_status_checks.contexts`: canonical contexts that are
|
|
85
|
+
* missing get added; extra contexts on the repo are preserved (superset
|
|
86
|
+
* contract — a repo that runs more CI gates legitimately needs them).
|
|
87
|
+
* - `required_approving_review_count`: canonical floor is 1; if the repo
|
|
88
|
+
* already requires more, we don't lower (raise-only contract).
|
|
89
|
+
* - All booleans (allow_force_pushes, allow_deletions, etc.): converge
|
|
90
|
+
* to the canonical value exactly.
|
|
91
|
+
*
|
|
92
|
+
* Throws on Octokit transport failure (auth, network, 403). The CLI
|
|
93
|
+
* wraps these with a friendly error message.
|
|
94
|
+
*/
|
|
95
|
+
export async function applyCanonicalRuleset(octokit, params) {
|
|
96
|
+
const { owner, repo, branch = 'main', dryRun = false, } = params;
|
|
97
|
+
const ruleset = params.ruleset ?? (await loadCanonicalV1());
|
|
98
|
+
const target = ruleset.branch_protection;
|
|
99
|
+
const changes = [];
|
|
100
|
+
// ----- Read current branch protection state ---------------------------
|
|
101
|
+
// 404 → no base protection rule. We treat that as "every canonical
|
|
102
|
+
// setting differs" so the apply path creates the rule. Other errors
|
|
103
|
+
// bubble up — callers can't recover from a 403.
|
|
104
|
+
let current = {};
|
|
105
|
+
let hasBaseProtection = true;
|
|
106
|
+
try {
|
|
107
|
+
const resp = await octokit.repos.getBranchProtection({
|
|
108
|
+
owner,
|
|
109
|
+
repo,
|
|
110
|
+
branch,
|
|
111
|
+
});
|
|
112
|
+
current = resp.data;
|
|
113
|
+
}
|
|
114
|
+
catch (err) {
|
|
115
|
+
if (isBranchNotProtected(err)) {
|
|
116
|
+
hasBaseProtection = false;
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
throw err;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
// ----- Diff: required_status_checks -----------------------------------
|
|
123
|
+
// Behavior: contexts are a SUPERSET. Strict mode must match exactly.
|
|
124
|
+
const currentChecks = current.required_status_checks ?? null;
|
|
125
|
+
const currentContexts = new Set(currentChecks?.contexts ?? []);
|
|
126
|
+
const targetContexts = new Set(target.required_status_checks.contexts);
|
|
127
|
+
const missingContexts = [...targetContexts].filter((c) => !currentContexts.has(c));
|
|
128
|
+
const strictMismatch = (currentChecks?.strict ?? false) !== target.required_status_checks.strict;
|
|
129
|
+
let needsChecksPatch = !hasBaseProtection;
|
|
130
|
+
if (missingContexts.length > 0) {
|
|
131
|
+
changes.push(`required_status_checks.contexts: add ${JSON.stringify(missingContexts)}`);
|
|
132
|
+
needsChecksPatch = true;
|
|
133
|
+
}
|
|
134
|
+
if (strictMismatch) {
|
|
135
|
+
changes.push(`required_status_checks.strict: ${currentChecks?.strict ?? false} → ${target.required_status_checks.strict}`);
|
|
136
|
+
needsChecksPatch = true;
|
|
137
|
+
}
|
|
138
|
+
// The merged context list: keep extras the repo already required.
|
|
139
|
+
const mergedContexts = [
|
|
140
|
+
...new Set([
|
|
141
|
+
...(currentChecks?.contexts ?? []),
|
|
142
|
+
...target.required_status_checks.contexts,
|
|
143
|
+
]),
|
|
144
|
+
];
|
|
145
|
+
// ----- Diff: required_pull_request_reviews ----------------------------
|
|
146
|
+
// Behavior: approving_review_count is a FLOOR (only raise). Booleans
|
|
147
|
+
// converge exactly.
|
|
148
|
+
const currentReviews = current.required_pull_request_reviews ?? null;
|
|
149
|
+
const currentCount = currentReviews?.required_approving_review_count ?? 0;
|
|
150
|
+
const targetCount = target.required_pull_request_reviews.required_approving_review_count;
|
|
151
|
+
const effectiveCount = Math.max(currentCount, targetCount);
|
|
152
|
+
const targetDismiss = target.required_pull_request_reviews.dismiss_stale_reviews;
|
|
153
|
+
const targetCodeOwner = target.required_pull_request_reviews.require_code_owner_reviews;
|
|
154
|
+
let needsReviewsPatch = !hasBaseProtection;
|
|
155
|
+
if (effectiveCount !== currentCount) {
|
|
156
|
+
changes.push(`required_pull_request_reviews.required_approving_review_count: ${currentCount} → ${effectiveCount}`);
|
|
157
|
+
needsReviewsPatch = true;
|
|
158
|
+
}
|
|
159
|
+
if ((currentReviews?.dismiss_stale_reviews ?? false) !== targetDismiss) {
|
|
160
|
+
changes.push(`required_pull_request_reviews.dismiss_stale_reviews: ${currentReviews?.dismiss_stale_reviews ?? false} → ${targetDismiss}`);
|
|
161
|
+
needsReviewsPatch = true;
|
|
162
|
+
}
|
|
163
|
+
if ((currentReviews?.require_code_owner_reviews ?? false) !== targetCodeOwner) {
|
|
164
|
+
changes.push(`required_pull_request_reviews.require_code_owner_reviews: ${currentReviews?.require_code_owner_reviews ?? false} → ${targetCodeOwner}`);
|
|
165
|
+
needsReviewsPatch = true;
|
|
166
|
+
}
|
|
167
|
+
// ----- Diff: single-flag branch protection booleans -------------------
|
|
168
|
+
const conv = current.required_conversation_resolution?.enabled ?? false;
|
|
169
|
+
const enforceAdmins = current.enforce_admins?.enabled ?? false;
|
|
170
|
+
const allowFP = current.allow_force_pushes?.enabled ?? false;
|
|
171
|
+
const allowDel = current.allow_deletions?.enabled ?? false;
|
|
172
|
+
const linearHistory = current.required_linear_history?.enabled ?? false;
|
|
173
|
+
if (conv !== target.required_conversation_resolution) {
|
|
174
|
+
changes.push(`required_conversation_resolution: ${conv} → ${target.required_conversation_resolution}`);
|
|
175
|
+
}
|
|
176
|
+
if (enforceAdmins !== target.enforce_admins) {
|
|
177
|
+
changes.push(`enforce_admins: ${enforceAdmins} → ${target.enforce_admins}`);
|
|
178
|
+
}
|
|
179
|
+
if (allowFP !== target.allow_force_pushes) {
|
|
180
|
+
changes.push(`allow_force_pushes: ${allowFP} → ${target.allow_force_pushes}`);
|
|
181
|
+
}
|
|
182
|
+
if (allowDel !== target.allow_deletions) {
|
|
183
|
+
changes.push(`allow_deletions: ${allowDel} → ${target.allow_deletions}`);
|
|
184
|
+
}
|
|
185
|
+
if (linearHistory !== target.required_linear_history) {
|
|
186
|
+
changes.push(`required_linear_history: ${linearHistory} → ${target.required_linear_history}`);
|
|
187
|
+
}
|
|
188
|
+
// ----- Diff: repo-level settings --------------------------------------
|
|
189
|
+
// delete_branch_on_merge / allow_auto_merge / squash commit shape live
|
|
190
|
+
// on the REPO, not the branch protection rule. Fetched separately.
|
|
191
|
+
const repoResp = await octokit.repos.get({ owner, repo });
|
|
192
|
+
const repoData = repoResp.data;
|
|
193
|
+
const repoPatch = {
|
|
194
|
+
owner,
|
|
195
|
+
repo,
|
|
196
|
+
};
|
|
197
|
+
let needsRepoPatch = false;
|
|
198
|
+
if ((repoData.delete_branch_on_merge ?? false) !== target.delete_branch_on_merge) {
|
|
199
|
+
changes.push(`delete_branch_on_merge: ${repoData.delete_branch_on_merge ?? false} → ${target.delete_branch_on_merge}`);
|
|
200
|
+
repoPatch.delete_branch_on_merge = target.delete_branch_on_merge;
|
|
201
|
+
needsRepoPatch = true;
|
|
202
|
+
}
|
|
203
|
+
if ((repoData.allow_auto_merge ?? false) !== target.allow_auto_merge) {
|
|
204
|
+
changes.push(`allow_auto_merge: ${repoData.allow_auto_merge ?? false} → ${target.allow_auto_merge}`);
|
|
205
|
+
repoPatch.allow_auto_merge = target.allow_auto_merge;
|
|
206
|
+
needsRepoPatch = true;
|
|
207
|
+
}
|
|
208
|
+
if ((repoData.squash_merge_commit_title ?? '') !==
|
|
209
|
+
target.squash_merge_commit_title) {
|
|
210
|
+
changes.push(`squash_merge_commit_title: ${repoData.squash_merge_commit_title ?? '(unset)'} → ${target.squash_merge_commit_title}`);
|
|
211
|
+
repoPatch.squash_merge_commit_title = target.squash_merge_commit_title;
|
|
212
|
+
needsRepoPatch = true;
|
|
213
|
+
}
|
|
214
|
+
if ((repoData.squash_merge_commit_message ?? '') !==
|
|
215
|
+
target.squash_merge_commit_message) {
|
|
216
|
+
changes.push(`squash_merge_commit_message: ${repoData.squash_merge_commit_message ?? '(unset)'} → ${target.squash_merge_commit_message}`);
|
|
217
|
+
repoPatch.squash_merge_commit_message = target.squash_merge_commit_message;
|
|
218
|
+
needsRepoPatch = true;
|
|
219
|
+
}
|
|
220
|
+
const alreadyCanonical = changes.length === 0;
|
|
221
|
+
if (alreadyCanonical || dryRun) {
|
|
222
|
+
return {
|
|
223
|
+
changes,
|
|
224
|
+
alreadyCanonical,
|
|
225
|
+
ruleset: 'canonical-v1',
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
// ----- Apply: branch protection ---------------------------------------
|
|
229
|
+
// The REST API requires a PUT (not PATCH) with the full settings object.
|
|
230
|
+
// We always send a complete envelope; the diff above gates whether we
|
|
231
|
+
// call PUT at all.
|
|
232
|
+
if (needsChecksPatch || needsReviewsPatch || !hasBaseProtection || hasBranchProtectionLevelDiff(changes)) {
|
|
233
|
+
await octokit.repos.updateBranchProtection({
|
|
234
|
+
owner,
|
|
235
|
+
repo,
|
|
236
|
+
branch,
|
|
237
|
+
required_status_checks: {
|
|
238
|
+
strict: target.required_status_checks.strict,
|
|
239
|
+
contexts: mergedContexts,
|
|
240
|
+
},
|
|
241
|
+
enforce_admins: target.enforce_admins,
|
|
242
|
+
required_pull_request_reviews: {
|
|
243
|
+
required_approving_review_count: effectiveCount,
|
|
244
|
+
dismiss_stale_reviews: targetDismiss,
|
|
245
|
+
require_code_owner_reviews: targetCodeOwner,
|
|
246
|
+
},
|
|
247
|
+
restrictions: null,
|
|
248
|
+
required_conversation_resolution: target.required_conversation_resolution,
|
|
249
|
+
required_linear_history: target.required_linear_history,
|
|
250
|
+
allow_force_pushes: target.allow_force_pushes,
|
|
251
|
+
allow_deletions: target.allow_deletions,
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
// ----- Apply: repo-level settings -------------------------------------
|
|
255
|
+
if (needsRepoPatch) {
|
|
256
|
+
await octokit.repos.update(repoPatch);
|
|
257
|
+
}
|
|
258
|
+
return {
|
|
259
|
+
changes,
|
|
260
|
+
alreadyCanonical: false,
|
|
261
|
+
ruleset: 'canonical-v1',
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Octokit (and gh's wrapping adapter) report a missing branch protection
|
|
266
|
+
* rule as a 404. We catch on the structural shape of the error so the
|
|
267
|
+
* downstream Octokit version doesn't matter.
|
|
268
|
+
*/
|
|
269
|
+
function isBranchNotProtected(err) {
|
|
270
|
+
if (!err || typeof err !== 'object')
|
|
271
|
+
return false;
|
|
272
|
+
const e = err;
|
|
273
|
+
if (e.status === 404)
|
|
274
|
+
return true;
|
|
275
|
+
if (typeof e.message === 'string') {
|
|
276
|
+
return /Branch not protected|Not Found|404/i.test(e.message);
|
|
277
|
+
}
|
|
278
|
+
return false;
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Detect whether any of the per-branch protection settings (conversation
|
|
282
|
+
* resolution / force-pushes / deletions / linear-history / enforce-admins)
|
|
283
|
+
* appeared in the change list. Used to decide whether the branch protection
|
|
284
|
+
* PUT needs to fire when status_checks + reviews are both in sync.
|
|
285
|
+
*/
|
|
286
|
+
function hasBranchProtectionLevelDiff(changes) {
|
|
287
|
+
return changes.some((c) => /^(required_conversation_resolution|enforce_admins|allow_force_pushes|allow_deletions|required_linear_history):/.test(c));
|
|
288
|
+
}
|
|
289
|
+
//# sourceMappingURL=configure-github.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"configure-github.js","sourceRoot":"","sources":["../../src/core/configure-github.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { ReviewTrigger } from './plan-review.js';
|
|
2
|
+
/** How design findings interact with the merge gate. */
|
|
3
|
+
export type DesignGate = 'advisory' | 'strict';
|
|
4
|
+
/** Resolved `.clud-bug.json` `design` block (defaults applied). */
|
|
5
|
+
export interface DesignConfig {
|
|
6
|
+
/** Master switch. Default OFF — the design pass never runs unless this is true. */
|
|
7
|
+
enabled: boolean;
|
|
8
|
+
/**
|
|
9
|
+
* `advisory` (default) — design findings post as comments, never block merge.
|
|
10
|
+
* `strict` — a design `critical` turns the check RED (opt-in).
|
|
11
|
+
*/
|
|
12
|
+
gate: DesignGate;
|
|
13
|
+
/** Themes to render + critique. Default both. */
|
|
14
|
+
themes: string[];
|
|
15
|
+
/** Viewports to render. Default a single desktop viewport. */
|
|
16
|
+
viewports: string[];
|
|
17
|
+
}
|
|
18
|
+
/** Off-by-default builtin — the cost-control floor. */
|
|
19
|
+
export declare const BUILTIN_DESIGN_CONFIG: DesignConfig;
|
|
20
|
+
/**
|
|
21
|
+
* Read + normalize the `design` block from a parsed `.clud-bug.json` manifest.
|
|
22
|
+
* Tolerant: a missing/malformed block resolves to the off-by-default builtin,
|
|
23
|
+
* so a typo can never silently *enable* the (cost-bearing) pass.
|
|
24
|
+
*/
|
|
25
|
+
export declare function readDesignConfig(manifest: unknown): DesignConfig;
|
|
26
|
+
/**
|
|
27
|
+
* Consumer-agnostic run-gate for the design-critic. Pure.
|
|
28
|
+
*
|
|
29
|
+
* True only when the repo opted in (`enabled`), at least one `kind: design`
|
|
30
|
+
* skill applies, and this is a PR-level review (the pass is too expensive for
|
|
31
|
+
* per-commit / per-push triggers). Consumers layer their own runtime
|
|
32
|
+
* preconditions on top: the local recipe defers the deploy-preview-URL check to
|
|
33
|
+
* the agent; the hosted bot additionally requires a paying tier + a resolved
|
|
34
|
+
* preview URL before it spends a render.
|
|
35
|
+
*/
|
|
36
|
+
export declare function shouldRunDesign(config: DesignConfig, designSkillCount: number, trigger: ReviewTrigger): boolean;
|
|
37
|
+
//# sourceMappingURL=design.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"design.d.ts","sourceRoot":"","sources":["../../src/core/design.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEtD,wDAAwD;AACxD,MAAM,MAAM,UAAU,GAAG,UAAU,GAAG,QAAQ,CAAC;AAE/C,mEAAmE;AACnE,MAAM,WAAW,YAAY;IAC3B,mFAAmF;IACnF,OAAO,EAAE,OAAO,CAAC;IACjB;;;OAGG;IACH,IAAI,EAAE,UAAU,CAAC;IACjB,iDAAiD;IACjD,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,8DAA8D;IAC9D,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB;AAED,uDAAuD;AACvD,eAAO,MAAM,qBAAqB,EAAE,YAKnC,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,OAAO,GAAG,YAAY,CAYhE;AAED;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,YAAY,EACpB,gBAAgB,EAAE,MAAM,EACxB,OAAO,EAAE,aAAa,GACrB,OAAO,CAET"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// Design-critic lens config + gate (Track B, rc.15).
|
|
2
|
+
//
|
|
3
|
+
// The design-critic is an OPTIONAL, off-by-default visual review pass that
|
|
4
|
+
// renders the changed UI (light + dark) and critiques it against `kind: design`
|
|
5
|
+
// skills. It is gated tightly so it only ever runs — and only ever costs — when
|
|
6
|
+
// a repo has explicitly opted in. This module is the shared, pure brain: the
|
|
7
|
+
// local recipe (`review-prompt`) and the hosted bot both resolve the config and
|
|
8
|
+
// the run-gate here so the policy can't fork (SPEC §11.5 / §12).
|
|
9
|
+
/** Off-by-default builtin — the cost-control floor. */
|
|
10
|
+
export const BUILTIN_DESIGN_CONFIG = {
|
|
11
|
+
enabled: false,
|
|
12
|
+
gate: 'advisory',
|
|
13
|
+
themes: ['light', 'dark'],
|
|
14
|
+
viewports: ['desktop'],
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Read + normalize the `design` block from a parsed `.clud-bug.json` manifest.
|
|
18
|
+
* Tolerant: a missing/malformed block resolves to the off-by-default builtin,
|
|
19
|
+
* so a typo can never silently *enable* the (cost-bearing) pass.
|
|
20
|
+
*/
|
|
21
|
+
export function readDesignConfig(manifest) {
|
|
22
|
+
const raw = manifest?.design;
|
|
23
|
+
if (!raw || typeof raw !== 'object')
|
|
24
|
+
return { ...BUILTIN_DESIGN_CONFIG };
|
|
25
|
+
const d = raw;
|
|
26
|
+
const strArr = (v, fallback) => Array.isArray(v) && v.length > 0 ? v.map(String) : [...fallback];
|
|
27
|
+
return {
|
|
28
|
+
enabled: d['enabled'] === true,
|
|
29
|
+
gate: d['gate'] === 'strict' ? 'strict' : 'advisory',
|
|
30
|
+
themes: strArr(d['themes'], BUILTIN_DESIGN_CONFIG.themes),
|
|
31
|
+
viewports: strArr(d['viewports'], BUILTIN_DESIGN_CONFIG.viewports),
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Consumer-agnostic run-gate for the design-critic. Pure.
|
|
36
|
+
*
|
|
37
|
+
* True only when the repo opted in (`enabled`), at least one `kind: design`
|
|
38
|
+
* skill applies, and this is a PR-level review (the pass is too expensive for
|
|
39
|
+
* per-commit / per-push triggers). Consumers layer their own runtime
|
|
40
|
+
* preconditions on top: the local recipe defers the deploy-preview-URL check to
|
|
41
|
+
* the agent; the hosted bot additionally requires a paying tier + a resolved
|
|
42
|
+
* preview URL before it spends a render.
|
|
43
|
+
*/
|
|
44
|
+
export function shouldRunDesign(config, designSkillCount, trigger) {
|
|
45
|
+
return config.enabled && designSkillCount > 0 && trigger === 'pr';
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=design.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"design.js","sourceRoot":"","sources":["../../src/core/design.ts"],"names":[],"mappings":"AAAA,qDAAqD;AACrD,EAAE;AACF,2EAA2E;AAC3E,gFAAgF;AAChF,gFAAgF;AAChF,6EAA6E;AAC7E,gFAAgF;AAChF,iEAAiE;AAsBjE,uDAAuD;AACvD,MAAM,CAAC,MAAM,qBAAqB,GAAiB;IACjD,OAAO,EAAE,KAAK;IACd,IAAI,EAAE,UAAU;IAChB,MAAM,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,SAAS,EAAE,CAAC,SAAS,CAAC;CACvB,CAAC;AAEF;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,QAAiB;IAChD,MAAM,GAAG,GAAI,QAAoD,EAAE,MAAM,CAAC;IAC1E,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,EAAE,GAAG,qBAAqB,EAAE,CAAC;IACzE,MAAM,CAAC,GAAG,GAA8B,CAAC;IACzC,MAAM,MAAM,GAAG,CAAC,CAAU,EAAE,QAAkB,EAAY,EAAE,CAC1D,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC;IACnE,OAAO;QACL,OAAO,EAAE,CAAC,CAAC,SAAS,CAAC,KAAK,IAAI;QAC9B,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,UAAU;QACpD,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,qBAAqB,CAAC,MAAM,CAAC;QACzD,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC,EAAE,qBAAqB,CAAC,SAAS,CAAC;KACnE,CAAC;AACJ,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,eAAe,CAC7B,MAAoB,EACpB,gBAAwB,EACxB,OAAsB;IAEtB,OAAO,MAAM,CAAC,OAAO,IAAI,gBAAgB,GAAG,CAAC,IAAI,OAAO,KAAK,IAAI,CAAC;AACpE,CAAC"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/** One parsed finding from a prior `docs/reviews/PR-<n>.md`. */
|
|
2
|
+
export type ParsedFinding = {
|
|
3
|
+
/** Relative path. Falls back to '(unknown file)' when the prior file
|
|
4
|
+
* itself rendered the unknown-file marker; we keep it intact for
|
|
5
|
+
* identity stability. */
|
|
6
|
+
file: string;
|
|
7
|
+
/**
|
|
8
|
+
* 1-indexed line number. Zero when the prior file had no `:N` suffix
|
|
9
|
+
* (cross-cutting findings). Zero participates in identity as-is.
|
|
10
|
+
*/
|
|
11
|
+
line: number;
|
|
12
|
+
severity: 'critical' | 'minor' | 'preexisting';
|
|
13
|
+
skillName: string;
|
|
14
|
+
summary: string;
|
|
15
|
+
};
|
|
16
|
+
export type ParsedReview = {
|
|
17
|
+
findings: ParsedFinding[];
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Parse a `docs/reviews/PR-<n>.md` markdown back into structured findings.
|
|
21
|
+
*
|
|
22
|
+
* Robust to:
|
|
23
|
+
* - `null` / `undefined` input → returns `null`
|
|
24
|
+
* - empty / whitespace-only markdown → returns `null`
|
|
25
|
+
* - missing severity sections → those sections contribute 0
|
|
26
|
+
* - lines that don't match the SPEC bullet shape → silently dropped
|
|
27
|
+
* - mixed unknown-file markers (`(unknown file)` vs absent)
|
|
28
|
+
*
|
|
29
|
+
* Parsing strategy: walk top-down, switch active severity on each
|
|
30
|
+
* `### <emoji> <Label>` header (one of the three SPEC §1.8.1 buckets),
|
|
31
|
+
* then collect every line starting with `- **` until the next header
|
|
32
|
+
* (or `---` end marker). Each `- **<file>:<line>** — <skill>: <summary>`
|
|
33
|
+
* line is split into its 4 fields; `:<line>` is optional (cross-cutting).
|
|
34
|
+
*
|
|
35
|
+
* `(unknown file)` files are preserved verbatim — they participate in
|
|
36
|
+
* identity, so the same cross-cutting finding can still be diffed across
|
|
37
|
+
* rounds even when neither pass has a line anchor.
|
|
38
|
+
*/
|
|
39
|
+
export declare function parsePriorReviewFile(markdown: string | null | undefined): ParsedReview | null;
|
|
40
|
+
/**
|
|
41
|
+
* Diff prior vs current.
|
|
42
|
+
*
|
|
43
|
+
* `resolvedFindings`: findings that appeared in `prior` but NOT in
|
|
44
|
+
* `current`. The PR author (or auto-fix) addressed them.
|
|
45
|
+
*
|
|
46
|
+
* `stillOpenFindings`: findings that appeared in BOTH `prior` and
|
|
47
|
+
* `current`. These are persistent — the PR author hasn't fixed them
|
|
48
|
+
* (or the bot still considers them findings post-fix-push).
|
|
49
|
+
*
|
|
50
|
+
* Findings unique to `current` (newly raised this round) appear in
|
|
51
|
+
* neither list — those are surfaced directly by the renderer's normal
|
|
52
|
+
* severity-bucket emission.
|
|
53
|
+
*
|
|
54
|
+
* Order is preserved from `prior` for stability across rounds.
|
|
55
|
+
*/
|
|
56
|
+
export declare function diffFindings(prior: ParsedReview | null, current: {
|
|
57
|
+
critical_findings?: Array<{
|
|
58
|
+
skill: string;
|
|
59
|
+
file?: string;
|
|
60
|
+
line?: number;
|
|
61
|
+
summary: string;
|
|
62
|
+
}>;
|
|
63
|
+
minor_findings?: Array<{
|
|
64
|
+
skill: string;
|
|
65
|
+
file?: string;
|
|
66
|
+
line?: number;
|
|
67
|
+
summary: string;
|
|
68
|
+
}>;
|
|
69
|
+
preexisting_findings?: Array<{
|
|
70
|
+
skill: string;
|
|
71
|
+
file?: string;
|
|
72
|
+
line?: number;
|
|
73
|
+
summary: string;
|
|
74
|
+
}>;
|
|
75
|
+
}): {
|
|
76
|
+
resolvedFindings: ParsedFinding[];
|
|
77
|
+
stillOpenFindings: ParsedFinding[];
|
|
78
|
+
};
|
|
79
|
+
/**
|
|
80
|
+
* Stable identity for a finding. Used for diffing prior vs current
|
|
81
|
+
* across review rounds AND (by intention) shareable with the
|
|
82
|
+
* clud-bug-app inline-thread anchor hash so future cross-feature
|
|
83
|
+
* surfaces (e.g. "the auto-fix that resolved this thread also resolves
|
|
84
|
+
* this doc-file finding") align without re-computing.
|
|
85
|
+
*
|
|
86
|
+
* Exposed for tests + downstream callers that want to align their own
|
|
87
|
+
* finding storage on the same scheme.
|
|
88
|
+
*/
|
|
89
|
+
export declare function findingIdentity(f: {
|
|
90
|
+
file: string;
|
|
91
|
+
line: number;
|
|
92
|
+
severity: 'critical' | 'minor' | 'preexisting';
|
|
93
|
+
skillName: string;
|
|
94
|
+
summary: string;
|
|
95
|
+
}): string;
|
|
96
|
+
//# sourceMappingURL=diff-findings.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diff-findings.d.ts","sourceRoot":"","sources":["../../src/core/diff-findings.ts"],"names":[],"mappings":"AAgCA,gEAAgE;AAChE,MAAM,MAAM,aAAa,GAAG;IAC1B;;8BAE0B;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb;;;OAGG;IACH,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,UAAU,GAAG,OAAO,GAAG,aAAa,CAAC;IAC/C,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IAAE,QAAQ,EAAE,aAAa,EAAE,CAAA;CAAE,CAAC;AAEzD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAClC,YAAY,GAAG,IAAI,CA+DrB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,YAAY,GAAG,IAAI,EAC1B,OAAO,EAAE;IACP,iBAAiB,CAAC,EAAE,KAAK,CAAC;QACxB,KAAK,EAAE,MAAM,CAAC;QACd,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC,CAAC;IACH,cAAc,CAAC,EAAE,KAAK,CAAC;QACrB,KAAK,EAAE,MAAM,CAAC;QACd,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC,CAAC;IACH,oBAAoB,CAAC,EAAE,KAAK,CAAC;QAC3B,KAAK,EAAE,MAAM,CAAC;QACd,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC,CAAC;CACJ,GACA;IACD,gBAAgB,EAAE,aAAa,EAAE,CAAC;IAClC,iBAAiB,EAAE,aAAa,EAAE,CAAC;CACpC,CAgDA;AAED;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,UAAU,GAAG,OAAO,GAAG,aAAa,CAAC;IAC/C,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB,GAAG,MAAM,CAST"}
|