madeby 0.3.6 → 0.3.7
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 +29 -0
- package/dist/madeby.mjs +67 -20
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -44,6 +44,8 @@ npm i -g madeby
|
|
|
44
44
|
madeby init Set MadeBy up: write .madeby/policy.json + the CI disclosure check.
|
|
45
45
|
madeby check [<range>] The gate: do commits meet .madeby/policy.json? (a verdict — exit code
|
|
46
46
|
gates). <range> e.g. origin/main..HEAD — a PR's own commits. --json too.
|
|
47
|
+
madeby coverage [<range>] The scoreboard: how much of the repo's authorship is disclosed (known)
|
|
48
|
+
vs unknown, the human/with_ai/ai mix, and the asserted/verified split.
|
|
47
49
|
madeby who [<path>] Made by whom? What's on the record about who made this — one file's
|
|
48
50
|
(alias: whom) origin, or the whole repo with no path. --lines=A-B to scope. --json too.
|
|
49
51
|
madeby ai [tool] Made by AI: attest AI authorship of HEAD (an Authored-by-ai trailer).
|
|
@@ -77,6 +79,33 @@ In CI, omit `<range>` — on a pull-request build `madeby check` **auto-scopes t
|
|
|
77
79
|
commits** from the CI environment (GitHub today; other hosts via their adapter). Pass an explicit
|
|
78
80
|
`<range>` to override.
|
|
79
81
|
|
|
82
|
+
### `madeby coverage` — the authorship scoreboard
|
|
83
|
+
|
|
84
|
+
`check` is the gate; `coverage` is the picture. Run it in any repo to see how much of your
|
|
85
|
+
history's authorship is **on the record** — no policy, no config:
|
|
86
|
+
|
|
87
|
+
```sh
|
|
88
|
+
madeby coverage # scores recent history
|
|
89
|
+
madeby coverage origin/main..HEAD # or a range
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
```
|
|
93
|
+
authorship coverage — last 500 commits (estimate · disclosure, not detection)
|
|
94
|
+
known 62% ████████████░░░░░░░░ disclosed — on the record
|
|
95
|
+
unknown 38% ███████░░░░░░░░░░░░░ no disclosure — origin unknown, never guessed
|
|
96
|
+
|
|
97
|
+
of the 310 known: human 41% · with_ai 38% · ai 21%
|
|
98
|
+
trust: asserted 88% · verified 12% — self-reported; sign commits to verify
|
|
99
|
+
|
|
100
|
+
→ 38% unknown. Require disclosure going forward: madeby init --required
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
It is a **labeled estimate**, honest by construction: an undisclosed commit is **unknown**, never
|
|
104
|
+
inferred as human — disclosure, not detection. The human/with_ai/ai mix is reported *of the known*,
|
|
105
|
+
never of the whole repo, and the asserted-vs-verified split shows how much is signed. `--json` emits
|
|
106
|
+
counts only (percentages are derived at the edge, never stored). Use it to see your gap, then
|
|
107
|
+
`madeby init --required` to close it going forward.
|
|
108
|
+
|
|
80
109
|
### `madeby me` — affirm human authorship
|
|
81
110
|
|
|
82
111
|
Disclosure runs both ways: if you wrote code yourself, affirm it so your work is *disclosed-human*,
|
package/dist/madeby.mjs
CHANGED
|
@@ -1992,9 +1992,20 @@ function whoCommand(args) {
|
|
|
1992
1992
|
}
|
|
1993
1993
|
|
|
1994
1994
|
// src/commands/coverage.ts
|
|
1995
|
+
import { existsSync as existsSync4, readFileSync as readFileSync6 } from "node:fs";
|
|
1996
|
+
import { join as join5 } from "node:path";
|
|
1997
|
+
function policyIsRequired(root) {
|
|
1998
|
+
const p = join5(root, ".madeby", "policy.json");
|
|
1999
|
+
if (!existsSync4(p)) return false;
|
|
2000
|
+
try {
|
|
2001
|
+
return parseDisclosurePolicy(JSON.parse(readFileSync6(p, "utf8"))).mode === "required";
|
|
2002
|
+
} catch {
|
|
2003
|
+
return false;
|
|
2004
|
+
}
|
|
2005
|
+
}
|
|
1995
2006
|
var pct = (n, d) => d > 0 ? Math.round(n / d * 100) : 0;
|
|
1996
2007
|
var bar = (p) => "\u2588".repeat(Math.round(p / 5)) + "\u2591".repeat(20 - Math.round(p / 5));
|
|
1997
|
-
function render(r) {
|
|
2008
|
+
function render(r, required) {
|
|
1998
2009
|
const knownPct = pct(r.known, r.total);
|
|
1999
2010
|
const unknownPct = 100 - knownPct;
|
|
2000
2011
|
console.log(`authorship coverage \u2014 ${r.scope} (estimate \xB7 disclosure, not detection)`);
|
|
@@ -2013,8 +2024,11 @@ function render(r) {
|
|
|
2013
2024
|
console.log(` trust: asserted ${pct(r.byTier.asserted, r.known)}% \xB7 verified ${pct(r.byTier.verified, r.known)}% \u2014 self-reported; sign commits to verify`);
|
|
2014
2025
|
}
|
|
2015
2026
|
if (r.unknown > 0) {
|
|
2016
|
-
console.log(
|
|
2017
|
-
|
|
2027
|
+
console.log(
|
|
2028
|
+
required ? `
|
|
2029
|
+
\u2192 ${unknownPct}% unknown. Disclosure is already required on pull requests \u2014 the unknown is history the gate didn\u2019t cover (e.g. from before it was on).` : `
|
|
2030
|
+
\u2192 ${unknownPct}% unknown. Require disclosure going forward: madeby init --required`
|
|
2031
|
+
);
|
|
2018
2032
|
}
|
|
2019
2033
|
}
|
|
2020
2034
|
function coverageCommand(args) {
|
|
@@ -2031,12 +2045,12 @@ function coverageCommand(args) {
|
|
|
2031
2045
|
console.log(JSON.stringify(r, null, 2));
|
|
2032
2046
|
return;
|
|
2033
2047
|
}
|
|
2034
|
-
render(r);
|
|
2048
|
+
render(r, policyIsRequired(root));
|
|
2035
2049
|
}
|
|
2036
2050
|
|
|
2037
2051
|
// src/init-repo.ts
|
|
2038
|
-
import { existsSync as
|
|
2039
|
-
import { dirname as dirname2, join as
|
|
2052
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync2, readFileSync as readFileSync7, writeFileSync as writeFileSync2 } from "node:fs";
|
|
2053
|
+
import { dirname as dirname2, join as join6 } from "node:path";
|
|
2040
2054
|
var ACO_VERSION = 1;
|
|
2041
2055
|
var ACO_OPEN = `<!-- madeby:aco v${ACO_VERSION} -->`;
|
|
2042
2056
|
var ACO_CLOSE = `<!-- /madeby:aco -->`;
|
|
@@ -2087,7 +2101,7 @@ function replaceAcoBlock(content, loc) {
|
|
|
2087
2101
|
return content.slice(0, loc.start) + ACO_BLOCK + content.slice(loc.end);
|
|
2088
2102
|
}
|
|
2089
2103
|
function writeIfAbsent(abs, rel, content, created, skipped) {
|
|
2090
|
-
if (
|
|
2104
|
+
if (existsSync5(abs)) {
|
|
2091
2105
|
skipped.push(rel);
|
|
2092
2106
|
return;
|
|
2093
2107
|
}
|
|
@@ -2095,15 +2109,39 @@ function writeIfAbsent(abs, rel, content, created, skipped) {
|
|
|
2095
2109
|
writeFileSync2(abs, content);
|
|
2096
2110
|
created.push(rel);
|
|
2097
2111
|
}
|
|
2112
|
+
function ensurePolicy(abs, rel, mode, res) {
|
|
2113
|
+
if (!existsSync5(abs)) {
|
|
2114
|
+
mkdirSync2(dirname2(abs), { recursive: true });
|
|
2115
|
+
writeFileSync2(abs, JSON.stringify({ version: 0, mode }, null, 2) + "\n");
|
|
2116
|
+
res.created.push(rel);
|
|
2117
|
+
return { mode, malformed: false };
|
|
2118
|
+
}
|
|
2119
|
+
let raw;
|
|
2120
|
+
try {
|
|
2121
|
+
raw = JSON.parse(readFileSync7(abs, "utf8"));
|
|
2122
|
+
} catch {
|
|
2123
|
+
res.skipped.push(rel);
|
|
2124
|
+
return { mode: "advisory", malformed: true };
|
|
2125
|
+
}
|
|
2126
|
+
const isObject = !!raw && typeof raw === "object" && !Array.isArray(raw);
|
|
2127
|
+
const current = parseDisclosurePolicy(raw).mode;
|
|
2128
|
+
if (mode === "required" && current !== "required" && isObject) {
|
|
2129
|
+
writeFileSync2(abs, JSON.stringify({ ...raw, mode: "required" }, null, 2) + "\n");
|
|
2130
|
+
res.raised.push(rel);
|
|
2131
|
+
return { mode: "required", malformed: false };
|
|
2132
|
+
}
|
|
2133
|
+
res.skipped.push(rel);
|
|
2134
|
+
return { mode: current, malformed: !isObject };
|
|
2135
|
+
}
|
|
2098
2136
|
function ensureAco(abs, rel, res, opts) {
|
|
2099
|
-
if (!
|
|
2137
|
+
if (!existsSync5(abs)) {
|
|
2100
2138
|
if (!opts.createIfAbsent) return;
|
|
2101
2139
|
mkdirSync2(dirname2(abs), { recursive: true });
|
|
2102
2140
|
writeFileSync2(abs, AGENTS_CONVENTION);
|
|
2103
2141
|
res.created.push(rel);
|
|
2104
2142
|
return;
|
|
2105
2143
|
}
|
|
2106
|
-
const content =
|
|
2144
|
+
const content = readFileSync7(abs, "utf8");
|
|
2107
2145
|
const status = acoStatus(content);
|
|
2108
2146
|
if (status === "absent") {
|
|
2109
2147
|
const sep = content.endsWith("\n") ? "" : "\n";
|
|
@@ -2129,14 +2167,21 @@ function initRepo(root, opts = {}) {
|
|
|
2129
2167
|
const merged = [];
|
|
2130
2168
|
const updated = [];
|
|
2131
2169
|
const outdated = [];
|
|
2132
|
-
const
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
ensureAco(
|
|
2170
|
+
const raised = [];
|
|
2171
|
+
const res = { created, skipped, merged, updated, outdated, raised };
|
|
2172
|
+
const policy = ensurePolicy(join6(root, ".madeby", "policy.json"), ".madeby/policy.json", mode, res);
|
|
2173
|
+
ensureAco(join6(root, "AGENTS.md"), "AGENTS.md", res, { createIfAbsent: true, update });
|
|
2174
|
+
ensureAco(join6(root, "CLAUDE.md"), "CLAUDE.md", res, { createIfAbsent: false, update });
|
|
2136
2175
|
if (host === "github") {
|
|
2137
|
-
writeIfAbsent(
|
|
2176
|
+
writeIfAbsent(join6(root, ".github", "workflows", "disclosure.yml"), ".github/workflows/disclosure.yml", GH_WORKFLOW, created, skipped);
|
|
2138
2177
|
}
|
|
2139
2178
|
const nextSteps = ["Commit the created files."];
|
|
2179
|
+
if (raised.length > 0) {
|
|
2180
|
+
nextSteps.push('Raised .madeby/policy.json to "required" \u2014 undisclosed commits now fail the check. To make it block merges, also mark the check required on the protected branch (see madeby://guide/enforce).');
|
|
2181
|
+
}
|
|
2182
|
+
if (policy.malformed) {
|
|
2183
|
+
nextSteps.push('.madeby/policy.json is not a valid policy, so the check treats it as advisory. It was left untouched \u2014 fix it (e.g. { "version": 0, "mode": "required" }) to enforce.');
|
|
2184
|
+
}
|
|
2140
2185
|
if (created.includes("AGENTS.md")) {
|
|
2141
2186
|
nextSteps.push("An AGENTS.md ACO convention was added \u2014 agents that read it disclose authorship (e.g. 'npx madeby@latest ai [tool]'). Tune it to your repo's tools.");
|
|
2142
2187
|
}
|
|
@@ -2154,12 +2199,12 @@ function initRepo(root, opts = {}) {
|
|
|
2154
2199
|
} else if (host !== "github") {
|
|
2155
2200
|
nextSteps.push(`Add a CI step that runs 'npx madeby check' on pull requests for ${host}.`);
|
|
2156
2201
|
}
|
|
2157
|
-
if (mode !== "required") {
|
|
2158
|
-
nextSteps.push(`When ready to enforce: set mode
|
|
2202
|
+
if (policy.mode !== "required") {
|
|
2203
|
+
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.`);
|
|
2159
2204
|
}
|
|
2160
2205
|
nextSteps.push("Contributors disclose origin with a Co-Authored-By / Generated-by trailer (AI), an Authored-by-human trailer ('madeby me'), an Authored-by-ai trailer ('madeby ai' \u2014 AI authored, committer accountable), a DCO Signed-off-by, or a signed commit. Evidence upgrade: 'madeby ai --witness' records your own AI session's witnessed spans.");
|
|
2161
2206
|
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.");
|
|
2162
|
-
return { created, skipped, merged, updated, outdated, policyMode: mode, host, nextSteps };
|
|
2207
|
+
return { created, skipped, merged, updated, outdated, raised, policyMode: policy.mode, host, nextSteps };
|
|
2163
2208
|
}
|
|
2164
2209
|
var GH_WORKFLOW = `name: Disclosure
|
|
2165
2210
|
on: pull_request
|
|
@@ -2189,6 +2234,7 @@ function initCommand(args) {
|
|
|
2189
2234
|
for (const f of r.created) console.log(` + ${f}`);
|
|
2190
2235
|
for (const f of r.merged) console.log(` ~ ${f} (merged in the ACO rule)`);
|
|
2191
2236
|
for (const f of r.updated) console.log(` \u2191 ${f} (ACO convention refreshed to the current version)`);
|
|
2237
|
+
for (const f of r.raised) console.log(` \u2191 ${f} (mode raised to required)`);
|
|
2192
2238
|
for (const f of r.outdated) console.log(` ! ${f} (ACO convention OUTDATED \u2014 run 'madeby init --update')`);
|
|
2193
2239
|
for (const f of r.skipped) console.log(` \xB7 ${f} (exists \u2014 left as-is)`);
|
|
2194
2240
|
console.log(`
|
|
@@ -2201,7 +2247,7 @@ madeby init \u2014 policy: ${r.policyMode}, host: ${r.host}`);
|
|
|
2201
2247
|
import { execFileSync as execFileSync6 } from "node:child_process";
|
|
2202
2248
|
import { createInterface } from "node:readline";
|
|
2203
2249
|
var DEFAULT_PROTOCOL = "2024-11-05";
|
|
2204
|
-
var SERVER_INFO = { name: "madeby", version: "0.3.
|
|
2250
|
+
var SERVER_INFO = { name: "madeby", version: "0.3.7" };
|
|
2205
2251
|
function rootFor(path) {
|
|
2206
2252
|
const cwd = path && path.length ? path : process.cwd();
|
|
2207
2253
|
return execFileSync6("git", ["-C", cwd, "-c", "core.quotePath=false", "rev-parse", "--show-toplevel"], { encoding: "utf8" }).trim();
|
|
@@ -2216,7 +2262,7 @@ var repoInput = {
|
|
|
2216
2262
|
var TOOLS = [
|
|
2217
2263
|
{
|
|
2218
2264
|
name: "init",
|
|
2219
|
-
description:
|
|
2265
|
+
description: 'Align a repository with MadeBy in one call: create .madeby/policy.json (advisory by default \u2014 reports, never fails) and the CI disclosure check for the host (github default; azure-devops guided). Idempotent \u2014 never clobbers an existing policy or workflow; the one change it makes is mode: "required" raising an existing advisory policy to required (it never lowers one). Returns the files created/skipped and the recommended next steps. Read the resources: madeby://guide/align (full workflow), madeby://guide/enforce (make the check required \u2014 the host\'s control plane, exact commands), madeby://guide/backfill (account for existing history honestly \u2014 never fabricate). The ACO convention block is versioned: the result flags an `outdated` AGENTS.md/CLAUDE.md (drift); call again with update:true to refresh just that block in place \u2014 how an agent keeps the convention current.',
|
|
2220
2266
|
inputSchema: {
|
|
2221
2267
|
type: "object",
|
|
2222
2268
|
properties: {
|
|
@@ -2300,7 +2346,8 @@ guesses whether code is AI. To align a repo:
|
|
|
2300
2346
|
|
|
2301
2347
|
1. **Assess** \u2014 call \`check\` (or \`who\` with no path for a repo-wide map) to see current coverage.
|
|
2302
2348
|
2. **Set up** \u2014 call \`init\`: it writes \`.madeby/policy.json\` (advisory) and the CI disclosure check.
|
|
2303
|
-
Idempotent; it never clobbers an existing policy or workflow
|
|
2349
|
+
Idempotent; it never clobbers an existing policy or workflow (\`mode: "required"\` only ever raises an
|
|
2350
|
+
advisory policy to required). Then commit the created files.
|
|
2304
2351
|
3. **Disclose going forward** \u2014 contributors disclose origin with a Co-Authored-By / Generated-by
|
|
2305
2352
|
trailer (AI), an \`Authored-by-human\` trailer (work they wrote themselves \u2014 the symmetric human
|
|
2306
2353
|
claim; \`madeby me\` adds it), a DCO Signed-off-by, a signed commit, or \`ai\` (record your own AI
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "madeby",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.7",
|
|
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",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"node": ">=20"
|
|
42
42
|
},
|
|
43
43
|
"bin": {
|
|
44
|
-
"madeby": "
|
|
44
|
+
"madeby": "dist/madeby.mjs"
|
|
45
45
|
},
|
|
46
46
|
"files": [
|
|
47
47
|
"dist",
|