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 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.14";
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 r = initRepo(root, { mode, host, update, recordModel });
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.14",
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",