madeby 0.3.6 → 0.3.8
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 +31 -0
- package/dist/madeby.mjs +116 -33
- 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,35 @@ 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). A plain merge authors nothing and
|
|
107
|
+
isn't scored; a merge that changes content of its own (a conflict resolution) is, like any other
|
|
108
|
+
commit — chosen by what it changes, never by whether it discloses. Use it to see your gap, then
|
|
109
|
+
`madeby init --required` to close it going forward.
|
|
110
|
+
|
|
80
111
|
### `madeby me` — affirm human authorship
|
|
81
112
|
|
|
82
113
|
Disclosure runs both ways: if you wrote code yourself, affirm it so your work is *disclosed-human*,
|
package/dist/madeby.mjs
CHANGED
|
@@ -4,6 +4,18 @@
|
|
|
4
4
|
import { execFileSync } from "node:child_process";
|
|
5
5
|
var FIELD = "";
|
|
6
6
|
var RECORD = "";
|
|
7
|
+
var GROUP = "";
|
|
8
|
+
var parseMeta = (rec) => {
|
|
9
|
+
const parts = rec.split(FIELD);
|
|
10
|
+
const sig = parts[3] ?? "N";
|
|
11
|
+
return {
|
|
12
|
+
sha: parts[0] ?? "",
|
|
13
|
+
authorName: parts[1] ?? "",
|
|
14
|
+
authorEmail: parts[2] ?? "",
|
|
15
|
+
signed: sig !== "N" && sig !== "",
|
|
16
|
+
message: parts.slice(4).join(FIELD).trimEnd()
|
|
17
|
+
};
|
|
18
|
+
};
|
|
7
19
|
function readGitLog(repoPath, limit, range) {
|
|
8
20
|
try {
|
|
9
21
|
const args = [
|
|
@@ -11,7 +23,7 @@ function readGitLog(repoPath, limit, range) {
|
|
|
11
23
|
repoPath,
|
|
12
24
|
"log",
|
|
13
25
|
"--no-merges",
|
|
14
|
-
// merge
|
|
26
|
+
// a merge is not authored content by default; readAuthoredMerges finds those that are
|
|
15
27
|
...limit ? ["-n", String(limit)] : [],
|
|
16
28
|
// %G? is a signature-PRESENCE probe for the Disclosure Score: 'N' = unsigned; any other code
|
|
17
29
|
// (G/U/E/…) = a signature is present (E = present but unverifiable here — no keyring). We only
|
|
@@ -22,19 +34,38 @@ function readGitLog(repoPath, limit, range) {
|
|
|
22
34
|
...range ? [range] : []
|
|
23
35
|
];
|
|
24
36
|
const out = execFileSync("git", args, { encoding: "utf8", maxBuffer: 256 * 1024 * 1024, stdio: ["ignore", "pipe", "ignore"] });
|
|
25
|
-
return out.split(RECORD).map((r) => r.replace(/^\s+/, "")).filter((r) => r.length > 0).map(
|
|
26
|
-
const parts = rec.split(FIELD);
|
|
27
|
-
const sha = parts[0] ?? "";
|
|
28
|
-
const authorName = parts[1] ?? "";
|
|
29
|
-
const authorEmail = parts[2] ?? "";
|
|
30
|
-
const sig = parts[3] ?? "N";
|
|
31
|
-
const message = parts.slice(4).join(FIELD).trimEnd();
|
|
32
|
-
return { sha, authorName, authorEmail, signed: sig !== "N" && sig !== "", message };
|
|
33
|
-
});
|
|
37
|
+
return out.split(RECORD).map((r) => r.replace(/^\s+/, "")).filter((r) => r.length > 0).map(parseMeta);
|
|
34
38
|
} catch {
|
|
35
39
|
return [];
|
|
36
40
|
}
|
|
37
41
|
}
|
|
42
|
+
function readAuthoredMerges(repoPath, opts = {}) {
|
|
43
|
+
try {
|
|
44
|
+
const args = [
|
|
45
|
+
"-C",
|
|
46
|
+
repoPath,
|
|
47
|
+
"log",
|
|
48
|
+
"--merges",
|
|
49
|
+
"--cc",
|
|
50
|
+
"--name-only",
|
|
51
|
+
`--format=${RECORD}%H${FIELD}%an${FIELD}%ae${FIELD}%G?${FIELD}%B${GROUP}`,
|
|
52
|
+
...opts.since !== void 0 && !opts.range ? [`--since=@${opts.since}`] : [],
|
|
53
|
+
...opts.range ? [opts.range] : []
|
|
54
|
+
];
|
|
55
|
+
const out = execFileSync("git", args, { encoding: "utf8", maxBuffer: 256 * 1024 * 1024, stdio: ["ignore", "pipe", "ignore"] });
|
|
56
|
+
return out.split(RECORD).filter((r) => r.includes(GROUP)).filter((r) => r.slice(r.lastIndexOf(GROUP) + 1).trim().length > 0).map((r) => parseMeta(r.slice(0, r.lastIndexOf(GROUP))));
|
|
57
|
+
} catch {
|
|
58
|
+
return [];
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function commitTime(repoPath, sha) {
|
|
62
|
+
try {
|
|
63
|
+
const t = Number(execFileSync("git", ["-C", repoPath, "log", "-1", "--format=%ct", sha], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim());
|
|
64
|
+
return Number.isFinite(t) && t > 0 ? t : void 0;
|
|
65
|
+
} catch {
|
|
66
|
+
return void 0;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
38
69
|
|
|
39
70
|
// ../classify/src/classify.ts
|
|
40
71
|
var AI_PATTERNS = [
|
|
@@ -1201,7 +1232,12 @@ function provenanceMap(root, opts = {}) {
|
|
|
1201
1232
|
|
|
1202
1233
|
// ../analyzer/src/coverage.ts
|
|
1203
1234
|
function repoCoverage(root, opts = {}) {
|
|
1204
|
-
const
|
|
1235
|
+
const limit = opts.range ? void 0 : opts.limit ?? 500;
|
|
1236
|
+
const authored = readGitLog(root, limit, opts.range);
|
|
1237
|
+
const oldest = authored.at(-1)?.sha;
|
|
1238
|
+
const since = limit && oldest && authored.length >= limit ? commitTime(root, oldest) : void 0;
|
|
1239
|
+
const merges = authored.length > 0 && (!limit || authored.length < limit || since !== void 0) ? readAuthoredMerges(root, { range: opts.range, since }) : [];
|
|
1240
|
+
const commits = [...authored, ...merges];
|
|
1205
1241
|
const byCategory = { human: 0, with_ai: 0, ai: 0, bot: 0 };
|
|
1206
1242
|
const byTier = { asserted: 0, verified: 0 };
|
|
1207
1243
|
let known = 0;
|
|
@@ -1217,8 +1253,8 @@ function repoCoverage(root, opts = {}) {
|
|
|
1217
1253
|
}
|
|
1218
1254
|
return {
|
|
1219
1255
|
isEstimate: true,
|
|
1220
|
-
methodology: "commit-level authorship disclosure (estimate); undisclosed = unknown, never inferred",
|
|
1221
|
-
scope: opts.range ? `range ${opts.range}` : `last ${commits.length}
|
|
1256
|
+
methodology: "commit-level authorship disclosure (estimate); undisclosed = unknown, never inferred; merges scored only when they change content of their own",
|
|
1257
|
+
scope: (opts.range ? `range ${opts.range}` : `last ${authored.length} commits`) + (merges.length ? ` + ${merges.length} merges with changes of their own` : ""),
|
|
1222
1258
|
total: commits.length,
|
|
1223
1259
|
known,
|
|
1224
1260
|
unknown: commits.length - known,
|
|
@@ -1992,9 +2028,20 @@ function whoCommand(args) {
|
|
|
1992
2028
|
}
|
|
1993
2029
|
|
|
1994
2030
|
// src/commands/coverage.ts
|
|
2031
|
+
import { existsSync as existsSync4, readFileSync as readFileSync6 } from "node:fs";
|
|
2032
|
+
import { join as join5 } from "node:path";
|
|
2033
|
+
function policyIsRequired(root) {
|
|
2034
|
+
const p = join5(root, ".madeby", "policy.json");
|
|
2035
|
+
if (!existsSync4(p)) return false;
|
|
2036
|
+
try {
|
|
2037
|
+
return parseDisclosurePolicy(JSON.parse(readFileSync6(p, "utf8"))).mode === "required";
|
|
2038
|
+
} catch {
|
|
2039
|
+
return false;
|
|
2040
|
+
}
|
|
2041
|
+
}
|
|
1995
2042
|
var pct = (n, d) => d > 0 ? Math.round(n / d * 100) : 0;
|
|
1996
2043
|
var bar = (p) => "\u2588".repeat(Math.round(p / 5)) + "\u2591".repeat(20 - Math.round(p / 5));
|
|
1997
|
-
function render(r) {
|
|
2044
|
+
function render(r, required) {
|
|
1998
2045
|
const knownPct = pct(r.known, r.total);
|
|
1999
2046
|
const unknownPct = 100 - knownPct;
|
|
2000
2047
|
console.log(`authorship coverage \u2014 ${r.scope} (estimate \xB7 disclosure, not detection)`);
|
|
@@ -2013,8 +2060,11 @@ function render(r) {
|
|
|
2013
2060
|
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
2061
|
}
|
|
2015
2062
|
if (r.unknown > 0) {
|
|
2016
|
-
console.log(
|
|
2017
|
-
|
|
2063
|
+
console.log(
|
|
2064
|
+
required ? `
|
|
2065
|
+
\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).` : `
|
|
2066
|
+
\u2192 ${unknownPct}% unknown. Require disclosure going forward: madeby init --required`
|
|
2067
|
+
);
|
|
2018
2068
|
}
|
|
2019
2069
|
}
|
|
2020
2070
|
function coverageCommand(args) {
|
|
@@ -2031,12 +2081,12 @@ function coverageCommand(args) {
|
|
|
2031
2081
|
console.log(JSON.stringify(r, null, 2));
|
|
2032
2082
|
return;
|
|
2033
2083
|
}
|
|
2034
|
-
render(r);
|
|
2084
|
+
render(r, policyIsRequired(root));
|
|
2035
2085
|
}
|
|
2036
2086
|
|
|
2037
2087
|
// src/init-repo.ts
|
|
2038
|
-
import { existsSync as
|
|
2039
|
-
import { dirname as dirname2, join as
|
|
2088
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync2, readFileSync as readFileSync7, writeFileSync as writeFileSync2 } from "node:fs";
|
|
2089
|
+
import { dirname as dirname2, join as join6 } from "node:path";
|
|
2040
2090
|
var ACO_VERSION = 1;
|
|
2041
2091
|
var ACO_OPEN = `<!-- madeby:aco v${ACO_VERSION} -->`;
|
|
2042
2092
|
var ACO_CLOSE = `<!-- /madeby:aco -->`;
|
|
@@ -2087,7 +2137,7 @@ function replaceAcoBlock(content, loc) {
|
|
|
2087
2137
|
return content.slice(0, loc.start) + ACO_BLOCK + content.slice(loc.end);
|
|
2088
2138
|
}
|
|
2089
2139
|
function writeIfAbsent(abs, rel, content, created, skipped) {
|
|
2090
|
-
if (
|
|
2140
|
+
if (existsSync5(abs)) {
|
|
2091
2141
|
skipped.push(rel);
|
|
2092
2142
|
return;
|
|
2093
2143
|
}
|
|
@@ -2095,15 +2145,39 @@ function writeIfAbsent(abs, rel, content, created, skipped) {
|
|
|
2095
2145
|
writeFileSync2(abs, content);
|
|
2096
2146
|
created.push(rel);
|
|
2097
2147
|
}
|
|
2148
|
+
function ensurePolicy(abs, rel, mode, res) {
|
|
2149
|
+
if (!existsSync5(abs)) {
|
|
2150
|
+
mkdirSync2(dirname2(abs), { recursive: true });
|
|
2151
|
+
writeFileSync2(abs, JSON.stringify({ version: 0, mode }, null, 2) + "\n");
|
|
2152
|
+
res.created.push(rel);
|
|
2153
|
+
return { mode, malformed: false };
|
|
2154
|
+
}
|
|
2155
|
+
let raw;
|
|
2156
|
+
try {
|
|
2157
|
+
raw = JSON.parse(readFileSync7(abs, "utf8"));
|
|
2158
|
+
} catch {
|
|
2159
|
+
res.skipped.push(rel);
|
|
2160
|
+
return { mode: "advisory", malformed: true };
|
|
2161
|
+
}
|
|
2162
|
+
const isObject = !!raw && typeof raw === "object" && !Array.isArray(raw);
|
|
2163
|
+
const current = parseDisclosurePolicy(raw).mode;
|
|
2164
|
+
if (mode === "required" && current !== "required" && isObject) {
|
|
2165
|
+
writeFileSync2(abs, JSON.stringify({ ...raw, mode: "required" }, null, 2) + "\n");
|
|
2166
|
+
res.raised.push(rel);
|
|
2167
|
+
return { mode: "required", malformed: false };
|
|
2168
|
+
}
|
|
2169
|
+
res.skipped.push(rel);
|
|
2170
|
+
return { mode: current, malformed: !isObject };
|
|
2171
|
+
}
|
|
2098
2172
|
function ensureAco(abs, rel, res, opts) {
|
|
2099
|
-
if (!
|
|
2173
|
+
if (!existsSync5(abs)) {
|
|
2100
2174
|
if (!opts.createIfAbsent) return;
|
|
2101
2175
|
mkdirSync2(dirname2(abs), { recursive: true });
|
|
2102
2176
|
writeFileSync2(abs, AGENTS_CONVENTION);
|
|
2103
2177
|
res.created.push(rel);
|
|
2104
2178
|
return;
|
|
2105
2179
|
}
|
|
2106
|
-
const content =
|
|
2180
|
+
const content = readFileSync7(abs, "utf8");
|
|
2107
2181
|
const status = acoStatus(content);
|
|
2108
2182
|
if (status === "absent") {
|
|
2109
2183
|
const sep = content.endsWith("\n") ? "" : "\n";
|
|
@@ -2129,14 +2203,21 @@ function initRepo(root, opts = {}) {
|
|
|
2129
2203
|
const merged = [];
|
|
2130
2204
|
const updated = [];
|
|
2131
2205
|
const outdated = [];
|
|
2132
|
-
const
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
ensureAco(
|
|
2206
|
+
const raised = [];
|
|
2207
|
+
const res = { created, skipped, merged, updated, outdated, raised };
|
|
2208
|
+
const policy = ensurePolicy(join6(root, ".madeby", "policy.json"), ".madeby/policy.json", mode, res);
|
|
2209
|
+
ensureAco(join6(root, "AGENTS.md"), "AGENTS.md", res, { createIfAbsent: true, update });
|
|
2210
|
+
ensureAco(join6(root, "CLAUDE.md"), "CLAUDE.md", res, { createIfAbsent: false, update });
|
|
2136
2211
|
if (host === "github") {
|
|
2137
|
-
writeIfAbsent(
|
|
2212
|
+
writeIfAbsent(join6(root, ".github", "workflows", "disclosure.yml"), ".github/workflows/disclosure.yml", GH_WORKFLOW, created, skipped);
|
|
2138
2213
|
}
|
|
2139
2214
|
const nextSteps = ["Commit the created files."];
|
|
2215
|
+
if (raised.length > 0) {
|
|
2216
|
+
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).');
|
|
2217
|
+
}
|
|
2218
|
+
if (policy.malformed) {
|
|
2219
|
+
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.');
|
|
2220
|
+
}
|
|
2140
2221
|
if (created.includes("AGENTS.md")) {
|
|
2141
2222
|
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
2223
|
}
|
|
@@ -2154,12 +2235,12 @@ function initRepo(root, opts = {}) {
|
|
|
2154
2235
|
} else if (host !== "github") {
|
|
2155
2236
|
nextSteps.push(`Add a CI step that runs 'npx madeby check' on pull requests for ${host}.`);
|
|
2156
2237
|
}
|
|
2157
|
-
if (mode !== "required") {
|
|
2158
|
-
nextSteps.push(`When ready to enforce: set mode
|
|
2238
|
+
if (policy.mode !== "required") {
|
|
2239
|
+
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
2240
|
}
|
|
2160
2241
|
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
2242
|
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 };
|
|
2243
|
+
return { created, skipped, merged, updated, outdated, raised, policyMode: policy.mode, host, nextSteps };
|
|
2163
2244
|
}
|
|
2164
2245
|
var GH_WORKFLOW = `name: Disclosure
|
|
2165
2246
|
on: pull_request
|
|
@@ -2189,6 +2270,7 @@ function initCommand(args) {
|
|
|
2189
2270
|
for (const f of r.created) console.log(` + ${f}`);
|
|
2190
2271
|
for (const f of r.merged) console.log(` ~ ${f} (merged in the ACO rule)`);
|
|
2191
2272
|
for (const f of r.updated) console.log(` \u2191 ${f} (ACO convention refreshed to the current version)`);
|
|
2273
|
+
for (const f of r.raised) console.log(` \u2191 ${f} (mode raised to required)`);
|
|
2192
2274
|
for (const f of r.outdated) console.log(` ! ${f} (ACO convention OUTDATED \u2014 run 'madeby init --update')`);
|
|
2193
2275
|
for (const f of r.skipped) console.log(` \xB7 ${f} (exists \u2014 left as-is)`);
|
|
2194
2276
|
console.log(`
|
|
@@ -2201,7 +2283,7 @@ madeby init \u2014 policy: ${r.policyMode}, host: ${r.host}`);
|
|
|
2201
2283
|
import { execFileSync as execFileSync6 } from "node:child_process";
|
|
2202
2284
|
import { createInterface } from "node:readline";
|
|
2203
2285
|
var DEFAULT_PROTOCOL = "2024-11-05";
|
|
2204
|
-
var SERVER_INFO = { name: "madeby", version: "0.3.
|
|
2286
|
+
var SERVER_INFO = { name: "madeby", version: "0.3.8" };
|
|
2205
2287
|
function rootFor(path) {
|
|
2206
2288
|
const cwd = path && path.length ? path : process.cwd();
|
|
2207
2289
|
return execFileSync6("git", ["-C", cwd, "-c", "core.quotePath=false", "rev-parse", "--show-toplevel"], { encoding: "utf8" }).trim();
|
|
@@ -2216,7 +2298,7 @@ var repoInput = {
|
|
|
2216
2298
|
var TOOLS = [
|
|
2217
2299
|
{
|
|
2218
2300
|
name: "init",
|
|
2219
|
-
description:
|
|
2301
|
+
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
2302
|
inputSchema: {
|
|
2221
2303
|
type: "object",
|
|
2222
2304
|
properties: {
|
|
@@ -2300,7 +2382,8 @@ guesses whether code is AI. To align a repo:
|
|
|
2300
2382
|
|
|
2301
2383
|
1. **Assess** \u2014 call \`check\` (or \`who\` with no path for a repo-wide map) to see current coverage.
|
|
2302
2384
|
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
|
|
2385
|
+
Idempotent; it never clobbers an existing policy or workflow (\`mode: "required"\` only ever raises an
|
|
2386
|
+
advisory policy to required). Then commit the created files.
|
|
2304
2387
|
3. **Disclose going forward** \u2014 contributors disclose origin with a Co-Authored-By / Generated-by
|
|
2305
2388
|
trailer (AI), an \`Authored-by-human\` trailer (work they wrote themselves \u2014 the symmetric human
|
|
2306
2389
|
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.8",
|
|
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",
|