madeby 0.3.14 → 0.3.15
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 +19 -0
- package/dist/madeby.mjs +45 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -43,6 +43,7 @@ npm i -g madeby
|
|
|
43
43
|
```
|
|
44
44
|
madeby init Set MadeBy up: write .madeby/policy.json + the CI disclosure check.
|
|
45
45
|
--record-model (opt-in) also asks agents to name the model version.
|
|
46
|
+
In a repo that uses Claude Code, also turns its commit trailer on.
|
|
46
47
|
madeby check [<range>] The gate: do commits meet .madeby/policy.json? (a verdict — exit code
|
|
47
48
|
gates). <range> e.g. origin/main..HEAD — a PR's own commits. --json too.
|
|
48
49
|
madeby coverage [<range>] The scoreboard: how much of the repo's authorship is disclosed (known)
|
|
@@ -198,6 +199,24 @@ Fail-safe by design: a missing or malformed policy degrades to `advisory`. `made
|
|
|
198
199
|
on its own error, and never invents a stricter gate than the maintainer wrote. It never passes a
|
|
199
200
|
`required` check it couldn't actually run, either: missing history fails it (see `check` above).
|
|
200
201
|
|
|
202
|
+
### Claude Code's own trailer
|
|
203
|
+
|
|
204
|
+
Claude Code adds a `Co-Authored-By` trailer to its commits unless a developer turns it off in their
|
|
205
|
+
personal settings — and then the `AGENTS.md` rule alone may not bring it back. So in a repo that uses
|
|
206
|
+
Claude Code (it has a `CLAUDE.md` or a `.claude/` folder; `--claude-code` to force), `madeby init`
|
|
207
|
+
sets the trailer in the shared `.claude/settings.json`:
|
|
208
|
+
|
|
209
|
+
```json
|
|
210
|
+
{ "attribution": { "commit": "Co-Authored-By: Claude Code <noreply@anthropic.com>" } }
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
The shared project file overrides a developer's personal settings, so everyone in the repo gets the
|
|
214
|
+
trailer back. It's fixed text — Claude Code's setting has no placeholder for the model — so to record
|
|
215
|
+
the model use `--record-model`. It merges into an existing file and never changes a value the team
|
|
216
|
+
already set, including turning attribution off on purpose. A developer's own
|
|
217
|
+
`.claude/settings.local.json` or instructions can still turn it off; the `required` check is what
|
|
218
|
+
holds the line.
|
|
219
|
+
|
|
201
220
|
## Use it as a PR check
|
|
202
221
|
|
|
203
222
|
A ready-made GitHub Action, an Azure DevOps Pipelines template, and a GitLab CI job wrap
|
package/dist/madeby.mjs
CHANGED
|
@@ -2591,7 +2591,7 @@ import { existsSync as existsSync7, mkdirSync as mkdirSync2, readFileSync as rea
|
|
|
2591
2591
|
import { dirname as dirname2, join as join8 } from "node:path";
|
|
2592
2592
|
|
|
2593
2593
|
// src/version.ts
|
|
2594
|
-
var VERSION = "0.3.
|
|
2594
|
+
var VERSION = "0.3.15";
|
|
2595
2595
|
|
|
2596
2596
|
// src/init-repo.ts
|
|
2597
2597
|
var ACO_VERSION = 2;
|
|
@@ -2687,6 +2687,31 @@ function ensurePolicy(abs, rel, want, res) {
|
|
|
2687
2687
|
}
|
|
2688
2688
|
return { mode: raise ? "required" : current.mode, recordModel: modelOn || current.recordModel === true, malformed: !isObject };
|
|
2689
2689
|
}
|
|
2690
|
+
var CLAUDE_TRAILER = "Co-Authored-By: Claude Code <noreply@anthropic.com>";
|
|
2691
|
+
function ensureClaudeSettings(root, force) {
|
|
2692
|
+
const abs = join8(root, ".claude", "settings.json");
|
|
2693
|
+
const usesClaude = force || existsSync7(join8(root, "CLAUDE.md")) || existsSync7(join8(root, ".claude"));
|
|
2694
|
+
if (!usesClaude) return "not-used";
|
|
2695
|
+
if (!existsSync7(abs)) {
|
|
2696
|
+
mkdirSync2(dirname2(abs), { recursive: true });
|
|
2697
|
+
writeFileSync2(abs, JSON.stringify({ attribution: { commit: CLAUDE_TRAILER } }, null, 2) + "\n");
|
|
2698
|
+
return "created";
|
|
2699
|
+
}
|
|
2700
|
+
let raw;
|
|
2701
|
+
try {
|
|
2702
|
+
raw = JSON.parse(readFileSync9(abs, "utf8"));
|
|
2703
|
+
} catch {
|
|
2704
|
+
return "malformed";
|
|
2705
|
+
}
|
|
2706
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return "malformed";
|
|
2707
|
+
const settings = raw;
|
|
2708
|
+
const attribution = settings.attribution;
|
|
2709
|
+
if (attribution === false || attribution && typeof attribution === "object" && "commit" in attribution) return "kept";
|
|
2710
|
+
if (attribution === void 0 && settings.includeCoAuthoredBy === false) return "kept";
|
|
2711
|
+
const next = { ...settings, attribution: { ...attribution && typeof attribution === "object" ? attribution : {}, commit: CLAUDE_TRAILER } };
|
|
2712
|
+
writeFileSync2(abs, JSON.stringify(next, null, 2) + "\n");
|
|
2713
|
+
return "merged";
|
|
2714
|
+
}
|
|
2690
2715
|
function ensureAco(abs, rel, res, opts) {
|
|
2691
2716
|
const block = acoBlock(opts.recordModel);
|
|
2692
2717
|
if (!existsSync7(abs)) {
|
|
@@ -2737,6 +2762,8 @@ function initRepo(root, opts = {}) {
|
|
|
2737
2762
|
const aco = { update, recordModel: policy.recordModel, modelRequested };
|
|
2738
2763
|
ensureAco(join8(root, "AGENTS.md"), "AGENTS.md", res, { createIfAbsent: true, ...aco });
|
|
2739
2764
|
ensureAco(join8(root, "CLAUDE.md"), "CLAUDE.md", res, { createIfAbsent: false, ...aco });
|
|
2765
|
+
const claudeSettings = ensureClaudeSettings(root, opts.claudeCode ?? false);
|
|
2766
|
+
if (claudeSettings === "created") created.push(".claude/settings.json");
|
|
2740
2767
|
if (host === "github") {
|
|
2741
2768
|
writeIfAbsent(join8(root, ".github", "workflows", "disclosure.yml"), ".github/workflows/disclosure.yml", GH_WORKFLOW, created, skipped);
|
|
2742
2769
|
} else if (host === "azure-devops") {
|
|
@@ -2773,8 +2800,15 @@ function initRepo(root, opts = {}) {
|
|
|
2773
2800
|
nextSteps.push(`When ready to enforce: run 'madeby init --required' (or set "mode": "required" in .madeby/policy.json), then make the check required on the protected branch \u2014 the host's control plane (GitHub: gh api branch protection; Azure: a build-validation policy). See the MCP resource madeby://guide/enforce for exact commands.`);
|
|
2774
2801
|
}
|
|
2775
2802
|
nextSteps.push("Contributors disclose origin with a Co-Authored-By / Generated-by trailer (AI), an Authored-by-human trailer ('madeby me'), or an Authored-by-ai trailer ('madeby ai' \u2014 AI authored, committer accountable). A DCO Signed-off-by or a signed commit alone doesn't say who wrote the code, so it counts only if the policy's 'accept' lists it. Evidence upgrade: 'madeby ai --witness' records your own AI session's witnessed spans.");
|
|
2803
|
+
if (claudeSettings === "created" || claudeSettings === "merged") {
|
|
2804
|
+
nextSteps.push(`Claude Code now adds '${CLAUDE_TRAILER}' to its commits in this repo (.claude/settings.json), for everyone \u2014 including developers who turned the trailer off in their personal settings. Their .claude/settings.local.json, or an instruction of their own, can still turn it off; the check is what holds the line.`);
|
|
2805
|
+
} else if (claudeSettings === "kept") {
|
|
2806
|
+
nextSteps.push("This repo's .claude/settings.json already sets Claude Code's commit attribution, so it was left as the team set it. Clear that setting for the default trailer, or set attribution.commit to name the AI.");
|
|
2807
|
+
} else if (claudeSettings === "malformed") {
|
|
2808
|
+
nextSteps.push(".claude/settings.json isn't valid JSON, so Claude Code's commit trailer wasn't set. Fix the file and run 'madeby init' again.");
|
|
2809
|
+
}
|
|
2776
2810
|
nextSteps.push("Existing history is accounted for honestly, not fabricated: run 'madeby check' over full history to recognize what's already disclosed; pre-adoption commits stay unknown. See madeby://guide/backfill.");
|
|
2777
|
-
return { created, skipped, merged, updated, outdated, raised, modelOn, policyMode: policy.mode, recordModel: policy.recordModel, host, nextSteps };
|
|
2811
|
+
return { created, skipped, merged, updated, outdated, raised, modelOn, policyMode: policy.mode, recordModel: policy.recordModel, claudeSettings, host, nextSteps };
|
|
2778
2812
|
}
|
|
2779
2813
|
var GH_WORKFLOW = `name: Disclosure
|
|
2780
2814
|
on: pull_request
|
|
@@ -2823,9 +2857,11 @@ function initCommand(args) {
|
|
|
2823
2857
|
const recordModel = args.includes("--record-model");
|
|
2824
2858
|
const hostIdx = args.indexOf("--host");
|
|
2825
2859
|
const host = hostIdx >= 0 ? args[hostIdx + 1] : void 0;
|
|
2826
|
-
const
|
|
2860
|
+
const claudeCode = args.includes("--claude-code");
|
|
2861
|
+
const r = initRepo(root, { mode, host, update, recordModel, claudeCode });
|
|
2827
2862
|
for (const f of r.created) console.log(` + ${f}`);
|
|
2828
2863
|
for (const f of r.merged) console.log(` ~ ${f} (merged in the ACO rule)`);
|
|
2864
|
+
if (r.claudeSettings === "merged") console.log(" ~ .claude/settings.json (merged in Claude Code's commit trailer)");
|
|
2829
2865
|
for (const f of r.updated) console.log(` \u2191 ${f} (ACO convention refreshed to the current version)`);
|
|
2830
2866
|
for (const f of r.raised) console.log(` \u2191 ${f} (mode raised to required)`);
|
|
2831
2867
|
for (const f of r.modelOn) console.log(` \u2191 ${f} (now records the model version)`);
|
|
@@ -2864,14 +2900,16 @@ var TOOLS = [
|
|
|
2864
2900
|
mode: { enum: ["advisory", "required"], description: "policy mode to seed (default: advisory)" },
|
|
2865
2901
|
host: { type: "string", description: "CI host to wire: github (default) or azure-devops" },
|
|
2866
2902
|
update: { type: "boolean", description: "refresh an outdated ACO convention block in place (drift update); default false (report only)" },
|
|
2867
|
-
record_model: { type: "boolean", description: "opt in: ask AI disclosures to also name the model version (tool:model) \u2014 policy recordModel + an AGENTS.md line. Never turned off by init; default false." }
|
|
2903
|
+
record_model: { type: "boolean", description: "opt in: ask AI disclosures to also name the model version (tool:model) \u2014 policy recordModel + an AGENTS.md line. Never turned off by init; default false." },
|
|
2904
|
+
claude_code: { type: "boolean", description: "set Claude Code's commit trailer in .claude/settings.json even if the repo shows no sign of Claude Code (it is set automatically when there is a CLAUDE.md or .claude/). A value the team already set is never changed." }
|
|
2868
2905
|
}
|
|
2869
2906
|
},
|
|
2870
2907
|
handler: (args) => initRepo(rootFor(args.path), {
|
|
2871
2908
|
mode: args.mode,
|
|
2872
2909
|
host: args.host,
|
|
2873
2910
|
update: args.update,
|
|
2874
|
-
recordModel: args.record_model
|
|
2911
|
+
recordModel: args.record_model,
|
|
2912
|
+
claudeCode: args.claude_code
|
|
2875
2913
|
})
|
|
2876
2914
|
},
|
|
2877
2915
|
{
|
|
@@ -3177,6 +3215,8 @@ Usage:
|
|
|
3177
3215
|
+ the CI disclosure check. --update refreshes an outdated ACO block in
|
|
3178
3216
|
place; --required to enforce now; --host azure-devops for Azure Pipelines;
|
|
3179
3217
|
--record-model to also ask agents for the model version (opt-in).
|
|
3218
|
+
In a repo that uses Claude Code (CLAUDE.md or .claude/; --claude-code to
|
|
3219
|
+
force) it also turns Claude Code's commit trailer on in .claude/settings.json.
|
|
3180
3220
|
madeby check [<range>] The gate: do commits meet .madeby/policy.json? (a verdict \u2014 exit
|
|
3181
3221
|
code gates). <range> e.g. origin/main..HEAD; in CI it's auto-detected.
|
|
3182
3222
|
--json for a machine-readable result.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "madeby",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.15",
|
|
4
4
|
"mcpName": "io.github.MadeByFYI/madeby",
|
|
5
5
|
"description": "Disclose AI authorship of code. `npx madeby ai` adds an Authored-by-ai git trailer; AI coding agents disclose per the ACO standard (human/with_ai/ai) from an AGENTS.md convention. Disclosure, not detection. Gate PRs with `madeby check`.",
|
|
6
6
|
"license": "MIT",
|