@themoltnet/legreffier 0.29.0 → 0.29.1
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 +16 -8
- package/dist/index.js +58 -27
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -1,19 +1,27 @@
|
|
|
1
1
|
# @themoltnet/legreffier
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
End-to-end attribution for AI coding agents — so you know who wrote the code,
|
|
4
|
+
why, and whether the context behind it actually worked. Built on
|
|
4
5
|
[MoltNet](https://themolt.net).
|
|
5
6
|
|
|
6
|
-
`legreffier init`
|
|
7
|
-
|
|
8
|
-
interactive flow.
|
|
7
|
+
`legreffier init` gives your agent its own cryptographic identity, a persistent
|
|
8
|
+
diary for decisions and rationale, and SSH-signed commits linked to signed
|
|
9
|
+
reasoning — all wired up in one interactive flow. Memory that's _attributed_,
|
|
10
|
+
rationale that's _signed_, and context that can be _measured_ — across sessions
|
|
11
|
+
and across agents.
|
|
9
12
|
|
|
10
13
|
## What You Get
|
|
11
14
|
|
|
12
15
|
1. **Own identity** — commits show the agent's name and avatar, not yours
|
|
13
|
-
2. **
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
16
|
+
2. **Persistent diary** — signed, content-addressed entries for decisions,
|
|
17
|
+
rationale, and context that survive across sessions and across agents
|
|
18
|
+
3. **Rationale linked to code** — non-trivial commits carry a `MoltNet-Diary:`
|
|
19
|
+
trailer pointing at a signed entry explaining _why_
|
|
20
|
+
4. **SSH-signed commits** — every commit is signed with the agent's Ed25519 key
|
|
21
|
+
5. **Measured context** — compiled packs can be scored against real coding
|
|
22
|
+
tasks via the MoltNet eval runner, so memory earns its place instead of just
|
|
23
|
+
accumulating
|
|
24
|
+
6. **GitHub App authentication** — push access via installation tokens, no
|
|
17
25
|
personal access tokens
|
|
18
26
|
|
|
19
27
|
## Prerequisites
|
package/dist/index.js
CHANGED
|
@@ -410,14 +410,14 @@ function CliHero({ animated = false }) {
|
|
|
410
410
|
/* @__PURE__ */ jsx(Text, { children: " " }),
|
|
411
411
|
/* @__PURE__ */ jsxs(Text, { children: [
|
|
412
412
|
" ",
|
|
413
|
-
/* @__PURE__ */
|
|
413
|
+
/* @__PURE__ */ jsxs(Text, {
|
|
414
414
|
color: cliTheme.color.text,
|
|
415
|
-
children: "
|
|
415
|
+
children: ["Attribution for AI coding agents.", " "]
|
|
416
416
|
}),
|
|
417
417
|
/* @__PURE__ */ jsx(Text, {
|
|
418
418
|
color: cliTheme.color.accent,
|
|
419
419
|
bold: true,
|
|
420
|
-
children: "
|
|
420
|
+
children: "Identity, memory, signed rationale."
|
|
421
421
|
})
|
|
422
422
|
] }),
|
|
423
423
|
/* @__PURE__ */ jsxs(Text, { children: [
|
|
@@ -7532,33 +7532,60 @@ async function downloadSkills(repoDir, skillDir) {
|
|
|
7532
7532
|
}
|
|
7533
7533
|
}
|
|
7534
7534
|
}
|
|
7535
|
-
function buildGhTokenRule(
|
|
7535
|
+
function buildGhTokenRule() {
|
|
7536
7536
|
return [
|
|
7537
|
-
"# GitHub CLI Authentication (
|
|
7537
|
+
"# GitHub CLI Authentication (MoltNet agents)",
|
|
7538
|
+
"",
|
|
7539
|
+
"> **STRICT RULE — read this before every `gh` call.**",
|
|
7540
|
+
">",
|
|
7541
|
+
"> When `GIT_CONFIG_GLOBAL` is set (matches `.moltnet/<agent>/gitconfig`), you",
|
|
7542
|
+
"> **MUST NOT** run bare `gh <command>`. You **MUST** prefix every `gh` call",
|
|
7543
|
+
"> with a `GH_TOKEN` resolved from an **absolute path** to `moltnet.json`.",
|
|
7544
|
+
"> Running bare `gh` silently falls back to the human personal token and",
|
|
7545
|
+
"> attributes the action to the wrong identity — this is a correctness bug,",
|
|
7546
|
+
"> not a warning.",
|
|
7538
7547
|
"",
|
|
7539
|
-
|
|
7540
|
-
"authenticate `gh` CLI commands as the GitHub App by prefixing them with:",
|
|
7548
|
+
"## The only correct form",
|
|
7541
7549
|
"",
|
|
7542
7550
|
"```bash",
|
|
7543
|
-
"
|
|
7551
|
+
"# 1. Resolve credentials to an ABSOLUTE path (never trust $GIT_CONFIG_GLOBAL as-is).",
|
|
7552
|
+
"CREDS=\"$(cd \"$(dirname \"$GIT_CONFIG_GLOBAL\")\" 2>/dev/null && pwd)/moltnet.json\"",
|
|
7553
|
+
"",
|
|
7554
|
+
"# 2. Refuse to proceed if the file does not exist at that absolute path.",
|
|
7555
|
+
"[ -f \"$CREDS\" ] || { echo \"FATAL: moltnet.json not found at $CREDS\" >&2; exit 1; }",
|
|
7556
|
+
"",
|
|
7557
|
+
"# 3. Call gh with GH_TOKEN inlined.",
|
|
7558
|
+
"GH_TOKEN=$(npx @themoltnet/cli github token --credentials \"$CREDS\") gh <command>",
|
|
7544
7559
|
"```",
|
|
7545
7560
|
"",
|
|
7546
|
-
"The
|
|
7561
|
+
"The credentials file (`moltnet.json`) always lives next to the `gitconfig`",
|
|
7562
|
+
"inside the same `.moltnet/<agent>/` directory, regardless of which agent is",
|
|
7563
|
+
"active. The token is cached locally (~1 hour lifetime, 5-min expiry buffer),",
|
|
7547
7564
|
"so repeated calls are fast after the first API hit.",
|
|
7548
7565
|
"",
|
|
7549
|
-
"##
|
|
7566
|
+
"## Why absolute paths are mandatory",
|
|
7550
7567
|
"",
|
|
7551
|
-
|
|
7552
|
-
"
|
|
7553
|
-
"
|
|
7554
|
-
"
|
|
7568
|
+
"`GIT_CONFIG_GLOBAL` is almost always a **relative path** (e.g. `.moltnet/<agent>/gitconfig`).",
|
|
7569
|
+
"Every git worktree has a different CWD from the main worktree root, so",
|
|
7570
|
+
"`$(dirname \"$GIT_CONFIG_GLOBAL\")` resolves differently depending on where you are.",
|
|
7571
|
+
"When it resolves to a non-existent directory:",
|
|
7555
7572
|
"",
|
|
7556
|
-
"
|
|
7573
|
+
"- `npx @themoltnet/cli github token` prints `no credentials found` to stderr,",
|
|
7574
|
+
"- the command substitution yields an empty `GH_TOKEN`,",
|
|
7575
|
+
"- `gh` silently falls back to your personal token,",
|
|
7576
|
+
"- the resulting API call is attributed to the **human**, not the agent.",
|
|
7557
7577
|
"",
|
|
7558
|
-
"
|
|
7559
|
-
"
|
|
7560
|
-
"
|
|
7561
|
-
"
|
|
7578
|
+
"This failure is invisible in normal output. The `cd ... && pwd` dance in step 1",
|
|
7579
|
+
"is the only reliable way to get an absolute path that works across worktrees.",
|
|
7580
|
+
"",
|
|
7581
|
+
"## Forbidden patterns",
|
|
7582
|
+
"",
|
|
7583
|
+
"- `gh <command>` — bare, no `GH_TOKEN`. **Never.**",
|
|
7584
|
+
"- `GH_TOKEN=$(... --credentials \"$(dirname \"$GIT_CONFIG_GLOBAL\")/moltnet.json\") gh ...`",
|
|
7585
|
+
" — uses the raw relative path. Breaks in worktrees.",
|
|
7586
|
+
"- `GH_TOKEN=$(... --credentials \"./moltnet.json\") gh ...` — relative. Breaks.",
|
|
7587
|
+
"- `GH_TOKEN=$(... --credentials \"~/.moltnet/...\") gh ...` — `~` is not expanded",
|
|
7588
|
+
" inside double quotes; use `$HOME` or the literal absolute path.",
|
|
7562
7589
|
"",
|
|
7563
7590
|
"## Allowed `gh` subcommands",
|
|
7564
7591
|
"",
|
|
@@ -7780,7 +7807,7 @@ var ClaudeAdapter = class {
|
|
|
7780
7807
|
async writeRules(opts) {
|
|
7781
7808
|
const dir = join(opts.repoDir, ".claude", "rules");
|
|
7782
7809
|
await mkdir(dir, { recursive: true });
|
|
7783
|
-
await writeFile(join(dir, "legreffier-gh.md"), buildGhTokenRule(
|
|
7810
|
+
await writeFile(join(dir, "legreffier-gh.md"), buildGhTokenRule(), "utf-8");
|
|
7784
7811
|
}
|
|
7785
7812
|
};
|
|
7786
7813
|
//#endregion
|
|
@@ -8057,17 +8084,21 @@ async function writePem(pem, appSlug, configDir) {
|
|
|
8057
8084
|
/**
|
|
8058
8085
|
* Write a standalone gitconfig file to <configDir>/gitconfig and return
|
|
8059
8086
|
* its path. The config sets user.name/email and enables SSH commit signing
|
|
8060
|
-
* using the agent's SSH key.
|
|
8087
|
+
* using the agent's SSH public key.
|
|
8088
|
+
*
|
|
8089
|
+
* **Important:** `signingkey` must live under `[user]`, not `[gpg "ssh"]`.
|
|
8090
|
+
* Git only reads `user.signingkey`; a key declared as `gpg.ssh.signingkey`
|
|
8091
|
+
* is silently ignored and `git commit -S` fails with
|
|
8092
|
+
* `fatal: either user.signingkey or gpg.ssh.defaultKeyCommand needs to be configured`.
|
|
8061
8093
|
*/
|
|
8062
|
-
async function writeGitConfig({ configDir, name, email,
|
|
8094
|
+
async function writeGitConfig({ configDir, name, email, sshPublicKeyPath }) {
|
|
8063
8095
|
const content = [
|
|
8064
8096
|
"[user]",
|
|
8065
8097
|
`\tname = ${name}`,
|
|
8066
8098
|
`\temail = ${email}`,
|
|
8099
|
+
`\tsigningkey = ${sshPublicKeyPath}`,
|
|
8067
8100
|
"[gpg]",
|
|
8068
8101
|
" format = ssh",
|
|
8069
|
-
"[gpg \"ssh\"]",
|
|
8070
|
-
`\tsigningKey = ${sshKeyPath}`,
|
|
8071
8102
|
"[commit]",
|
|
8072
8103
|
" gpgsign = true",
|
|
8073
8104
|
""
|
|
@@ -8322,7 +8353,7 @@ async function runGitSetupPhase(opts) {
|
|
|
8322
8353
|
key: "gitSetup",
|
|
8323
8354
|
status: "running"
|
|
8324
8355
|
});
|
|
8325
|
-
const {
|
|
8356
|
+
const { publicPath } = await exportSSHKey({ configDir });
|
|
8326
8357
|
const email = buildBotEmail((await lookupBotUser(appSlug, { maxRetries: 5 })).id, appSlug);
|
|
8327
8358
|
await updateConfigSection("git", {
|
|
8328
8359
|
name: agentName,
|
|
@@ -8332,7 +8363,7 @@ async function runGitSetupPhase(opts) {
|
|
|
8332
8363
|
configDir,
|
|
8333
8364
|
name: agentName,
|
|
8334
8365
|
email,
|
|
8335
|
-
|
|
8366
|
+
sshPublicKeyPath: publicPath
|
|
8336
8367
|
})
|
|
8337
8368
|
}, configDir);
|
|
8338
8369
|
dispatch({
|
|
@@ -9252,7 +9283,7 @@ async function runPortRewritePhase(opts) {
|
|
|
9252
9283
|
configDir: targetDir,
|
|
9253
9284
|
name: config.git.name,
|
|
9254
9285
|
email: config.git.email,
|
|
9255
|
-
|
|
9286
|
+
sshPublicKeyPath: newSshPub
|
|
9256
9287
|
});
|
|
9257
9288
|
await writeEnvFile({
|
|
9258
9289
|
envDir: targetDir,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@themoltnet/legreffier",
|
|
3
|
-
"version": "0.29.
|
|
4
|
-
"description": "LeGreffier —
|
|
3
|
+
"version": "0.29.1",
|
|
4
|
+
"description": "LeGreffier — attribution and measured memory for AI coding agents.",
|
|
5
5
|
"license": "AGPL-3.0-only",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"repository": {
|
|
@@ -36,8 +36,8 @@
|
|
|
36
36
|
"@moltnet/api-client": "0.1.0",
|
|
37
37
|
"@themoltnet/design-system": "0.3.2",
|
|
38
38
|
"@moltnet/crypto-service": "0.1.0",
|
|
39
|
-
"@themoltnet/
|
|
40
|
-
"@themoltnet/
|
|
39
|
+
"@themoltnet/sdk": "0.88.0",
|
|
40
|
+
"@themoltnet/github-agent": "0.23.0"
|
|
41
41
|
},
|
|
42
42
|
"scripts": {
|
|
43
43
|
"dev": "vite build --watch",
|